├── .eas └── workflows │ └── staging_deploy.yml ├── .env.example ├── .eslintrc.js ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── codeql.yml │ ├── prod_app_deployment.yml │ ├── prod_web_deployment.yml │ ├── staging_app_deployment.yml │ └── staging_web_deployment.yml ├── .gitignore ├── .husky └── pre-commit ├── .prettierrc ├── .vscode ├── extensions.json ├── keybindings.json └── settings.json ├── CHANGELOG.md ├── CLAUDE.md ├── Procfile ├── README.md ├── app.json ├── babel.config.js ├── buildprod.sh ├── config └── nginx.conf.erb ├── deploy_frontend.sh ├── docs ├── docs │ ├── capabilities.md │ ├── faq.md │ ├── index.md │ ├── material.md │ ├── ramadan-results.md │ └── validation.md └── mkdocs.yml ├── eas.json ├── metro.config.js ├── nativewind-env.d.ts ├── nginx.conf.sample ├── package-lock.json ├── package.json ├── public ├── icon-192x192.png ├── icon-256x256.png ├── icon-512x512.png ├── icons │ ├── ansariWordDesktop.svg │ ├── ansariWordMobile.svg │ ├── compositeLogo.svg │ ├── compositeLogoDark.svg │ ├── compositeLogoMobile.svg │ └── logo.svg ├── images │ ├── background.png │ ├── backgroundImage.svg │ ├── backgroundRoundImage.svg │ ├── darkBackgroundImage.svg │ └── lightBackgroundImage.svg ├── index.html ├── logo-192_x_192.png ├── logo-256_x_256.png ├── logo-512_x_512.png ├── manifest.json └── robots.txt ├── src ├── app │ ├── (app) │ │ ├── _layout.tsx │ │ ├── chat │ │ │ └── [threadId].tsx │ │ ├── delete-account.tsx │ │ ├── index.tsx │ │ └── logout.tsx │ ├── (public) │ │ ├── 404.tsx │ │ ├── _layout.tsx │ │ ├── forgot-password.tsx │ │ ├── login.tsx │ │ ├── register.tsx │ │ └── reset-password.tsx │ ├── +html.tsx │ ├── +not-found.tsx │ ├── _layout.tsx │ ├── share │ │ ├── [shareThreadId].tsx │ │ └── _layout.tsx │ └── welcome │ │ ├── _layout.tsx │ │ └── index.tsx ├── assets │ └── images │ │ ├── background.png │ │ ├── icon-dark.png │ │ ├── icon.png │ │ ├── splash-icon-dark.png │ │ └── splash-icon.png ├── components │ ├── ActionButtons.tsx │ ├── AppUpdatePopup.tsx │ ├── ConfirmationDialog.tsx │ ├── Footer.tsx │ ├── Header.tsx │ ├── InfoPopup.tsx │ ├── KeyboardHandler.tsx │ ├── LanguageSelector.tsx │ ├── LoadingScreen.tsx │ ├── MaintenanceScreen.tsx │ ├── RootContainer.tsx │ ├── RootContainer.web.tsx │ ├── RootImageBackground.tsx │ ├── RootImageBackground.web.tsx │ ├── StyledText.tsx │ ├── Subscription.tsx │ ├── TermsAndPrivacy.tsx │ ├── Toast.tsx │ ├── buttons │ │ └── ENButton.tsx │ ├── chat │ │ ├── ChatContainer.tsx │ │ ├── ChatInput.tsx │ │ ├── MessageBubble.tsx │ │ ├── MessageList.tsx │ │ ├── ReactionButtons.tsx │ │ └── ShareContainer.tsx │ ├── index.ts │ ├── menu │ │ ├── MenuDrawer.tsx │ │ ├── NameContainer.tsx │ │ ├── SideMenuBody.tsx │ │ └── index.ts │ ├── prompts │ │ ├── PromptCard.tsx │ │ └── PromptList.tsx │ ├── share │ │ └── SharePopup.tsx │ ├── svg │ │ ├── AddIcon.tsx │ │ ├── AnsariWordDesktopIcon.tsx │ │ ├── AnsariWordMobileIcon.tsx │ │ ├── ChallengeIcon.tsx │ │ ├── ChatDeleteLineIcon.tsx │ │ ├── ChatIcon.tsx │ │ ├── CheckIcon.tsx │ │ ├── CloseIcon.tsx │ │ ├── CollapseIcon.tsx │ │ ├── CopyIcon.tsx │ │ ├── DarkIcon.tsx │ │ ├── DeleteIcon.tsx │ │ ├── DislikeIcon.tsx │ │ ├── DoubleCheckIcon.tsx │ │ ├── EditIcon.tsx │ │ ├── ExpandIcon.tsx │ │ ├── EyeIcon.tsx │ │ ├── FlagIcon.tsx │ │ ├── FullScreenExitIcon.tsx │ │ ├── FullScreenIcon.tsx │ │ ├── InfoIcon.tsx │ │ ├── InformationGreenIcon.tsx │ │ ├── InformationIcon.tsx │ │ ├── Language18Icon.tsx │ │ ├── Language24Icon.tsx │ │ ├── LanguageGreenIcon.tsx │ │ ├── LanguageIcon.tsx │ │ ├── LightIcon.tsx │ │ ├── LikeIcon.tsx │ │ ├── LineIcon.tsx │ │ ├── LoadingIcon.tsx │ │ ├── LogoIcon.tsx │ │ ├── LogoRoundIcon.tsx │ │ ├── LogoTextIcon.tsx │ │ ├── LogoutIcon.tsx │ │ ├── MenuIcon.tsx │ │ ├── MenuKebabIcon.tsx │ │ ├── MessageLoaderIcon.tsx │ │ ├── PaperPlaneLeftIcon.tsx │ │ ├── PaperPlaneRightIcon.tsx │ │ ├── PrayingIcon.tsx │ │ ├── ReactNativeSvg.tsx │ │ ├── RenameIcon.tsx │ │ ├── RightArrowIcon.tsx │ │ ├── ScrollToBottomIcon.tsx │ │ ├── SelectTextIcon.tsx │ │ ├── SendIcon.tsx │ │ ├── SettingIcon.tsx │ │ ├── ShareIcon.tsx │ │ ├── StopIcon.tsx │ │ ├── StopResponseIcon.tsx │ │ ├── UserIcon.tsx │ │ ├── WarningCircleIcon.tsx │ │ └── index.ts │ └── threads │ │ ├── IconContainer.tsx │ │ ├── ThreadCard.tsx │ │ ├── ThreadsList.tsx │ │ └── index.ts ├── constant │ └── index.ts ├── env │ └── index.ts ├── errors │ ├── ApplicationError.ts │ ├── ForbiddenError.ts │ ├── NotFoundError.ts │ ├── TokenRefreshError.ts │ └── index.ts ├── global.css ├── hooks │ ├── chat │ │ ├── index.ts │ │ ├── useChat.ts │ │ ├── useFeedbackHandler.ts │ │ ├── useFeedbackService.ts │ │ └── useScrollManagement.ts │ ├── index.ts │ ├── useAuth.ts │ ├── useDeleteAccount.ts │ ├── useDirection.ts │ ├── useGuest.ts │ ├── useLogout.ts │ ├── useScreenInfo.ts │ ├── useToggleInfoPopup.ts │ └── useTokenFromUrl.ts ├── i18n │ ├── i18n.ts │ ├── index.ts │ ├── locales │ │ ├── ar.json │ │ ├── ar │ │ │ ├── common.json │ │ │ ├── feedback.json │ │ │ ├── login.json │ │ │ ├── prompts.json │ │ │ └── register.json │ │ ├── bs.json │ │ ├── bs │ │ │ ├── common.json │ │ │ ├── feedback.json │ │ │ ├── login.json │ │ │ ├── prompts.json │ │ │ └── register.json │ │ ├── en.json │ │ ├── en │ │ │ ├── common.json │ │ │ ├── feedback.json │ │ │ ├── login.json │ │ │ ├── prompts.json │ │ │ └── register.json │ │ ├── fr.json │ │ ├── fr │ │ │ ├── common.json │ │ │ ├── feedback.json │ │ │ ├── login.json │ │ │ ├── prompts.json │ │ │ └── register.json │ │ ├── id.json │ │ ├── id │ │ │ ├── common.json │ │ │ ├── feedback.json │ │ │ ├── login.json │ │ │ ├── prompts.json │ │ │ └── register.json │ │ ├── tml │ │ │ ├── common.json │ │ │ ├── feedback.json │ │ │ ├── login.json │ │ │ ├── prompts.json │ │ │ └── register.json │ │ ├── tur.json │ │ ├── tur │ │ │ ├── common.json │ │ │ ├── feedback.json │ │ │ ├── login.json │ │ │ ├── prompts.json │ │ │ └── register.json │ │ ├── ur.json │ │ └── ur │ │ │ ├── common.json │ │ │ ├── feedback.json │ │ │ ├── login.json │ │ │ ├── prompts.json │ │ │ └── register.json │ └── resources.ts ├── interfaces │ ├── index.ts │ └── message.ts ├── services │ ├── ApiService.ts │ ├── ChatService.ts │ ├── FeedbackService.ts │ ├── PromptsService.ts │ ├── StorageService.ts │ ├── UserService.ts │ └── index.ts ├── store │ ├── actions │ │ ├── authActions.ts │ │ ├── chatActions.ts │ │ └── index.ts │ ├── index.ts │ ├── slices │ │ ├── authSlice.ts │ │ ├── chatSlice.ts │ │ ├── index.ts │ │ ├── informationPopupSlice.ts │ │ ├── inputFullModeSlice.ts │ │ ├── reactionButtonsSlice.ts │ │ ├── shareSlice.ts │ │ ├── sideMenuSlice.ts │ │ └── themeSlice.ts │ ├── store.ts │ └── types │ │ ├── chatTypes.ts │ │ └── index.ts ├── types │ ├── index.ts │ └── types.ts ├── utils │ ├── getEnv.ts │ ├── helpers.ts │ ├── index.ts │ ├── styles.ts │ └── theme.ts └── validation │ ├── index.ts │ ├── loginSchema.ts │ └── registerSchema.ts ├── tailwind.config.js ├── tsconfig.json └── vercel.json /.eas/workflows/staging_deploy.yml: -------------------------------------------------------------------------------- 1 | name: Staging Deployment 2 | 3 | on: 4 | push: 5 | branches: ['develop'] 6 | 7 | jobs: 8 | deploy: 9 | type: deploy 10 | name: Staging Deployment 11 | environment: preview 12 | params: 13 | prod: true 14 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | EXPO_PUBLIC_ENVIRONMENT=development 2 | EXPO_PUBLIC_SHARE_URL=http://localhost:8081 3 | 4 | EXPO_PUBLIC_API_V2_URL=https://staging-api.ansari.chat/api/v2 5 | EXPO_PUBLIC_API_TIMEOUT=60000 6 | 7 | EXPO_PUBLIC_SUBSCRIBE_URL=http://eepurl.com/iFCJaA 8 | EXPO_PUBLIC_FEEDBACK_EMAIL=feedback@ansari.chat 9 | EXPO_PUBLIC_COMPREHENSIVE_GUIDE_URL=https://docs.ansari.chat/capabilities/ 10 | EXPO_PUBLIC_PRIVACY_URL=https://docs.ansari.chat/privacy/ 11 | EXPO_PUBLIC_TERMS_URL=https://docs.ansari.chat/terms/ 12 | EXPO_PUBLIC_ENABLE_SHARE=false -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://docs.expo.dev/guides/using-eslint/ 2 | module.exports = { 3 | extends: ['expo', 'prettier'], 4 | plugins: ['prettier'], 5 | rules: { 6 | '@typescript-eslint/explicit-function-return-type': 'off', 7 | 'no-undef': 'off', 8 | '@typescript-eslint/no-require-imports': 'off', 9 | 'react/react-in-jsx-scope': 'off', 10 | camelcase: 'error', 11 | 'spaced-comment': 'error', 12 | quotes: ['error', 'single'], 13 | 'no-duplicate-imports': 'error', 14 | 'prettier/prettier': [ 15 | 'error', 16 | { 17 | endOfLine: 'auto', 18 | }, 19 | ], 20 | }, 21 | ignorePatterns: ['/dist/*', '/capacitor/*', '/android/*', '/ios/*'], 22 | } 23 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.png binary -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/prod_web_deployment.yml: -------------------------------------------------------------------------------- 1 | name: Production Web Deployment 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | jobs: 8 | build-and-deploy: 9 | runs-on: ubuntu-latest 10 | environment: production 11 | env: 12 | VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} 13 | VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} 14 | steps: 15 | - name: 🏗 Setup repo 16 | uses: actions/checkout@v3 17 | 18 | - name: 🏗 Setup Node 19 | uses: actions/setup-node@v3 20 | with: 21 | node-version: 20.x 22 | 23 | - name: 📦 Install Vercel CLI 24 | run: npm install --global vercel@latest 25 | 26 | - name: 📦 Install dependencies 27 | run: npm ci 28 | 29 | - name: 🏗 Setup EAS 30 | uses: expo/expo-github-action@v8 31 | with: 32 | eas-version: latest 33 | token: ${{ secrets.EXPO_TOKEN }} 34 | 35 | - name: Pull Vercel Environment Information 36 | run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} 37 | 38 | - name: 🏗 Pull in environment variables 39 | run: eas env:pull production 40 | 41 | - name: 🏗 Build 42 | run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} 43 | 44 | - name: 🚀 Deploy to Vercel 45 | run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }} 46 | -------------------------------------------------------------------------------- /.github/workflows/staging_app_deployment.yml: -------------------------------------------------------------------------------- 1 | name: Staging App Deployment 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | 8 | env: 9 | EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} 10 | 11 | jobs: 12 | continuously-deploy: 13 | runs-on: ubuntu-latest 14 | concurrency: continuous-deploy-fingerprint-${{ github.run_id }} 15 | permissions: 16 | contents: read # Allow checkout 17 | steps: 18 | - name: 🏗 Setup repo 19 | uses: actions/checkout@v4 20 | 21 | - name: 🏗 Setup Node 22 | uses: actions/setup-node@v3 23 | with: 24 | node-version: 18.x 25 | 26 | - name: 📦 Install dependencies 27 | run: npm ci 28 | 29 | - name: 🏗 Setup EAS 30 | uses: expo/expo-github-action@main 31 | with: 32 | eas-version: latest 33 | token: ${{ secrets.EXPO_TOKEN }} 34 | 35 | - name: Continuously Deploy 36 | uses: expo/expo-github-action/continuous-deploy-fingerprint@main 37 | with: 38 | profile: preview 39 | branch: preview 40 | environment: preview 41 | -------------------------------------------------------------------------------- /.github/workflows/staging_web_deployment.yml: -------------------------------------------------------------------------------- 1 | name: Staging Web Deployment 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | jobs: 8 | build-and-deploy: 9 | runs-on: ubuntu-latest 10 | environment: staging 11 | env: 12 | VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} 13 | VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} 14 | steps: 15 | - name: 🏗 Setup repo 16 | uses: actions/checkout@v3 17 | 18 | - name: 🏗 Setup Node 19 | uses: actions/setup-node@v3 20 | with: 21 | node-version: 20.x 22 | 23 | - name: 📦 Install Vercel CLI 24 | run: npm install --global vercel@latest 25 | 26 | - name: 📦 Install dependencies 27 | run: npm ci 28 | 29 | - name: 🏗 Setup EAS 30 | uses: expo/expo-github-action@v8 31 | with: 32 | eas-version: latest 33 | token: ${{ secrets.EXPO_TOKEN }} 34 | 35 | - name: Pull Vercel Environment Information 36 | run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }} 37 | 38 | - name: 🏗 Pull in environment variables 39 | run: eas env:pull preview 40 | 41 | - name: 🏗 Build 42 | run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} 43 | 44 | - name: 🚀 Deploy to Vercel 45 | run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }} 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # Expo 9 | .expo/ 10 | dist/ 11 | web-build/ 12 | expo-env.d.ts 13 | 14 | # Native 15 | ios 16 | android 17 | android-keystores 18 | google-service-account 19 | *.orig.* 20 | *.jks 21 | *.p8 22 | *.p12 23 | *.key 24 | *.mobileprovision 25 | credentials.json 26 | 27 | # Metro 28 | .metro-health-check* 29 | 30 | # testing 31 | /coverage 32 | 33 | # production 34 | /build 35 | 36 | # misc 37 | .DS_Store 38 | .env.local 39 | .env.development.local 40 | .env.test.local 41 | .env.production.local 42 | .history 43 | .env 44 | 45 | npm-debug.log* 46 | yarn-debug.log* 47 | yarn-error.log* 48 | node_modules 49 | 50 | .metro-health-check* -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm run lint -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "printWidth": 120, 5 | "tabWidth": 2, 6 | "trailingComma": "all", 7 | "jsxSingleQuote": true, 8 | "bracketSpacing": true, 9 | "arrowParens": "always" 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Gruntfuggly.todo-tree"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/keybindings.json: -------------------------------------------------------------------------------- 1 | // Place your key bindings in this file to override the defaultsauto[] 2 | [ 3 | { 4 | "key": "ctrl+shift+down", 5 | "command": "editor.action.copyLinesDownAction", 6 | "when": "editorTextFocus && !editorReadonly" 7 | }, 8 | { 9 | "key": "ctrl+shift+alt+down", 10 | "command": "-editor.action.copyLinesDownAction", 11 | "when": "editorTextFocus && !editorReadonly" 12 | }, 13 | { 14 | "key": "ctrl+shift+up", 15 | "command": "editor.action.copyLinesUpAction", 16 | "when": "editorTextFocus && !editorReadonly" 17 | }, 18 | { 19 | "key": "ctrl+shift+alt+up", 20 | "command": "-editor.action.copyLinesUpAction", 21 | "when": "editorTextFocus && !editorReadonly" 22 | } 23 | ] -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.eol": "\n", 3 | "editor.defaultFormatter": "esbenp.prettier-vscode", 4 | "editor.formatOnSave": true, 5 | "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"], 6 | "[json]": { 7 | "editor.defaultFormatter": "esbenp.prettier-vscode", 8 | "editor.formatOnSave": true 9 | }, 10 | "reactSnippets.settings.prettierEnabled": true, 11 | "prettier.jsxSingleQuote": true, 12 | "prettier.semi": false, 13 | "prettier.singleQuote": true, 14 | "prettier.trailingComma": "all", 15 | "prettier.useTabs": true, 16 | "prettier.vueIndentScriptAndStyle": true, 17 | "vs-code-prettier-eslint.prettierLast": true, 18 | "editor.columnSelection": false, 19 | "todo-tree.tree.showBadges": false, 20 | "todo-tree.tree.disableCompactFolders": true, 21 | "cSpell.words": ["endeavorpal", "Pressable", "reduxjs"] 22 | } 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Version 1.1.0 2 | 3 | ## New Feature 4 | 5 | - Add Français language. 6 | - Add "Stop" response. 7 | - Disable Pull to Refresh on mobile 8 | - Add greeting text on the homepage. 9 | - Change the mobile "Enter" button to add a new line. 10 | 11 | ## Bug fixes 12 | 13 | - Auto-scroll message while printing the response 14 | 15 | # Version 1.0.0 16 | 17 | Initial Release with core functionality, supporting English, العربية, Türkçe, اردو, Bosanski, Bahasa Indonesia 18 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bin/start-nginx-static -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "Ansari Chat", 4 | "owner": "ansari-project", 5 | "slug": "ansari-chat", 6 | "version": "1.0.0", 7 | "scheme": "ansarichat", 8 | "userInterfaceStyle": "automatic", 9 | "orientation": "portrait", 10 | "newArchEnabled": true, 11 | "icon": "./src/assets/images/icon.png", 12 | "web": { 13 | "bundler": "metro" 14 | }, 15 | "plugins": [ 16 | "expo-router", 17 | "expo-font", 18 | "expo-localization", 19 | [ 20 | "@sentry/react-native/expo", 21 | { 22 | "organization": "ansari-project-llc", 23 | "project": "ansari-frontend", 24 | "url": "https://sentry.io/" 25 | } 26 | ], 27 | [ 28 | "expo-splash-screen", 29 | { 30 | "backgroundColor": "#FFFFFF", 31 | "image": "./src/assets/images/splash-icon.png", 32 | "dark": { 33 | "backgroundColor": "#0F0F0F", 34 | "image": "./src/assets/images/splash-icon-dark.png" 35 | }, 36 | "imageWidth": 200, 37 | "resizeMode": "contain" 38 | } 39 | ], 40 | [ 41 | "expo-screen-orientation", 42 | { 43 | "initialOrientation": "PORTRAIT" 44 | } 45 | ] 46 | ], 47 | "extra": { 48 | "supportsRTL": true, 49 | "router": { 50 | "origin": false 51 | }, 52 | "eas": { 53 | "projectId": "e2f465a7-8007-4e83-91ba-4a9df4c5209a" 54 | } 55 | }, 56 | "android": { 57 | "package": "chat.ansari.app" 58 | }, 59 | "ios": { 60 | "bundleIdentifier": "chat.ansari.app", 61 | "supportsTablet": false, 62 | "infoPlist": { 63 | "ITSAppUsesNonExemptEncryption": false 64 | } 65 | }, 66 | "runtimeVersion": { 67 | "policy": "fingerprint" 68 | }, 69 | "updates": { 70 | "url": "https://u.expo.dev/e2f465a7-8007-4e83-91ba-4a9df4c5209a" 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true) 3 | return { 4 | presets: [['babel-preset-expo', { jsxImportSource: 'nativewind' }], 'nativewind/babel'], 5 | 6 | plugins: ['@babel/plugin-proposal-export-namespace-from', 'react-native-reanimated/plugin'], 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /buildprod.sh: -------------------------------------------------------------------------------- 1 | cp .env.prod .env 2 | source .env 3 | rm -rf yarn.lock node_modules build package-lock.json 4 | yarn install 5 | yarn add --dev @typescript-eslint/eslint-plugin @typescript-eslint/parser prettier eslint-config-prettier eslint-plugin-prettier react-app-rewired react-app-rewire-alias 6 | yarn build 7 | -------------------------------------------------------------------------------- /deploy_frontend.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Configurations 5 | BUCKET_NAME=${BUCKET_NAME:-"ansari.chat"} 6 | REGION=${REGION:-"us-west1"} 7 | BUILD_DIR=${BUILD_DIR:-"$PWD/dist"} 8 | MAIN_PAGE=${MAIN_PAGE:-"index.html"} 9 | # Make the error page also index.html to do client-side routing 10 | ERROR_PAGE=${ERROR_PAGE:-"index.html"} 11 | 12 | # Validations 13 | [[ -z "$BUCKET_NAME" || ! "$BUCKET_NAME" =~ ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ ]] && { echo "Invalid BUCKET_NAME."; exit 1; } 14 | command -v gcloud &>/dev/null || { echo "gcloud not found."; exit 1; } 15 | 16 | # Build React App (if applicable) 17 | [[ "$REBUILD_APP" == true ]] && { command -v yarn &>/dev/null && yarn build || { echo "Yarn not found."; exit 1; }; } 18 | [[ ! -d "$BUILD_DIR" ]] && { echo "BUILD_DIR not found."; exit 1; } 19 | 20 | # Create Bucket if Not Exists 21 | gcloud storage buckets list --filter="name:$BUCKET_NAME" --format="value(name)" | grep -q "$BUCKET_NAME" || \ 22 | gcloud storage buckets create "gs://$BUCKET_NAME" --location="$REGION" 23 | 24 | # Upload Files 25 | gcloud storage rsync -r --delete-unmatched-destination-objects "$BUILD_DIR" "gs://$BUCKET_NAME" 26 | 27 | # Set Website Configuration 28 | gcloud storage buckets update "gs://$BUCKET_NAME" --web-main-page-suffix="$MAIN_PAGE" --web-error-page="$ERROR_PAGE" 29 | 30 | # Make Bucket Public (if applicable) 31 | [[ "$MAKE_BUCKET_PUBLIC" == true ]] && \ 32 | gcloud storage buckets add-iam-policy-binding "gs://$BUCKET_NAME" --member="allUsers" --role="roles/storage.objectViewer" 33 | 34 | # Output Final URL 35 | echo "Deployment complete. Your site: https://$BUCKET_NAME" 36 | 37 | -------------------------------------------------------------------------------- /docs/docs/faq.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions 2 | 3 | ## What is Ansari? 4 | 5 | Ansari is an AI assistant to help increase understanding of the Islamic faith and help Muslims practise their faith more effectively. 6 | 7 | ## Who wrote Ansari? 8 | 9 | Waleed Kadous ([site](http://walee.dk/home), [LinkedIn](https://www.linkedin.com/in/waleedkadous/)) is the primary author of Ansari. EndeavorPal has been helping with the web frontend and mobile applications. 10 | 11 | ## Where do I send my thoughts, errors, feedback about Ansari? 12 | 13 | Please send to [feedback@ansari.chat]("mailto:feedback@ansari.chat"). It is checked daily. 14 | 15 | ## Is there a particular Large Language Model Ansari uses? 16 | 17 | Yes. It uses GPT-4-Turbo from [OpenAI](https://openai.com). We have also tried GPT-3.5-Turbo, but the accuracy really suffers. In one test, GPT-4-Turbo running Ansari got 97% accuracy; GPT-3.5-Turbo with identical prompts, tools etc, only got 77% accuracy. 18 | 19 | ## How is it different to GPT-4-Turbo, then? 20 | 21 | It differs in the following ways: 22 | 23 | - Carefully crafted system prompts to ensure information is presented from an Islamic perspective. 24 | - Tools specifically to retrieve Qur'an and Sunnah for retrieval augmented generation. This helps significantly reduce hallucination. 25 | - Has been validated on multiple Islamic data sets. 26 | - Is constrained to focus and answer questions primarily on topics related to Islam. 27 | 28 | ## Is Ansari Open Source? 29 | 30 | Yes it is. Here is the source code for the [backend](https://github.com/waleedkadous/ansari-backend) and the [frontend](https://github.com/waleedkadous/ansari-frontend). 31 | 32 | ## Does Ansari cost money? 33 | 34 | Users do not have to pay anything to use Ansari. However, LLMs are very expensive to use (especially high end onesl ike GPT-4-Turbo). So each request and response costs about 5c or so. 35 | 36 | -------------------------------------------------------------------------------- /docs/docs/index.md: -------------------------------------------------------------------------------- 1 | ## Ansari 2 | 3 | Ansari is an AI assistant to increase understanding of the Islamic faith and to help Muslims improve their practice of Islam. 4 | 5 | Since AI assistants are such a new thing and Islamic AI assistants even moreso, this documentation is intended to help explain how to use Ansari and share some of the findings and work behind it. 6 | 7 | In the rest of this documentation, we will cover: 8 | 9 | - [What Ansari can do](capabilities.md) 10 | - [The work that has gone in to validating Ansari](validation.md). 11 | - [Frequently asked questions about Ansari](faq.md). 12 | 13 | There's also a section on [additional material](material.md) about Ansari. 14 | 15 | If you have any suggestions for the documentation, please send e-mail to [feedback@ansari.chat](mailto:feedback@ansari.chat). 16 | 17 | -------------------------------------------------------------------------------- /docs/docs/material.md: -------------------------------------------------------------------------------- 1 | # Additional Material 2 | 3 | ## Presentation about Ansari at International Conference on Islamic Applications in Computer Science and Technologies 2023 4 | 5 | ### Video 6 | 7 | 8 | 9 | ### Slides 10 | 11 |
Ansari: Practical experiences with an LLM-based Islamic Assistant from M Waleed Kadous
12 | -------------------------------------------------------------------------------- /docs/mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Ansari 2 | site_url: https://beta.ansari.chat/docs 3 | nav: 4 | - Home: index.md 5 | - What Ansari can do: capabilities.md 6 | - Validation: validation.md 7 | - FAQ: faq.md 8 | - Additional material: material.md 9 | 10 | theme: readthedocs 11 | 12 | -------------------------------------------------------------------------------- /eas.json: -------------------------------------------------------------------------------- 1 | { 2 | "cli": { 3 | "version": ">= 15.0.12", 4 | "appVersionSource": "remote" 5 | }, 6 | "build": { 7 | "development": { 8 | "environment": "development", 9 | "channel": "development", 10 | "developmentClient": true, 11 | "distribution": "internal" 12 | }, 13 | "preview": { 14 | "environment": "preview", 15 | "channel": "preview", 16 | "distribution": "internal" 17 | }, 18 | "production-internal": { 19 | "environment": "production", 20 | "channel": "production", 21 | "distribution": "internal" 22 | }, 23 | "production": { 24 | "environment": "production", 25 | "channel": "production", 26 | "autoIncrement": true, 27 | "android": { 28 | "credentialsSource": "remote" 29 | } 30 | } 31 | }, 32 | "submit": { 33 | "production": { 34 | "android": { 35 | "serviceAccountKeyPath": "./google-service-account/service-account.json", 36 | "track": "internal" 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /metro.config.js: -------------------------------------------------------------------------------- 1 | const { getSentryExpoConfig } = require('@sentry/react-native/metro') 2 | const { withNativeWind } = require('nativewind/metro') 3 | const { wrapWithReanimatedMetroConfig } = require('react-native-reanimated/metro-config') 4 | 5 | const config = getSentryExpoConfig(__dirname) 6 | 7 | const nativeWindConfig = withNativeWind(config, { input: './src/global.css' }) 8 | 9 | module.exports = wrapWithReanimatedMetroConfig(nativeWindConfig) 10 | -------------------------------------------------------------------------------- /nativewind-env.d.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line spaced-comment 2 | /// 3 | -------------------------------------------------------------------------------- /nginx.conf.sample: -------------------------------------------------------------------------------- 1 | # HTTP Server Configuration 2 | 3 | server { 4 | listen 80; 5 | listen [::]:80; 6 | server_name {your-domain}; # Replace with your domain name 7 | 8 | if ($host = {your-domain}) { 9 | return 301 https://$host$request_uri; 10 | } 11 | 12 | return 404; 13 | } 14 | 15 | server { 16 | server_name {your-domain}; # Replace with your domain name 17 | root {project-path}/build; # Replace with the path to your project build directory 18 | 19 | # HTTPS Server Configuration (Uncomment and configure if SSL is enabled) 20 | listen 443 ssl; 21 | listen [::]:443 ssl; 22 | server_name {your-domain}; # Replace with your domain name 23 | 24 | ssl_certificate {path-to-ssl-keys}/fullchain.pem; # SSL certificate path 25 | ssl_certificate_key {path-to-ssl-keys}/privkey.pem; # SSL key path 26 | 27 | # Default configurations 28 | index index.html; 29 | charset utf-8; 30 | 31 | # Main location block 32 | location / { 33 | try_files $uri $uri/ /index.html?$query_string; 34 | } 35 | 36 | # Proxy Configuration (Uncomment and configure if needed) 37 | # location /api { 38 | # proxy_buffering off; 39 | # proxy_pass {backend-path}/api; # Replace with your backend path 40 | # } 41 | 42 | # Static file caching 43 | location ~* \.(ico|css|js|gif|jpe?g|png|webp)$ { 44 | expires 30d; 45 | add_header Vary Accept-Encoding; 46 | access_log off; 47 | } 48 | 49 | # Security configurations 50 | location ~ /\.ht { 51 | deny all; 52 | } 53 | 54 | location ~ (/\.ht|web.config|/\.git|/\.env) { 55 | deny all; 56 | } 57 | 58 | # Logging configurations 59 | access_log off; 60 | error_log /var/log/nginx/error.log error; 61 | } 62 | -------------------------------------------------------------------------------- /public/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansari-project/ansari-frontend/e23a4ef43ae9a8f2c0943d40a94ed83c93c7eb45/public/icon-192x192.png -------------------------------------------------------------------------------- /public/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansari-project/ansari-frontend/e23a4ef43ae9a8f2c0943d40a94ed83c93c7eb45/public/icon-256x256.png -------------------------------------------------------------------------------- /public/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansari-project/ansari-frontend/e23a4ef43ae9a8f2c0943d40a94ed83c93c7eb45/public/icon-512x512.png -------------------------------------------------------------------------------- /public/images/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansari-project/ansari-frontend/e23a4ef43ae9a8f2c0943d40a94ed83c93c7eb45/public/images/background.png -------------------------------------------------------------------------------- /public/logo-192_x_192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansari-project/ansari-frontend/e23a4ef43ae9a8f2c0943d40a94ed83c93c7eb45/public/logo-192_x_192.png -------------------------------------------------------------------------------- /public/logo-256_x_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansari-project/ansari-frontend/e23a4ef43ae9a8f2c0943d40a94ed83c93c7eb45/public/logo-256_x_256.png -------------------------------------------------------------------------------- /public/logo-512_x_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansari-project/ansari-frontend/e23a4ef43ae9a8f2c0943d40a94ed83c93c7eb45/public/logo-512_x_512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Ansari Chat", 3 | "name": "Ansari Chat App", 4 | "icons": [ 5 | { 6 | "src": "logo-192_x_192.png", 7 | "type": "image/png", 8 | "sizes": "192x192" 9 | }, 10 | { 11 | "src": "logo-256_x_256.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo-512_x_512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "scope": "/", 23 | "display": "standalone", 24 | "theme_color": "#08786B", 25 | "background_color": "#ffffff", 26 | "orientation": "any" 27 | } 28 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/app/(app)/_layout.tsx: -------------------------------------------------------------------------------- 1 | import { useAuth } from '@/hooks' 2 | import React from 'react' 3 | import { View } from 'react-native' 4 | import Footer from '@/components/Footer' 5 | import Header from '@/components/Header' 6 | import { MenuDrawer } from '@/components/menu' 7 | import { Redirect, Slot } from 'expo-router' 8 | import RootImageBackground from '@/components/RootImageBackground' 9 | import { SafeAreaView } from 'react-native-safe-area-context' 10 | import KeyboardHandler from '@/components/KeyboardHandler' 11 | 12 | /** 13 | * AppLayout Component. 14 | * This component serves as the main layout for the application. 15 | * It includes the header, side menu, and main content area. 16 | */ 17 | export const AppLayout = () => { 18 | // Hook to check authentication status 19 | const { isAuthenticated, accessToken } = useAuth() 20 | 21 | const showFooter = true 22 | 23 | if (!isAuthenticated || !accessToken) { 24 | return 25 | } 26 | 27 | return ( 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | 37 | {showFooter &&