From 2c697b4072e694f3ab33896e90a07306565659d6 Mon Sep 17 00:00:00 2001 From: vmikhalevskii Date: Tue, 11 Aug 2026 10:56:58 +0300 Subject: [PATCH 01/17] chore: add expo-store-review --- apps/mobile/package.json | 1 + package-lock.json | 12 ++++++++++++ package.json | 1 + 3 files changed, 14 insertions(+) diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 0e8238c..e7fe4c1 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -73,6 +73,7 @@ "expo-speech": "~57.0.1", "expo-splash-screen": "~57.0.5", "expo-status-bar": "~57.0.1", + "expo-store-review": "~57.0.1", "expo-updates": "~57.0.10", "html-entities": "^2.6.0", "i18n-js": "^4.5.1", diff --git a/package-lock.json b/package-lock.json index b63861a..65bee50 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,6 +62,7 @@ "expo-speech": "~57.0.1", "expo-splash-screen": "~57.0.5", "expo-status-bar": "~57.0.1", + "expo-store-review": "~57.0.1", "expo-updates": "~57.0.10", "html-entities": "^2.6.0", "i18n-js": "^4.5.1", @@ -219,6 +220,7 @@ "expo-speech": "~57.0.1", "expo-splash-screen": "~57.0.5", "expo-status-bar": "~57.0.1", + "expo-store-review": "~57.0.1", "expo-updates": "~57.0.10", "html-entities": "^2.6.0", "i18n-js": "^4.5.1", @@ -17901,6 +17903,16 @@ "react-native": "*" } }, + "node_modules/expo-store-review": { + "version": "57.0.1", + "resolved": "https://registry.npmjs.org/expo-store-review/-/expo-store-review-57.0.1.tgz", + "integrity": "sha512-OktDBfIEe4DQXVxz7umFyg+slkZo/nrr4GfwiFJlNjOXl+F+vNsaGBdEn1aE4W5DAnSVpLQsjaZt1dts2HJXlQ==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react-native": "*" + } + }, "node_modules/expo-structured-headers": { "version": "57.0.0", "resolved": "https://registry.npmjs.org/expo-structured-headers/-/expo-structured-headers-57.0.0.tgz", diff --git a/package.json b/package.json index b42fa24..793b5fb 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "expo-speech": "~57.0.1", "expo-splash-screen": "~57.0.5", "expo-status-bar": "~57.0.1", + "expo-store-review": "~57.0.1", "expo-updates": "~57.0.10", "html-entities": "^2.6.0", "i18n-js": "^4.5.1", From fdb25f84f096c9dff57271f8aba7ed6952dfe75c Mon Sep 17 00:00:00 2001 From: vmikhalevskii Date: Tue, 11 Aug 2026 15:11:54 +0300 Subject: [PATCH 02/17] feat: add new icons --- libs/mobile/shared/ui/ui-kit/src/assets/icons/index.ts | 4 ++++ libs/mobile/shared/ui/ui-kit/src/assets/icons/message.svg | 4 ++++ libs/mobile/shared/ui/ui-kit/src/assets/icons/star.svg | 3 +++ 3 files changed, 11 insertions(+) create mode 100644 libs/mobile/shared/ui/ui-kit/src/assets/icons/message.svg create mode 100644 libs/mobile/shared/ui/ui-kit/src/assets/icons/star.svg diff --git a/libs/mobile/shared/ui/ui-kit/src/assets/icons/index.ts b/libs/mobile/shared/ui/ui-kit/src/assets/icons/index.ts index ec8ca0e..f876082 100644 --- a/libs/mobile/shared/ui/ui-kit/src/assets/icons/index.ts +++ b/libs/mobile/shared/ui/ui-kit/src/assets/icons/index.ts @@ -34,6 +34,7 @@ import logoLight from './logo-light.svg'; import logoSmallDark from './logo-small-dark.svg'; import logoSmallLight from './logo-small-light.svg'; import logout from './logout.svg'; +import message from './message.svg'; import microphone from './microphone.svg'; import moreDots from './more-dots.svg'; import moreText from './more-text.svg'; @@ -44,6 +45,7 @@ import plusInCircle from './plus-in-circle.svg'; import plus from './plus.svg'; import refresh from './refresh.svg'; import search from './search.svg'; +import star from './star.svg'; import stop from './stop.svg'; import strokeLeft from './stroke-left.svg'; import tick from './tick.svg'; @@ -96,11 +98,13 @@ export const Icons = { plusInCircle, folderPlus, folder, + message, microphone, unarchive, closeSM, strokeLeft, tick, + star, stop, play, refresh, diff --git a/libs/mobile/shared/ui/ui-kit/src/assets/icons/message.svg b/libs/mobile/shared/ui/ui-kit/src/assets/icons/message.svg new file mode 100644 index 0000000..4d23451 --- /dev/null +++ b/libs/mobile/shared/ui/ui-kit/src/assets/icons/message.svg @@ -0,0 +1,4 @@ + + + + diff --git a/libs/mobile/shared/ui/ui-kit/src/assets/icons/star.svg b/libs/mobile/shared/ui/ui-kit/src/assets/icons/star.svg new file mode 100644 index 0000000..453380f --- /dev/null +++ b/libs/mobile/shared/ui/ui-kit/src/assets/icons/star.svg @@ -0,0 +1,3 @@ + + + From 0ac18cdc0016d0d726c255e7bf44a296a8a974ef Mon Sep 17 00:00:00 2001 From: vmikhalevskii Date: Tue, 11 Aug 2026 15:12:56 +0300 Subject: [PATCH 03/17] feat: add StoreReviewService --- apps/mobile/.env.template | 1 + apps/mobile/app.config.ts | 2 ++ .../utils/store-review-service/.babelrc | 12 ++++++++++++ .../utils/store-review-service/README.md | 7 +++++++ .../store-review-service/eslint.config.cjs | 12 ++++++++++++ .../utils/store-review-service/project.json | 9 +++++++++ .../utils/store-review-service/src/index.ts | 1 + .../utils/store-review-service/src/service.ts | 17 +++++++++++++++++ .../utils/store-review-service/tsconfig.json | 17 +++++++++++++++++ .../store-review-service/tsconfig.lib.json | 19 +++++++++++++++++++ tsconfig.base.json | 3 +++ 11 files changed, 100 insertions(+) create mode 100644 libs/shared/utils/store-review-service/.babelrc create mode 100644 libs/shared/utils/store-review-service/README.md create mode 100644 libs/shared/utils/store-review-service/eslint.config.cjs create mode 100644 libs/shared/utils/store-review-service/project.json create mode 100644 libs/shared/utils/store-review-service/src/index.ts create mode 100644 libs/shared/utils/store-review-service/src/service.ts create mode 100644 libs/shared/utils/store-review-service/tsconfig.json create mode 100644 libs/shared/utils/store-review-service/tsconfig.lib.json diff --git a/apps/mobile/.env.template b/apps/mobile/.env.template index da8ec8d..29c5043 100644 --- a/apps/mobile/.env.template +++ b/apps/mobile/.env.template @@ -1,6 +1,7 @@ EXPO_PUBLIC_PROJECT_ID= EXPO_PUBLIC_APP_ID= +EXPO_PUBLIC_IOS_APP_STORE_ID= EXPO_PUBLIC_APP_OWNER= EXPO_PUBLIC_APP_SCHEME= EXPO_PUBLIC_APP_NAME= diff --git a/apps/mobile/app.config.ts b/apps/mobile/app.config.ts index 65ed104..e066587 100644 --- a/apps/mobile/app.config.ts +++ b/apps/mobile/app.config.ts @@ -47,6 +47,7 @@ const createConfig = (): Omit & { extra: { eas: EASConfig } }, ios: { bundleIdentifier: appId, + appStoreUrl: `https://apps.apple.com/app/id${process.env.EXPO_PUBLIC_IOS_APP_STORE_ID}`, supportsTablet: false, buildNumber: appEnv.select({ default: '18', @@ -66,6 +67,7 @@ const createConfig = (): Omit & { extra: { eas: EASConfig } }, android: { package: appId, + playStoreUrl: `https://play.google.com/store/apps/details?id=${appId}`, versionCode: appEnv.select({ default: 15, production: 36, diff --git a/libs/shared/utils/store-review-service/.babelrc b/libs/shared/utils/store-review-service/.babelrc new file mode 100644 index 0000000..1ea870e --- /dev/null +++ b/libs/shared/utils/store-review-service/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/libs/shared/utils/store-review-service/README.md b/libs/shared/utils/store-review-service/README.md new file mode 100644 index 0000000..d358961 --- /dev/null +++ b/libs/shared/utils/store-review-service/README.md @@ -0,0 +1,7 @@ +# shared/utils/store-review-service + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test shared/utils/store-review-service` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/shared/utils/store-review-service/eslint.config.cjs b/libs/shared/utils/store-review-service/eslint.config.cjs new file mode 100644 index 0000000..87eef8a --- /dev/null +++ b/libs/shared/utils/store-review-service/eslint.config.cjs @@ -0,0 +1,12 @@ +const nx = require('@nx/eslint-plugin'); +const baseConfig = require('../../../../eslint.config.cjs'); + +module.exports = [ + ...baseConfig, + ...nx.configs['flat/react'], + { + files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], + // Override or add rules here + rules: {}, + }, +]; diff --git a/libs/shared/utils/store-review-service/project.json b/libs/shared/utils/store-review-service/project.json new file mode 100644 index 0000000..985f690 --- /dev/null +++ b/libs/shared/utils/store-review-service/project.json @@ -0,0 +1,9 @@ +{ + "name": "shared/utils/store-review-service", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/shared/utils/store-review-service/src", + "projectType": "library", + "tags": ["app:shared", "scope:shared", "type:utils"], + "// targets": "to see all targets run: nx show project shared/utils/store-review-service --web", + "targets": {} +} diff --git a/libs/shared/utils/store-review-service/src/index.ts b/libs/shared/utils/store-review-service/src/index.ts new file mode 100644 index 0000000..f78beab --- /dev/null +++ b/libs/shared/utils/store-review-service/src/index.ts @@ -0,0 +1 @@ +export * from './service'; diff --git a/libs/shared/utils/store-review-service/src/service.ts b/libs/shared/utils/store-review-service/src/service.ts new file mode 100644 index 0000000..e5a3cb8 --- /dev/null +++ b/libs/shared/utils/store-review-service/src/service.ts @@ -0,0 +1,17 @@ +import { storeUrl } from 'expo-store-review'; +import { Linking, Platform } from 'react-native'; + +class StoreReviewService { + public async openStoreReview(): Promise { + const isAndroid = Platform.OS === 'android'; + const url = storeUrl(); + + const canOpenUrl = url && (await Linking.canOpenURL(url)); + + if (canOpenUrl) { + await Linking.openURL(`${url}${isAndroid ? '&showAllReviews=true' : `?action=write-review`}`); + } + } +} + +export const storeReviewService = new StoreReviewService(); diff --git a/libs/shared/utils/store-review-service/tsconfig.json b/libs/shared/utils/store-review-service/tsconfig.json new file mode 100644 index 0000000..93614c4 --- /dev/null +++ b/libs/shared/utils/store-review-service/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ], + "extends": "../../../../tsconfig.base.json" +} diff --git a/libs/shared/utils/store-review-service/tsconfig.lib.json b/libs/shared/utils/store-review-service/tsconfig.lib.json new file mode 100644 index 0000000..45b2297 --- /dev/null +++ b/libs/shared/utils/store-review-service/tsconfig.lib.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../dist/out-tsc", + "types": ["node", "@nx/react/typings/cssmodule.d.ts", "@nx/react/typings/image.d.ts"] + }, + "exclude": [ + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts", + "src/**/*.spec.tsx", + "src/**/*.test.tsx", + "src/**/*.spec.js", + "src/**/*.test.js", + "src/**/*.spec.jsx", + "src/**/*.test.jsx" + ], + "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] +} diff --git a/tsconfig.base.json b/tsconfig.base.json index 7c830cb..93ed2cb 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -269,6 +269,9 @@ "@open-webui-react-native/shared/utils/objects": ["libs/shared/utils/objects/src/index.ts"], "@open-webui-react-native/shared/utils/permission-alert": ["libs/shared/utils/permission-alert/src/index.ts"], "@open-webui-react-native/shared/utils/reactotron": ["libs/shared/utils/reactotron/src/index.ts"], + "@open-webui-react-native/shared/utils/store-review-service": [ + "libs/shared/utils/store-review-service/src/index.ts" + ], "@open-webui-react-native/shared/utils/strings": ["libs/shared/utils/strings/src/index.ts"], "@open-webui-react-native/shared/utils/toast-service": ["libs/shared/utils/toast-service/src/index.ts"], "@open-webui-react-native/shared/utils/use-app-state-change": [ From 0cbedb76a86a7419ba17a6129c695a0b254dc17b Mon Sep 17 00:00:00 2001 From: vmikhalevskii Date: Thu, 13 Aug 2026 16:27:41 +0300 Subject: [PATCH 04/17] feat: add request store review logic --- .../chat/features/chat/src/lib/component.tsx | 17 +++++++++-- .../chat/features/chat/src/lib/utils/index.ts | 1 + .../src/lib/utils/request-store-review.ts | 28 +++++++++++++++++++ .../shared/data-access/storage/src/service.ts | 2 ++ .../utils/store-review-service/src/service.ts | 14 +++++++++- 5 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 libs/mobile/chat/features/chat/src/lib/utils/request-store-review.ts diff --git a/libs/mobile/chat/features/chat/src/lib/component.tsx b/libs/mobile/chat/features/chat/src/lib/component.tsx index 432862e..823cfbd 100644 --- a/libs/mobile/chat/features/chat/src/lib/component.tsx +++ b/libs/mobile/chat/features/chat/src/lib/component.tsx @@ -2,7 +2,7 @@ import { useSelector } from '@legendapp/state/react'; import { useTranslation } from '@ronas-it/react-native-common-modules/i18n'; import dayjs from 'dayjs'; import { delay } from 'lodash-es'; -import React, { Fragment, ReactElement, useEffect, useMemo, useState } from 'react'; +import React, { Fragment, ReactElement, useEffect, useMemo, useRef, useState } from 'react'; import { useForm } from 'react-hook-form'; import { InteractionManager } from 'react-native'; import { EditMessageInput } from '@open-webui-react-native/mobile/chat/features/edit-message-input'; @@ -28,7 +28,7 @@ import { AnalyticsEvent, analyticsService } from '@open-webui-react-native/share import { ToastService } from '@open-webui-react-native/shared/utils/toast-service'; import { useAppStateChange } from '@open-webui-react-native/shared/utils/use-app-state-change'; import { ActiveInputMode } from './enums'; -import { patchNewChat } from './utils'; +import { patchNewChat, requestStoreReview } from './utils'; const LazyChatMessagesList = React.lazy(() => import('./components/messages-list/component')); @@ -85,7 +85,11 @@ export function Chat({ chatId, selectedModelId, isNewChat, resetToChatsList }: C const history = chat?.chat.history; // NOTE: chat.messages is a legacy snapshot the backend no longer maintains, so the current branch is derived from history const messages = useMemo(() => (history ? createMessagesList(history, history.currentId) : []), [history]); - const isResponseGenerating = !history?.messages[history.currentId].done; + const currentMessage = history?.messages[history.currentId]; + const isResponseGenerating = !currentMessage?.done; + const isAssistantMessage = currentMessage?.role === Role.ASSISTANT; + + const firstMessageGeneratedRef = useRef(false); const shouldHideContent = isLoading || isRefetching || !isMessagesListLoaded || !selectedModelId; @@ -208,6 +212,13 @@ export function Chat({ chatId, selectedModelId, isNewChat, resetToChatsList }: C } }, [isNewChat, isSuccess, chatId]); + useEffect(() => { + if (isNewChat && !firstMessageGeneratedRef.current && isAssistantMessage && currentMessage.done) { + firstMessageGeneratedRef.current = true; + requestStoreReview(chatId); + } + }, [isNewChat, chatId, currentMessage?.done, isAssistantMessage]); + return ( {shouldHideContent && ( diff --git a/libs/mobile/chat/features/chat/src/lib/utils/index.ts b/libs/mobile/chat/features/chat/src/lib/utils/index.ts index 047452f..28df7ca 100644 --- a/libs/mobile/chat/features/chat/src/lib/utils/index.ts +++ b/libs/mobile/chat/features/chat/src/lib/utils/index.ts @@ -1,3 +1,4 @@ export * from './get-siblings-ids'; export * from './patch-new-chat'; export * from './parse-response-message-content'; +export * from './request-store-review'; diff --git a/libs/mobile/chat/features/chat/src/lib/utils/request-store-review.ts b/libs/mobile/chat/features/chat/src/lib/utils/request-store-review.ts new file mode 100644 index 0000000..acefbe5 --- /dev/null +++ b/libs/mobile/chat/features/chat/src/lib/utils/request-store-review.ts @@ -0,0 +1,28 @@ +import { appStorageService } from '@open-webui-react-native/shared/data-access/storage'; +import { storeReviewService } from '@open-webui-react-native/shared/utils/store-review-service'; + +export const requestStoreReview = async (chatId: string): Promise => { + const createdChatsIdsString = appStorageService.createdChatsIds.get(); + const isRateAppReviewed = appStorageService.isRateAppReviewed.get(); + + if (isRateAppReviewed === 'true') { + return; + } + + const createdChatsIds = createdChatsIdsString ? createdChatsIdsString.split(',') : []; + + if (createdChatsIds.length >= 2) { + const isReviewRequested = await storeReviewService.requestStoreReview(); + + if (isReviewRequested) { + appStorageService.isRateAppReviewed.set('true'); + } + + return; + } + + if (!createdChatsIds.includes(chatId)) { + // If amount of created chats is less than 2, we just add the current chatId to the list of created chats + appStorageService.createdChatsIds.set(`${createdChatsIds.length ? createdChatsIds + ',' : ''}` + chatId); + } +}; diff --git a/libs/shared/data-access/storage/src/service.ts b/libs/shared/data-access/storage/src/service.ts index d9c3e01..fd4af81 100644 --- a/libs/shared/data-access/storage/src/service.ts +++ b/libs/shared/data-access/storage/src/service.ts @@ -11,6 +11,8 @@ export const storage = new MMKV({ class AppStorageService { public token = new StorageItem('token', storage); public apiUrl = new StorageItem('apiUrl', storage); + public createdChatsIds = new StorageItem('createdChatsIds', storage); + public isRateAppReviewed = new StorageItem('isRateAppReviewed', storage); } export const appStorageService = new AppStorageService(); diff --git a/libs/shared/utils/store-review-service/src/service.ts b/libs/shared/utils/store-review-service/src/service.ts index e5a3cb8..c874b80 100644 --- a/libs/shared/utils/store-review-service/src/service.ts +++ b/libs/shared/utils/store-review-service/src/service.ts @@ -1,4 +1,4 @@ -import { storeUrl } from 'expo-store-review'; +import { storeUrl, hasAction, requestReview } from 'expo-store-review'; import { Linking, Platform } from 'react-native'; class StoreReviewService { @@ -12,6 +12,18 @@ class StoreReviewService { await Linking.openURL(`${url}${isAndroid ? '&showAllReviews=true' : `?action=write-review`}`); } } + + public async requestStoreReview(): Promise { + const isAvailable = await hasAction(); + + if (isAvailable) { + await requestReview(); + + return true; + } else { + return false; + } + } } export const storeReviewService = new StoreReviewService(); From ff84fb359f5d1c5bf98c9a7f06343d6d58c30967 Mon Sep 17 00:00:00 2001 From: vmikhalevskii Date: Fri, 14 Aug 2026 17:42:19 +0300 Subject: [PATCH 05/17] chore: add @supabase/supabase-js --- apps/mobile/package.json | 2 + package-lock.json | 114 +++++++++++++++++++++++++++++++++++++++ package.json | 2 + 3 files changed, 118 insertions(+) diff --git a/apps/mobile/package.json b/apps/mobile/package.json index e7fe4c1..ce911ad 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -42,6 +42,7 @@ "@ronas-it/rtkq-entity-api": "^1.1.1", "@sentry/react-native": "~7.11.0", "@shopify/flash-list": "2.0.2", + "@supabase/supabase-js": "^2.112.3", "@tanstack/react-query": "^5.80.6", "@tanstack/react-query-persist-client": "^5.87.4", "axios-observable": "^2.1.0", @@ -107,6 +108,7 @@ "react-native-svg": "15.15.4", "react-native-svg-transformer": "~1.5.0", "react-native-toast-message": "^2.3.0", + "react-native-url-polyfill": "^4.0.0", "react-native-uuid": "^2.0.3", "react-native-web": "^0.21.0", "react-native-webview": "13.16.1", diff --git a/package-lock.json b/package-lock.json index 65bee50..f45b64d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "@ronas-it/rtkq-entity-api": "^1.1.1", "@sentry/react-native": "~7.11.0", "@shopify/flash-list": "2.0.2", + "@supabase/supabase-js": "^2.112.3", "@tanstack/react-query": "^5.80.6", "@tanstack/react-query-persist-client": "^5.87.4", "axios-observable": "^2.1.0", @@ -96,6 +97,7 @@ "react-native-svg": "15.15.4", "react-native-svg-transformer": "~1.5.0", "react-native-toast-message": "^2.3.0", + "react-native-url-polyfill": "^4.0.0", "react-native-uuid": "^2.0.3", "react-native-web": "^0.21.0", "react-native-webview": "13.16.1", @@ -189,6 +191,7 @@ "@ronas-it/rtkq-entity-api": "^1.1.1", "@sentry/react-native": "~7.11.0", "@shopify/flash-list": "2.0.2", + "@supabase/supabase-js": "^2.112.3", "@tanstack/react-query": "^5.80.6", "@tanstack/react-query-persist-client": "^5.87.4", "axios-observable": "^2.1.0", @@ -254,6 +257,7 @@ "react-native-svg": "15.15.4", "react-native-svg-transformer": "~1.5.0", "react-native-toast-message": "^2.3.0", + "react-native-url-polyfill": "^4.0.0", "react-native-uuid": "^2.0.3", "react-native-web": "^0.21.0", "react-native-webview": "13.16.1", @@ -9846,6 +9850,98 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/@supabase/auth-js": { + "version": "2.112.3", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.112.3.tgz", + "integrity": "sha512-NA0rsgAlWZPvbhw8aUdmgfpHVgUAcd8zK5ov43l++o1bLIPXZhRiAlRobhwF5AatQuovpqxsMH50F4oyyV4XZw==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.112.3", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.112.3.tgz", + "integrity": "sha512-gfv481mTOVWtZIJgXupxZpni2V2UWPf6jeF/jOK7HdMHdH+mt6sU0sHHwf0POsPip8ltlulu9OUHgwVzl5ddRw==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@supabase/phoenix": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/@supabase/phoenix/-/phoenix-0.4.5.tgz", + "integrity": "sha512-aAn9H9ovVyeApKy11OWOrrOGq8DV68yWeH4ud2lN9fzn4aO8Zb5GLL9m1pUg9nLqIcT+ZDfAcsZe0E/nqdv2lw==", + "license": "MIT" + }, + "node_modules/@supabase/postgrest-js": { + "version": "2.112.3", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.112.3.tgz", + "integrity": "sha512-+Mf6uCpzr00bqxwX8hTK2X2L9eAL/1vuOjdEjx6upz9ulb0RmQT16XeU/JkMUlVHw/B46ZnPa2busY4Kd9YCzw==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.112.3", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.112.3.tgz", + "integrity": "sha512-E6wljXWs7DUOloyIB69i3YFInWE6IyCvgTAbQ0KYxOHv26FdA1KzEXTuzxrYEdf70t406Z9BRwUlGyclGF2FXA==", + "license": "MIT", + "dependencies": { + "@supabase/phoenix": "0.4.5", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.112.3", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.112.3.tgz", + "integrity": "sha512-oSK61tzlUvg+BWPqpKQCu9qqonsO26btaoAR9D6Gest2aj7xUqToj9rKyaoYOJczkhg9BjqA1REbYy9tPI4bDA==", + "license": "MIT", + "dependencies": { + "iceberg-js": "^0.8.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.112.3", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.112.3.tgz", + "integrity": "sha512-Jv1bxVQmEJNkjvPEhFaKjPzsh+Ozyew6lWGD+SoYcsclDEP1z7yEvKvfUQfzy0DkxRIQnZNxmmWtAzw5XLTQoA==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.112.3", + "@supabase/functions-js": "2.112.3", + "@supabase/postgrest-js": "2.112.3", + "@supabase/realtime-js": "2.112.3", + "@supabase/storage-js": "2.112.3" + }, + "engines": { + "node": ">=22.0.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + } + } + }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", @@ -20513,6 +20609,15 @@ "make-plural": "7.5.0" } }, + "node_modules/iceberg-js": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", + "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -28752,6 +28857,15 @@ "react-native": "*" } }, + "node_modules/react-native-url-polyfill": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-4.0.0.tgz", + "integrity": "sha512-eqYM3wBAA0eL1sPYbBAoNfbES3+NkgcxUdelQ7QzmoVtqKB5qGG0U13MPTRUroAWK+y2EoJFS3MZUK0fwTf0pA==", + "license": "MIT", + "peerDependencies": { + "react-native": "*" + } + }, "node_modules/react-native-uuid": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/react-native-uuid/-/react-native-uuid-2.0.4.tgz", diff --git a/package.json b/package.json index 793b5fb..ec1a925 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@ronas-it/rtkq-entity-api": "^1.1.1", "@sentry/react-native": "~7.11.0", "@shopify/flash-list": "2.0.2", + "@supabase/supabase-js": "^2.112.3", "@tanstack/react-query": "^5.80.6", "@tanstack/react-query-persist-client": "^5.87.4", "axios-observable": "^2.1.0", @@ -94,6 +95,7 @@ "react-native-svg": "15.15.4", "react-native-svg-transformer": "~1.5.0", "react-native-toast-message": "^2.3.0", + "react-native-url-polyfill": "^4.0.0", "react-native-uuid": "^2.0.3", "react-native-web": "^0.21.0", "react-native-webview": "13.16.1", From 1d852224248be09056c6e14ec8d17e8b9c39d164 Mon Sep 17 00:00:00 2001 From: vmikhalevskii Date: Fri, 14 Aug 2026 17:42:42 +0300 Subject: [PATCH 06/17] feat: add new icons --- .../shared/ui/ui-kit/src/assets/icons/attachment-round.svg | 4 ++++ libs/mobile/shared/ui/ui-kit/src/assets/icons/attachment.svg | 3 +++ libs/mobile/shared/ui/ui-kit/src/assets/icons/index.ts | 4 ++++ 3 files changed, 11 insertions(+) create mode 100644 libs/mobile/shared/ui/ui-kit/src/assets/icons/attachment-round.svg create mode 100644 libs/mobile/shared/ui/ui-kit/src/assets/icons/attachment.svg diff --git a/libs/mobile/shared/ui/ui-kit/src/assets/icons/attachment-round.svg b/libs/mobile/shared/ui/ui-kit/src/assets/icons/attachment-round.svg new file mode 100644 index 0000000..805b56e --- /dev/null +++ b/libs/mobile/shared/ui/ui-kit/src/assets/icons/attachment-round.svg @@ -0,0 +1,4 @@ + + + + diff --git a/libs/mobile/shared/ui/ui-kit/src/assets/icons/attachment.svg b/libs/mobile/shared/ui/ui-kit/src/assets/icons/attachment.svg new file mode 100644 index 0000000..167f315 --- /dev/null +++ b/libs/mobile/shared/ui/ui-kit/src/assets/icons/attachment.svg @@ -0,0 +1,3 @@ + + + diff --git a/libs/mobile/shared/ui/ui-kit/src/assets/icons/index.ts b/libs/mobile/shared/ui/ui-kit/src/assets/icons/index.ts index f876082..db8da0a 100644 --- a/libs/mobile/shared/ui/ui-kit/src/assets/icons/index.ts +++ b/libs/mobile/shared/ui/ui-kit/src/assets/icons/index.ts @@ -2,6 +2,8 @@ import alert from './alert.svg'; import archive from './archive.svg'; import arrowDown from './arrow-down.svg'; import arrowUp from './arrow-up.svg'; +import attachmentRound from './attachment-round.svg'; +import attachment from './attachment.svg'; import camera from './camera.svg'; import checkedSmall from './checked-small.svg'; import checked from './checked.svg'; @@ -72,6 +74,8 @@ export const Icons = { checked, search, arrowUp, + attachmentRound, + attachment, arrowDown, moreDots, archive, From b8cb090cf1241c270da940b069d35c9ef22890f8 Mon Sep 17 00:00:00 2001 From: vmikhalevskii Date: Fri, 14 Aug 2026 17:57:31 +0300 Subject: [PATCH 07/17] feat: add supabaseService and supabaseApi --- apps/mobile/.env.template | 2 ++ apps/mobile/app.config.ts | 4 +++ apps/mobile/app/_layout.tsx | 3 ++ libs/shared/data-access/api/src/lib/index.ts | 1 + .../data-access/api/src/lib/supabase/api.ts | 15 ++++++++ .../data-access/api/src/lib/supabase/index.ts | 1 + libs/shared/utils/supabase-service/.babelrc | 12 +++++++ libs/shared/utils/supabase-service/README.md | 7 ++++ .../utils/supabase-service/eslint.config.cjs | 12 +++++++ .../utils/supabase-service/project.json | 9 +++++ .../utils/supabase-service/src/index.ts | 2 ++ .../supabase-service/src/models/index.ts | 1 + .../src/models/submit-feedback-request.ts | 7 ++++ .../utils/supabase-service/src/service.ts | 35 +++++++++++++++++++ .../utils/supabase-service/tsconfig.json | 17 +++++++++ .../utils/supabase-service/tsconfig.lib.json | 19 ++++++++++ types.d.ts | 1 + 17 files changed, 148 insertions(+) create mode 100644 libs/shared/data-access/api/src/lib/supabase/api.ts create mode 100644 libs/shared/data-access/api/src/lib/supabase/index.ts create mode 100644 libs/shared/utils/supabase-service/.babelrc create mode 100644 libs/shared/utils/supabase-service/README.md create mode 100644 libs/shared/utils/supabase-service/eslint.config.cjs create mode 100644 libs/shared/utils/supabase-service/project.json create mode 100644 libs/shared/utils/supabase-service/src/index.ts create mode 100644 libs/shared/utils/supabase-service/src/models/index.ts create mode 100644 libs/shared/utils/supabase-service/src/models/submit-feedback-request.ts create mode 100644 libs/shared/utils/supabase-service/src/service.ts create mode 100644 libs/shared/utils/supabase-service/tsconfig.json create mode 100644 libs/shared/utils/supabase-service/tsconfig.lib.json diff --git a/apps/mobile/.env.template b/apps/mobile/.env.template index 29c5043..6cd6458 100644 --- a/apps/mobile/.env.template +++ b/apps/mobile/.env.template @@ -10,3 +10,5 @@ EXPO_PUBLIC_IS_INTERNAL_RELEASE= SENTRY_DSN= AMPLITUDE_API_KEY_DEV= AMPLITUDE_API_KEY_PROD= +SUPABASE_URL= +SUPABASE_PUBLISHABLE_KEY= diff --git a/apps/mobile/app.config.ts b/apps/mobile/app.config.ts index e066587..776b2b7 100644 --- a/apps/mobile/app.config.ts +++ b/apps/mobile/app.config.ts @@ -19,6 +19,10 @@ const createConfig = (): Omit & { extra: { eas: EASConfig } apiKeyDev: process.env.AMPLITUDE_API_KEY_DEV, apiKeyProd: process.env.AMPLITUDE_API_KEY_PROD, }, + supabase: { + url: process.env.SUPABASE_URL, + publishableKey: process.env.SUPABASE_PUBLISHABLE_KEY, + }, env: appEnv.current, googleIosClientId: appEnv.select({ default: process.env.GOOGLE_IOS_CLIENT_ID_DEV, diff --git a/apps/mobile/app/_layout.tsx b/apps/mobile/app/_layout.tsx index 7148336..10963cb 100644 --- a/apps/mobile/app/_layout.tsx +++ b/apps/mobile/app/_layout.tsx @@ -17,6 +17,7 @@ import { useNetworkConnection } from '@open-webui-react-native/shared/features/n import { analyticsService } from '@open-webui-react-native/shared/utils/analytics-service'; import { constants } from '@open-webui-react-native/shared/utils/config'; import { setupReactotron } from '@open-webui-react-native/shared/utils/reactotron'; +import { supabaseService } from '@open-webui-react-native/shared/utils/supabase-service'; import { setLanguage } from '@ronas-it/react-native-common-modules/i18n'; import * as Sentry from '@sentry/react-native'; import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'; @@ -30,6 +31,7 @@ import { SafeAreaProvider } from 'react-native-safe-area-context'; import '../global.css'; import 'reflect-metadata'; import 'expo-dev-client'; +import 'react-native-url-polyfill/auto'; export { ErrorBoundary } from 'expo-router'; @@ -58,6 +60,7 @@ const useLanguage = setLanguage(translations, constants.defaultLocale); setupReactotron('open-web-ui'); analyticsService.init(); +supabaseService.init(); // Prevent the splash screen from auto-hiding before asset loading is complete. SplashScreen.preventAutoHideAsync(); diff --git a/libs/shared/data-access/api/src/lib/index.ts b/libs/shared/data-access/api/src/lib/index.ts index 4e90938..147558c 100644 --- a/libs/shared/data-access/api/src/lib/index.ts +++ b/libs/shared/data-access/api/src/lib/index.ts @@ -8,3 +8,4 @@ export * from './audio'; export * from './folders'; export * from './knowledge'; export * from './tasks'; +export * from './supabase'; diff --git a/libs/shared/data-access/api/src/lib/supabase/api.ts b/libs/shared/data-access/api/src/lib/supabase/api.ts new file mode 100644 index 0000000..982a079 --- /dev/null +++ b/libs/shared/data-access/api/src/lib/supabase/api.ts @@ -0,0 +1,15 @@ +import { useMutation, UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; +import { SubmitFeedbackRequest, supabaseService } from '@open-webui-react-native/shared/utils/supabase-service'; + +function useSubmitFeedback( + props?: UseMutationOptions, +): UseMutationResult { + return useMutation({ + mutationFn: (request) => supabaseService.submit(request), + ...props, + }); +} + +export const supabaseApi = { + useSubmitFeedback, +}; diff --git a/libs/shared/data-access/api/src/lib/supabase/index.ts b/libs/shared/data-access/api/src/lib/supabase/index.ts new file mode 100644 index 0000000..b1c13e7 --- /dev/null +++ b/libs/shared/data-access/api/src/lib/supabase/index.ts @@ -0,0 +1 @@ +export * from './api'; diff --git a/libs/shared/utils/supabase-service/.babelrc b/libs/shared/utils/supabase-service/.babelrc new file mode 100644 index 0000000..1ea870e --- /dev/null +++ b/libs/shared/utils/supabase-service/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/libs/shared/utils/supabase-service/README.md b/libs/shared/utils/supabase-service/README.md new file mode 100644 index 0000000..40c05ee --- /dev/null +++ b/libs/shared/utils/supabase-service/README.md @@ -0,0 +1,7 @@ +# shared-utils-supabase-service + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test shared-utils-supabase-service` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/shared/utils/supabase-service/eslint.config.cjs b/libs/shared/utils/supabase-service/eslint.config.cjs new file mode 100644 index 0000000..87eef8a --- /dev/null +++ b/libs/shared/utils/supabase-service/eslint.config.cjs @@ -0,0 +1,12 @@ +const nx = require('@nx/eslint-plugin'); +const baseConfig = require('../../../../eslint.config.cjs'); + +module.exports = [ + ...baseConfig, + ...nx.configs['flat/react'], + { + files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], + // Override or add rules here + rules: {}, + }, +]; diff --git a/libs/shared/utils/supabase-service/project.json b/libs/shared/utils/supabase-service/project.json new file mode 100644 index 0000000..5d5e608 --- /dev/null +++ b/libs/shared/utils/supabase-service/project.json @@ -0,0 +1,9 @@ +{ + "name": "shared-utils-supabase-service", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/shared/utils/supabase-service/src", + "projectType": "library", + "tags": ["app:shared", "scope:shared", "type:utils"], + "// targets": "to see all targets run: nx show project shared-utils-supabase-service --web", + "targets": {} +} diff --git a/libs/shared/utils/supabase-service/src/index.ts b/libs/shared/utils/supabase-service/src/index.ts new file mode 100644 index 0000000..715cab2 --- /dev/null +++ b/libs/shared/utils/supabase-service/src/index.ts @@ -0,0 +1,2 @@ +export * from './service'; +export * from './models'; diff --git a/libs/shared/utils/supabase-service/src/models/index.ts b/libs/shared/utils/supabase-service/src/models/index.ts new file mode 100644 index 0000000..7206480 --- /dev/null +++ b/libs/shared/utils/supabase-service/src/models/index.ts @@ -0,0 +1 @@ +export * from './submit-feedback-request'; diff --git a/libs/shared/utils/supabase-service/src/models/submit-feedback-request.ts b/libs/shared/utils/supabase-service/src/models/submit-feedback-request.ts new file mode 100644 index 0000000..0f2e726 --- /dev/null +++ b/libs/shared/utils/supabase-service/src/models/submit-feedback-request.ts @@ -0,0 +1,7 @@ +export interface SubmitFeedbackRequest { + message: string; + platform: string; + appVersion?: string; + apiVersion?: string; + userId?: string; +} diff --git a/libs/shared/utils/supabase-service/src/service.ts b/libs/shared/utils/supabase-service/src/service.ts new file mode 100644 index 0000000..9a93de7 --- /dev/null +++ b/libs/shared/utils/supabase-service/src/service.ts @@ -0,0 +1,35 @@ +import { createClient, SupabaseClient } from '@supabase/supabase-js'; +import Constants from 'expo-constants'; +import { SubmitFeedbackRequest } from './models'; + +class SupabaseService { + private client: SupabaseClient; + + public init(): void { + const supabaseConfig = Constants.expoConfig?.extra?.supabase; + + this.client = createClient(supabaseConfig?.url ?? '', supabaseConfig?.publishableKey ?? '', { + auth: { + persistSession: false, + autoRefreshToken: false, + detectSessionInUrl: false, + }, + }); + } + + public async submit(request: SubmitFeedbackRequest): Promise { + const { error } = await this.client.from('feedback').insert({ + message: request.message, + platform: request.platform, + app_version: request.appVersion, + api_version: request.apiVersion, + user_id: request.userId, + }); + + if (error) { + throw error; + } + } +} + +export const supabaseService = new SupabaseService(); diff --git a/libs/shared/utils/supabase-service/tsconfig.json b/libs/shared/utils/supabase-service/tsconfig.json new file mode 100644 index 0000000..93614c4 --- /dev/null +++ b/libs/shared/utils/supabase-service/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ], + "extends": "../../../../tsconfig.base.json" +} diff --git a/libs/shared/utils/supabase-service/tsconfig.lib.json b/libs/shared/utils/supabase-service/tsconfig.lib.json new file mode 100644 index 0000000..45b2297 --- /dev/null +++ b/libs/shared/utils/supabase-service/tsconfig.lib.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../dist/out-tsc", + "types": ["node", "@nx/react/typings/cssmodule.d.ts", "@nx/react/typings/image.d.ts"] + }, + "exclude": [ + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts", + "src/**/*.spec.tsx", + "src/**/*.test.tsx", + "src/**/*.spec.js", + "src/**/*.test.js", + "src/**/*.spec.jsx", + "src/**/*.test.jsx" + ], + "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] +} diff --git a/types.d.ts b/types.d.ts index 771a040..fab0200 100644 --- a/types.d.ts +++ b/types.d.ts @@ -2,3 +2,4 @@ // Issue: https://github.com/gustavopch/tsc-files/issues/20 // TODO: Uncomment the line below if you have an Expo app import 'node_modules/@nx/expo/typings/svg.d.ts'; +import 'apps/mobile/nativewind-env.d.ts'; From f65e09ddaf19e9026a15c693c3d7416afaf9d384 Mon Sep 17 00:00:00 2001 From: vmikhalevskii Date: Fri, 14 Aug 2026 18:36:25 +0300 Subject: [PATCH 08/17] chore: add base64-arraybuffer --- apps/mobile/package.json | 1 + package-lock.json | 11 +++++++++++ package.json | 1 + 3 files changed, 13 insertions(+) diff --git a/apps/mobile/package.json b/apps/mobile/package.json index ce911ad..e3a267a 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -48,6 +48,7 @@ "axios-observable": "^2.1.0", "babel-plugin-module-resolver": "^5.0.2", "babel-plugin-react-compiler": "^19.0.0-beta-af1b7da-20250417", + "base64-arraybuffer": "^1.0.2", "class-transformer": "^0.5.1", "clsx": "^2.1.1", "dayjs": "^1.11.13", diff --git a/package-lock.json b/package-lock.json index f45b64d..91beaba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "axios-observable": "^2.1.0", "babel-plugin-module-resolver": "^5.0.2", "babel-plugin-react-compiler": "^19.0.0-beta-af1b7da-20250417", + "base64-arraybuffer": "^1.0.2", "class-transformer": "^0.5.1", "clsx": "^2.1.1", "dayjs": "^1.11.13", @@ -197,6 +198,7 @@ "axios-observable": "^2.1.0", "babel-plugin-module-resolver": "^5.0.2", "babel-plugin-react-compiler": "^19.0.0-beta-af1b7da-20250417", + "base64-arraybuffer": "^1.0.2", "class-transformer": "^0.5.1", "clsx": "^2.1.1", "dayjs": "^1.11.13", @@ -13363,6 +13365,15 @@ "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", "license": "MIT" }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", diff --git a/package.json b/package.json index ec1a925..0f0fe57 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "axios-observable": "^2.1.0", "babel-plugin-module-resolver": "^5.0.2", "babel-plugin-react-compiler": "^19.0.0-beta-af1b7da-20250417", + "base64-arraybuffer": "^1.0.2", "class-transformer": "^0.5.1", "clsx": "^2.1.1", "dayjs": "^1.11.13", From 94378505f11ff23b12c36e6f4ea55e9349260085 Mon Sep 17 00:00:00 2001 From: vmikhalevskii Date: Fri, 14 Aug 2026 18:37:44 +0300 Subject: [PATCH 09/17] feat: add feedback attachment logic to supabaseService --- .../utils/supabase-service/src/enums/index.ts | 2 ++ .../src/enums/supabase-bucket.ts | 3 ++ .../src/enums/supabase-table.ts | 3 ++ .../src/models/submit-feedback-request.ts | 7 +++++ .../utils/supabase-service/src/service.ts | 29 +++++++++++++++++-- 5 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 libs/shared/utils/supabase-service/src/enums/index.ts create mode 100644 libs/shared/utils/supabase-service/src/enums/supabase-bucket.ts create mode 100644 libs/shared/utils/supabase-service/src/enums/supabase-table.ts diff --git a/libs/shared/utils/supabase-service/src/enums/index.ts b/libs/shared/utils/supabase-service/src/enums/index.ts new file mode 100644 index 0000000..4961caa --- /dev/null +++ b/libs/shared/utils/supabase-service/src/enums/index.ts @@ -0,0 +1,2 @@ +export * from './supabase-table'; +export * from './supabase-bucket'; diff --git a/libs/shared/utils/supabase-service/src/enums/supabase-bucket.ts b/libs/shared/utils/supabase-service/src/enums/supabase-bucket.ts new file mode 100644 index 0000000..cd379cf --- /dev/null +++ b/libs/shared/utils/supabase-service/src/enums/supabase-bucket.ts @@ -0,0 +1,3 @@ +export enum SupabaseBucket { + FEEDBACK_ATTACHMENTS = 'feedback-attachments', +} diff --git a/libs/shared/utils/supabase-service/src/enums/supabase-table.ts b/libs/shared/utils/supabase-service/src/enums/supabase-table.ts new file mode 100644 index 0000000..e01b423 --- /dev/null +++ b/libs/shared/utils/supabase-service/src/enums/supabase-table.ts @@ -0,0 +1,3 @@ +export enum SupabaseTable { + FEEDBACK = 'feedback', +} diff --git a/libs/shared/utils/supabase-service/src/models/submit-feedback-request.ts b/libs/shared/utils/supabase-service/src/models/submit-feedback-request.ts index 0f2e726..85939c7 100644 --- a/libs/shared/utils/supabase-service/src/models/submit-feedback-request.ts +++ b/libs/shared/utils/supabase-service/src/models/submit-feedback-request.ts @@ -1,7 +1,14 @@ +export interface SubmitFeedbackAttachment { + base64: string; + mimeType?: string; + fileName?: string; +} + export interface SubmitFeedbackRequest { message: string; platform: string; appVersion?: string; apiVersion?: string; userId?: string; + attachments?: Array; } diff --git a/libs/shared/utils/supabase-service/src/service.ts b/libs/shared/utils/supabase-service/src/service.ts index 9a93de7..7077053 100644 --- a/libs/shared/utils/supabase-service/src/service.ts +++ b/libs/shared/utils/supabase-service/src/service.ts @@ -1,6 +1,8 @@ import { createClient, SupabaseClient } from '@supabase/supabase-js'; +import { decode } from 'base64-arraybuffer'; import Constants from 'expo-constants'; -import { SubmitFeedbackRequest } from './models'; +import { SupabaseBucket, SupabaseTable } from './enums'; +import { SubmitFeedbackAttachment, SubmitFeedbackRequest } from './models'; class SupabaseService { private client: SupabaseClient; @@ -18,18 +20,39 @@ class SupabaseService { } public async submit(request: SubmitFeedbackRequest): Promise { - const { error } = await this.client.from('feedback').insert({ + const attachmentPaths = request.attachments?.length + ? await Promise.all(request.attachments.map((attachment, index) => this.uploadAttachment(attachment, index))) + : []; + + const { error } = await this.client.from(SupabaseTable.FEEDBACK).insert({ message: request.message, platform: request.platform, app_version: request.appVersion, api_version: request.apiVersion, - user_id: request.userId, + app_user_id: request.userId, + attachment_paths: attachmentPaths, }); if (error) { throw error; } } + + private async uploadAttachment(attachment: SubmitFeedbackAttachment, index: number): Promise { + const path = `${Date.now()}-${index}-${attachment.fileName ?? 'image.jpg'}`; + + const { error } = await this.client.storage + .from(SupabaseBucket.FEEDBACK_ATTACHMENTS) + .upload(path, decode(attachment.base64), { + contentType: attachment.mimeType, + }); + + if (error) { + throw error; + } + + return path; + } } export const supabaseService = new SupabaseService(); From 9da123586ac269ea0bc5cc7514d445d90c969228 Mon Sep 17 00:00:00 2001 From: vmikhalevskii Date: Fri, 14 Aug 2026 18:39:27 +0300 Subject: [PATCH 10/17] feat: add ContactSupportSheet --- i18n/mobile/profile/en.json | 2 + i18n/mobile/shared/en.json | 9 +- .../features/contact-support-sheet/.babelrc | 12 ++ .../features/contact-support-sheet/README.md | 7 + .../contact-support-sheet/eslint.config.cjs | 12 ++ .../contact-support-sheet/project.json | 9 + .../contact-support-sheet/src/index.ts | 1 + .../src/lib/component.tsx | 156 ++++++++++++++++++ .../components/attachment-row/component.tsx | 28 ++++ .../lib/components/attachment-row/index.ts | 1 + .../src/lib/components/index.ts | 1 + .../src/lib/forms/contact-support-form.ts | 16 ++ .../src/lib/forms/index.ts | 1 + .../contact-support-sheet/src/lib/index.ts | 1 + .../contact-support-sheet/tsconfig.json | 17 ++ .../contact-support-sheet/tsconfig.lib.json | 19 +++ .../profile-menu-sheet/src/lib/component.tsx | 35 +++- .../src/use-attached-files.ts | 1 + .../common/src/types/image-data.ts | 1 + tsconfig.base.json | 4 + 20 files changed, 326 insertions(+), 7 deletions(-) create mode 100644 libs/mobile/shared/features/contact-support-sheet/.babelrc create mode 100644 libs/mobile/shared/features/contact-support-sheet/README.md create mode 100644 libs/mobile/shared/features/contact-support-sheet/eslint.config.cjs create mode 100644 libs/mobile/shared/features/contact-support-sheet/project.json create mode 100644 libs/mobile/shared/features/contact-support-sheet/src/index.ts create mode 100644 libs/mobile/shared/features/contact-support-sheet/src/lib/component.tsx create mode 100644 libs/mobile/shared/features/contact-support-sheet/src/lib/components/attachment-row/component.tsx create mode 100644 libs/mobile/shared/features/contact-support-sheet/src/lib/components/attachment-row/index.ts create mode 100644 libs/mobile/shared/features/contact-support-sheet/src/lib/components/index.ts create mode 100644 libs/mobile/shared/features/contact-support-sheet/src/lib/forms/contact-support-form.ts create mode 100644 libs/mobile/shared/features/contact-support-sheet/src/lib/forms/index.ts create mode 100644 libs/mobile/shared/features/contact-support-sheet/src/lib/index.ts create mode 100644 libs/mobile/shared/features/contact-support-sheet/tsconfig.json create mode 100644 libs/mobile/shared/features/contact-support-sheet/tsconfig.lib.json diff --git a/i18n/mobile/profile/en.json b/i18n/mobile/profile/en.json index c43db1d..5753e8f 100644 --- a/i18n/mobile/profile/en.json +++ b/i18n/mobile/profile/en.json @@ -3,6 +3,8 @@ "PROFILE_MENU_SHEET": { "TEXT_LOGOUT": "Logout", "TEXT_ARCHIVED_CHATS": "Archived Chats", + "TEXT_REPORT_BUG": "Report a bug", + "TEXT_RATE_APP": "Rate app", "TEXT_DELETE_ACCOUNT": "Delete Account", "TEXT_DELETE_ACCOUNT_TITLE": "Are you sure you want to delete your account?", "TEXT_DELETE_ACCOUNT_MESSAGE": "This action will result in permanent data loss if we don't hear from you in the next 30 days.", diff --git a/i18n/mobile/shared/en.json b/i18n/mobile/shared/en.json index 596f871..65397ae 100644 --- a/i18n/mobile/shared/en.json +++ b/i18n/mobile/shared/en.json @@ -5,7 +5,8 @@ "TEXT_REQUIRED_EMAIL": "Please enter your email", "TEXT_REQUIRED_PASSWORD": "Please enter your password", "TEXT_INVALID_URL": "Please enter a valid server URL, e.g. https://your.domain or http://192.168.1.10:3000", - "TEXT_REQUIRED_FOLDER_NAME": "Please enter a folder name" + "TEXT_REQUIRED_FOLDER_NAME": "Please enter a folder name", + "TEXT_REQUIRED_MESSAGE": "Please enter a message" }, "COMMON": { "TEXT_FEATURE_NOT_IMPLEMENTED_YET": "Feature is not implemented yet.", @@ -103,6 +104,12 @@ "SEARCH_INPUT": { "BUTTON_CANCEL": "Cancel", "TEXT_SEARCH_CHATS": "Search chats" + }, + "CONTACT_SUPPORT_SHEET": { + "TEXT_REPORT_BUG": "Report a bug", + "TEXT_YOUR_MESSAGE": "Your message", + "TEXT_MESSAGE_SENT": "Thank you for your feedback! Your message has been sent successfully", + "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Attach screenshots or video" } } } diff --git a/libs/mobile/shared/features/contact-support-sheet/.babelrc b/libs/mobile/shared/features/contact-support-sheet/.babelrc new file mode 100644 index 0000000..1ea870e --- /dev/null +++ b/libs/mobile/shared/features/contact-support-sheet/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/libs/mobile/shared/features/contact-support-sheet/README.md b/libs/mobile/shared/features/contact-support-sheet/README.md new file mode 100644 index 0000000..b4e01f8 --- /dev/null +++ b/libs/mobile/shared/features/contact-support-sheet/README.md @@ -0,0 +1,7 @@ +# mobile/shared/features/contact-support-sheet + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test mobile/shared/features/contact-support-sheet` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/mobile/shared/features/contact-support-sheet/eslint.config.cjs b/libs/mobile/shared/features/contact-support-sheet/eslint.config.cjs new file mode 100644 index 0000000..6f47c1a --- /dev/null +++ b/libs/mobile/shared/features/contact-support-sheet/eslint.config.cjs @@ -0,0 +1,12 @@ +const nx = require('@nx/eslint-plugin'); +const baseConfig = require('../../../../../eslint.config.cjs'); + +module.exports = [ + ...baseConfig, + ...nx.configs['flat/react'], + { + files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], + // Override or add rules here + rules: {}, + }, +]; diff --git a/libs/mobile/shared/features/contact-support-sheet/project.json b/libs/mobile/shared/features/contact-support-sheet/project.json new file mode 100644 index 0000000..7ee0664 --- /dev/null +++ b/libs/mobile/shared/features/contact-support-sheet/project.json @@ -0,0 +1,9 @@ +{ + "name": "mobile/shared/features/contact-support-sheet", + "$schema": "../../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/mobile/shared/features/contact-support-sheet/src", + "projectType": "library", + "tags": ["app:mobile", "scope:shared", "type:features"], + "// targets": "to see all targets run: nx show project mobile/shared/features/contact-support-sheet --web", + "targets": {} +} diff --git a/libs/mobile/shared/features/contact-support-sheet/src/index.ts b/libs/mobile/shared/features/contact-support-sheet/src/index.ts new file mode 100644 index 0000000..f41a696 --- /dev/null +++ b/libs/mobile/shared/features/contact-support-sheet/src/index.ts @@ -0,0 +1 @@ +export * from './lib'; diff --git a/libs/mobile/shared/features/contact-support-sheet/src/lib/component.tsx b/libs/mobile/shared/features/contact-support-sheet/src/lib/component.tsx new file mode 100644 index 0000000..e852c80 --- /dev/null +++ b/libs/mobile/shared/features/contact-support-sheet/src/lib/component.tsx @@ -0,0 +1,156 @@ +import { BottomSheetModal } from '@gorhom/bottom-sheet'; +import { yupResolver } from '@hookform/resolvers/yup'; +import { useSelector } from '@legendapp/state/react'; +import { useTranslation } from '@ronas-it/react-native-common-modules/i18n'; +import Constants from 'expo-constants'; +import { ForwardedRef, ReactElement, useImperativeHandle, useRef } from 'react'; +import { useForm } from 'react-hook-form'; +import { Platform, TextInput } from 'react-native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { + imagePickerService, + ImagePickerSource, +} from '@open-webui-react-native/mobile/shared/data-access/image-picker-service'; +import { useAttachedFiles } from '@open-webui-react-native/mobile/shared/features/use-attached-files'; +import { + AppBottomSheet, + AppBottomSheetKeyboardAwareScrollView, + AppBottomSheetPropsType, + AppButton, + FormFloatedLabelInput, + SheetHeader, + View, +} from '@open-webui-react-native/mobile/shared/ui/ui-kit'; +import { appConfigurationApi, authApi, supabaseApi } from '@open-webui-react-native/shared/data-access/api'; +import { ImageData } from '@open-webui-react-native/shared/data-access/common'; +import { ToastService } from '@open-webui-react-native/shared/utils/toast-service'; +import { AttachmentRow } from './components'; +import { ContactSupportFormSchema } from './forms'; + +export type ContactSupportSheetMethods = { + present: () => void; +}; + +export type ContactSupportSheetRef = ForwardedRef; + +export type ContactSupportSheetProps = Partial> & { + ref?: ContactSupportSheetRef; +}; + +export function ContactSupportSheet({ ref, ...props }: ContactSupportSheetProps): ReactElement { + const translate = useTranslation('SHARED.CONTACT_SUPPORT_SHEET'); + const { bottom } = useSafeAreaInsets(); + + const sheetRef = useRef(null); + const inputRef = useRef(null); + + const { data: profile } = authApi.useGetProfile(); + const { data: configuration } = appConfigurationApi.useGetAppConfiguration(); + const { mutateAsync: submitFeedback, isPending } = supabaseApi.useSubmitFeedback(); + + const { attachedImages, handleImageUploaded, handleDeleteImage, resetAttachments } = useAttachedFiles(); + + const images = useSelector(attachedImages).flatMap((image) => (image ? [image] : [])); + + const { control, handleSubmit, reset } = useForm({ + defaultValues: new ContactSupportFormSchema(), + resolver: yupResolver(ContactSupportFormSchema.validationSchema), + }); + + const closeModal = (): void => sheetRef.current?.close(); + + const openModal = (): void => sheetRef.current?.present(); + + const handleOpen = (): void => inputRef.current?.focus(); + + const handlePickImage = async (): Promise => { + const image = await imagePickerService.getImage(ImagePickerSource.GALLERY); + const asset = image?.assets?.[0]; + + if (!asset || !asset.base64) { + return; + } + + handleImageUploaded({ + uri: asset.uri, + base64: asset.base64, + mimeType: asset.mimeType, + fileName: asset.fileName || undefined, + }); + }; + + const onSubmit = async ({ message }: ContactSupportFormSchema): Promise => { + try { + await submitFeedback({ + message, + platform: Platform.OS, + appVersion: Constants.expoConfig?.version, + apiVersion: configuration?.version, + userId: profile?.id, + attachments: images, + }); + + closeModal(); + reset(new ContactSupportFormSchema()); + resetAttachments(); + ToastService.showSuccess(translate('TEXT_MESSAGE_SENT')); + } catch { + ToastService.showError(); + } + }; + + useImperativeHandle(ref, () => { + return { + present: openModal, + }; + }, []); + + const renderAttachment = (image: ImageData, index: number): ReactElement => ( + handleDeleteImage(image.uri)} /> + ); + + return ( + + + + + + {images.map(renderAttachment)} + + + + + } + /> + ); +} diff --git a/libs/mobile/shared/features/contact-support-sheet/src/lib/components/attachment-row/component.tsx b/libs/mobile/shared/features/contact-support-sheet/src/lib/components/attachment-row/component.tsx new file mode 100644 index 0000000..9bf6ef6 --- /dev/null +++ b/libs/mobile/shared/features/contact-support-sheet/src/lib/components/attachment-row/component.tsx @@ -0,0 +1,28 @@ +import { ReactElement } from 'react'; +import { AppPressable, AppText, Icon, View } from '@open-webui-react-native/mobile/shared/ui/ui-kit'; +import { ImageData } from '@open-webui-react-native/shared/data-access/common'; + +export interface AttachmentRowProps { + attachment: ImageData; + onRemove: () => void; +} + +export function AttachmentRow({ attachment, onRemove }: AttachmentRowProps): ReactElement { + const fileName = attachment.fileName ?? attachment.uri.split('/').pop(); + + return ( + + + + + {fileName} + + + + + + + ); +} diff --git a/libs/mobile/shared/features/contact-support-sheet/src/lib/components/attachment-row/index.ts b/libs/mobile/shared/features/contact-support-sheet/src/lib/components/attachment-row/index.ts new file mode 100644 index 0000000..bb82484 --- /dev/null +++ b/libs/mobile/shared/features/contact-support-sheet/src/lib/components/attachment-row/index.ts @@ -0,0 +1 @@ +export * from './component'; diff --git a/libs/mobile/shared/features/contact-support-sheet/src/lib/components/index.ts b/libs/mobile/shared/features/contact-support-sheet/src/lib/components/index.ts new file mode 100644 index 0000000..e6f8740 --- /dev/null +++ b/libs/mobile/shared/features/contact-support-sheet/src/lib/components/index.ts @@ -0,0 +1 @@ +export * from './attachment-row'; diff --git a/libs/mobile/shared/features/contact-support-sheet/src/lib/forms/contact-support-form.ts b/libs/mobile/shared/features/contact-support-sheet/src/lib/forms/contact-support-form.ts new file mode 100644 index 0000000..90589db --- /dev/null +++ b/libs/mobile/shared/features/contact-support-sheet/src/lib/forms/contact-support-form.ts @@ -0,0 +1,16 @@ +import { i18n } from '@ronas-it/react-native-common-modules/i18n'; +import * as Yup from 'yup'; + +export class ContactSupportFormSchema { + public message: string; + + constructor(schema?: Partial) { + this.message = schema?.message || ''; + } + + public static get validationSchema(): Yup.ObjectSchema { + return Yup.object().shape({ + message: Yup.string().required(i18n.t('SHARED.VALIDATION.TEXT_REQUIRED_MESSAGE')), + }); + } +} diff --git a/libs/mobile/shared/features/contact-support-sheet/src/lib/forms/index.ts b/libs/mobile/shared/features/contact-support-sheet/src/lib/forms/index.ts new file mode 100644 index 0000000..fc11537 --- /dev/null +++ b/libs/mobile/shared/features/contact-support-sheet/src/lib/forms/index.ts @@ -0,0 +1 @@ +export * from './contact-support-form'; diff --git a/libs/mobile/shared/features/contact-support-sheet/src/lib/index.ts b/libs/mobile/shared/features/contact-support-sheet/src/lib/index.ts new file mode 100644 index 0000000..bb82484 --- /dev/null +++ b/libs/mobile/shared/features/contact-support-sheet/src/lib/index.ts @@ -0,0 +1 @@ +export * from './component'; diff --git a/libs/mobile/shared/features/contact-support-sheet/tsconfig.json b/libs/mobile/shared/features/contact-support-sheet/tsconfig.json new file mode 100644 index 0000000..ec74bfc --- /dev/null +++ b/libs/mobile/shared/features/contact-support-sheet/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ], + "extends": "../../../../../tsconfig.base.json" +} diff --git a/libs/mobile/shared/features/contact-support-sheet/tsconfig.lib.json b/libs/mobile/shared/features/contact-support-sheet/tsconfig.lib.json new file mode 100644 index 0000000..27f2b91 --- /dev/null +++ b/libs/mobile/shared/features/contact-support-sheet/tsconfig.lib.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../../dist/out-tsc", + "types": ["node", "@nx/react/typings/cssmodule.d.ts", "@nx/react/typings/image.d.ts"] + }, + "exclude": [ + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts", + "src/**/*.spec.tsx", + "src/**/*.test.tsx", + "src/**/*.spec.js", + "src/**/*.test.js", + "src/**/*.spec.jsx", + "src/**/*.test.jsx" + ], + "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] +} diff --git a/libs/mobile/shared/features/profile-menu-sheet/src/lib/component.tsx b/libs/mobile/shared/features/profile-menu-sheet/src/lib/component.tsx index 2cfdd78..7df1c6a 100644 --- a/libs/mobile/shared/features/profile-menu-sheet/src/lib/component.tsx +++ b/libs/mobile/shared/features/profile-menu-sheet/src/lib/component.tsx @@ -1,7 +1,11 @@ import { BottomSheetModal } from '@gorhom/bottom-sheet'; import { useTranslation } from '@ronas-it/react-native-common-modules/i18n'; -import { ReactElement, useRef } from 'react'; +import { Fragment, ReactElement, useRef } from 'react'; import { Alert } from 'react-native'; +import { + ContactSupportSheet, + ContactSupportSheetMethods, +} from '@open-webui-react-native/mobile/shared/features/contact-support-sheet'; import { useLogout } from '@open-webui-react-native/mobile/shared/features/use-logout'; import { Avatar, @@ -12,6 +16,7 @@ import { } from '@open-webui-react-native/mobile/shared/ui/ui-kit'; import { authApi } from '@open-webui-react-native/shared/data-access/api'; import { FeatureID, isFeatureEnabled } from '@open-webui-react-native/shared/utils/feature-flag'; +import { storeReviewService } from '@open-webui-react-native/shared/utils/store-review-service'; import { ToastService } from '@open-webui-react-native/shared/utils/toast-service'; interface ProfileMenuSheetProps extends Pick { @@ -25,6 +30,7 @@ export function ProfileMenuSheet({ onArchivedChatsPress, ...restProps }: Profile const { data: profile } = authApi.useGetProfile(); const actionsBottomSheetRef = useRef(null); + const contactSupportSheetRef = useRef(null); const closeActionsSheet = (): Promise => new Promise((resolve) => { @@ -37,6 +43,10 @@ export function ProfileMenuSheet({ onArchivedChatsPress, ...restProps }: Profile onArchivedChatsPress(); }; + const handleRateAppPress = (): void => { + storeReviewService.openStoreReview(); + }; + const handleDeleteAccountPress = (): void => { ToastService.show(translate('TEXT_ACCOUNT_DELETION_REQUEST')); }; @@ -64,6 +74,16 @@ export function ProfileMenuSheet({ onArchivedChatsPress, ...restProps }: Profile ? handleArchivedChatsPress : ToastService.showFeatureNotImplemented, }, + { + title: translate('TEXT_REPORT_BUG'), + iconName: 'message', + onPress: () => contactSupportSheetRef.current?.present(), + }, + { + title: translate('TEXT_RATE_APP'), + iconName: 'star', + onPress: handleRateAppPress, + }, { title: translate('TEXT_DELETE_ACCOUNT'), iconName: 'trashCan', @@ -87,10 +107,13 @@ export function ProfileMenuSheet({ onArchivedChatsPress, ...restProps }: Profile ); return ( - + + + + ); } diff --git a/libs/mobile/shared/features/use-attached-files/src/use-attached-files.ts b/libs/mobile/shared/features/use-attached-files/src/use-attached-files.ts index 3edd323..e5641c5 100644 --- a/libs/mobile/shared/features/use-attached-files/src/use-attached-files.ts +++ b/libs/mobile/shared/features/use-attached-files/src/use-attached-files.ts @@ -25,6 +25,7 @@ export function useAttachedFiles(): typeof result { mimeType: ImageMimeType.JPEG, uri: compressed, base64: await fileSystemService.convertToBase64(compressed), + fileName: image.fileName, }; } diff --git a/libs/shared/data-access/common/src/types/image-data.ts b/libs/shared/data-access/common/src/types/image-data.ts index cb83032..12010d4 100644 --- a/libs/shared/data-access/common/src/types/image-data.ts +++ b/libs/shared/data-access/common/src/types/image-data.ts @@ -2,4 +2,5 @@ export type ImageData = { uri: string; base64: string; mimeType?: string; + fileName?: string; }; diff --git a/tsconfig.base.json b/tsconfig.base.json index 93ed2cb..1d204dc 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -175,6 +175,9 @@ "@open-webui-react-native/mobile/shared/features/chat-actions-menu-sheet": [ "libs/mobile/shared/features/chat-actions-menu-sheet/src/index.ts" ], + "@open-webui-react-native/mobile/shared/features/contact-support-sheet": [ + "libs/mobile/shared/features/contact-support-sheet/src/index.ts" + ], "@open-webui-react-native/mobile/shared/features/download-chat": [ "libs/mobile/shared/features/download-chat/src/index.ts" ], @@ -273,6 +276,7 @@ "libs/shared/utils/store-review-service/src/index.ts" ], "@open-webui-react-native/shared/utils/strings": ["libs/shared/utils/strings/src/index.ts"], + "@open-webui-react-native/shared/utils/supabase-service": ["libs/shared/utils/supabase-service/src/index.ts"], "@open-webui-react-native/shared/utils/toast-service": ["libs/shared/utils/toast-service/src/index.ts"], "@open-webui-react-native/shared/utils/use-app-state-change": [ "libs/shared/utils/use-app-state-change/src/index.ts" From 3fca311bd606efd043e0095a894aa6e8c0e660a9 Mon Sep 17 00:00:00 2001 From: vmikhalevskii Date: Thu, 20 Aug 2026 12:51:59 +0300 Subject: [PATCH 11/17] feat: add video support for feedback functionality --- .../image-picker-service/src/enums/source.ts | 1 + .../image-picker-service/src/service.ts | 24 ++++++++++--------- .../src/lib/component.tsx | 6 ++--- .../src/use-attached-files.ts | 6 +++++ .../utils/supabase-service/src/service.ts | 3 ++- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/libs/mobile/shared/data-access/image-picker-service/src/enums/source.ts b/libs/mobile/shared/data-access/image-picker-service/src/enums/source.ts index c4b1451..cb34c90 100644 --- a/libs/mobile/shared/data-access/image-picker-service/src/enums/source.ts +++ b/libs/mobile/shared/data-access/image-picker-service/src/enums/source.ts @@ -1,4 +1,5 @@ export enum ImagePickerSource { CAMERA = 'camera', GALLERY = 'gallery', + GALLERY_WITH_VIDEO = 'gallery_with_video', } diff --git a/libs/mobile/shared/data-access/image-picker-service/src/service.ts b/libs/mobile/shared/data-access/image-picker-service/src/service.ts index 2ef806d..9f94992 100644 --- a/libs/mobile/shared/data-access/image-picker-service/src/service.ts +++ b/libs/mobile/shared/data-access/image-picker-service/src/service.ts @@ -5,23 +5,25 @@ import { ToastService } from '@open-webui-react-native/shared/utils/toast-servic import { ImagePickerSource } from './enums'; export class ImagePickerService { - public defaultOptions: ImagePicker.ImagePickerOptions = { - mediaTypes: 'images', - allowsEditing: false, - base64: true, - quality: 0.2, - }; - public getImage(source: ImagePickerSource): Promise { return this.pickImage(source); } + private getDefaultOptions(withVideo = false): ImagePicker.ImagePickerOptions { + return { + mediaTypes: withVideo ? ['images', 'videos'] : 'images', + allowsEditing: false, + base64: true, + quality: 0.2, + }; + } + private requestGalleryAccess(): Promise { return ImagePicker.requestMediaLibraryPermissionsAsync(); } - private launchGallery(): Promise { - return ImagePicker.launchImageLibraryAsync(this.defaultOptions); + private launchGallery(withVideos = false): Promise { + return ImagePicker.launchImageLibraryAsync(this.getDefaultOptions(withVideos)); } private requestCameraAccess(): Promise { @@ -29,7 +31,7 @@ export class ImagePickerService { } private launchCamera(): Promise { - return ImagePicker.launchCameraAsync(this.defaultOptions); + return ImagePicker.launchCameraAsync(this.getDefaultOptions()); } private async pickImage(source: ImagePickerSource): Promise { @@ -43,7 +45,7 @@ export class ImagePickerService { return null; } - return isCamera ? this.launchCamera() : this.launchGallery(); + return isCamera ? this.launchCamera() : this.launchGallery(source === ImagePickerSource.GALLERY_WITH_VIDEO); } } diff --git a/libs/mobile/shared/features/contact-support-sheet/src/lib/component.tsx b/libs/mobile/shared/features/contact-support-sheet/src/lib/component.tsx index e852c80..945e702 100644 --- a/libs/mobile/shared/features/contact-support-sheet/src/lib/component.tsx +++ b/libs/mobile/shared/features/contact-support-sheet/src/lib/component.tsx @@ -64,16 +64,16 @@ export function ContactSupportSheet({ ref, ...props }: ContactSupportSheetProps) const handleOpen = (): void => inputRef.current?.focus(); const handlePickImage = async (): Promise => { - const image = await imagePickerService.getImage(ImagePickerSource.GALLERY); + const image = await imagePickerService.getImage(ImagePickerSource.GALLERY_WITH_VIDEO); const asset = image?.assets?.[0]; - if (!asset || !asset.base64) { + if (!asset) { return; } handleImageUploaded({ uri: asset.uri, - base64: asset.base64, + base64: asset.base64 ?? '', mimeType: asset.mimeType, fileName: asset.fileName || undefined, }); diff --git a/libs/mobile/shared/features/use-attached-files/src/use-attached-files.ts b/libs/mobile/shared/features/use-attached-files/src/use-attached-files.ts index e5641c5..d9f5217 100644 --- a/libs/mobile/shared/features/use-attached-files/src/use-attached-files.ts +++ b/libs/mobile/shared/features/use-attached-files/src/use-attached-files.ts @@ -27,6 +27,12 @@ export function useAttachedFiles(): typeof result { base64: await fileSystemService.convertToBase64(compressed), fileName: image.fileName, }; + } else if (!image.base64) { + // NOTE: expo-image-picker only base64-encodes images, not videos, so videos need to be read from disk + processed = { + ...image, + base64: await fileSystemService.convertToBase64(image.uri), + }; } attachedImages.set((prev) => [...prev, processed]); diff --git a/libs/shared/utils/supabase-service/src/service.ts b/libs/shared/utils/supabase-service/src/service.ts index 7077053..436b22a 100644 --- a/libs/shared/utils/supabase-service/src/service.ts +++ b/libs/shared/utils/supabase-service/src/service.ts @@ -39,7 +39,8 @@ class SupabaseService { } private async uploadAttachment(attachment: SubmitFeedbackAttachment, index: number): Promise { - const path = `${Date.now()}-${index}-${attachment.fileName ?? 'image.jpg'}`; + const fallbackFileName = attachment.mimeType?.startsWith('video/') ? 'video.mp4' : 'image.jpg'; + const path = `${Date.now()}-${index}-${attachment.fileName ?? fallbackFileName}`; const { error } = await this.client.storage .from(SupabaseBucket.FEEDBACK_ATTACHMENTS) From cdd12b8f762a507929f50e3af94921768dbab31d Mon Sep 17 00:00:00 2001 From: vmikhalevskii Date: Fri, 21 Aug 2026 10:09:28 +0300 Subject: [PATCH 12/17] fix: correct icons --- .../shared/ui/ui-kit/src/assets/icons/message.svg | 10 ---------- libs/mobile/shared/ui/ui-kit/src/assets/icons/star.svg | 3 +++ 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/libs/mobile/shared/ui/ui-kit/src/assets/icons/message.svg b/libs/mobile/shared/ui/ui-kit/src/assets/icons/message.svg index 8a9d1fe..4d23451 100644 --- a/libs/mobile/shared/ui/ui-kit/src/assets/icons/message.svg +++ b/libs/mobile/shared/ui/ui-kit/src/assets/icons/message.svg @@ -1,14 +1,4 @@ -<<<<<<< HEAD -======= - - - ->>>>>>> development diff --git a/libs/mobile/shared/ui/ui-kit/src/assets/icons/star.svg b/libs/mobile/shared/ui/ui-kit/src/assets/icons/star.svg index e69de29..453380f 100644 --- a/libs/mobile/shared/ui/ui-kit/src/assets/icons/star.svg +++ b/libs/mobile/shared/ui/ui-kit/src/assets/icons/star.svg @@ -0,0 +1,3 @@ + + + From ee6d3d726b56c8ce9d3f0e5443930f1d8ddfe733 Mon Sep 17 00:00:00 2001 From: vmikhalevskii Date: Fri, 21 Aug 2026 10:11:15 +0300 Subject: [PATCH 13/17] feat: move ContactSupportSheet to settings scope --- .../features/contact-support-sheet/.babelrc | 0 .../features/contact-support-sheet/README.md | 7 ++ .../contact-support-sheet/eslint.config.cjs | 0 .../contact-support-sheet/project.json | 9 ++ .../contact-support-sheet/src/index.ts | 0 .../src/lib/component.tsx | 2 +- .../components/attachment-row/component.tsx | 0 .../lib/components/attachment-row}/index.ts | 0 .../src/lib/components/index.ts | 0 .../src/lib/forms/contact-support-form.ts | 0 .../src/lib/forms/index.ts | 0 .../contact-support-sheet/src/lib}/index.ts | 0 .../contact-support-sheet/tsconfig.json | 0 .../contact-support-sheet/tsconfig.lib.json | 0 .../contact-support-sheet/component.tsx | 116 ------------------ .../settings/src/lib/components/index.ts | 1 - .../features/contact-support-sheet/README.md | 7 -- .../contact-support-sheet/project.json | 9 -- .../contact-support-sheet/src/lib/index.ts | 1 - tsconfig.base.json | 6 +- 20 files changed, 20 insertions(+), 138 deletions(-) rename libs/mobile/{shared => settings}/features/contact-support-sheet/.babelrc (100%) create mode 100644 libs/mobile/settings/features/contact-support-sheet/README.md rename libs/mobile/{shared => settings}/features/contact-support-sheet/eslint.config.cjs (100%) create mode 100644 libs/mobile/settings/features/contact-support-sheet/project.json rename libs/mobile/{shared => settings}/features/contact-support-sheet/src/index.ts (100%) rename libs/mobile/{shared => settings}/features/contact-support-sheet/src/lib/component.tsx (98%) rename libs/mobile/{shared => settings}/features/contact-support-sheet/src/lib/components/attachment-row/component.tsx (100%) rename libs/mobile/settings/features/{settings/src/lib/components/contact-support-sheet => contact-support-sheet/src/lib/components/attachment-row}/index.ts (100%) rename libs/mobile/{shared => settings}/features/contact-support-sheet/src/lib/components/index.ts (100%) rename libs/mobile/{shared => settings}/features/contact-support-sheet/src/lib/forms/contact-support-form.ts (100%) rename libs/mobile/{shared => settings}/features/contact-support-sheet/src/lib/forms/index.ts (100%) rename libs/mobile/{shared/features/contact-support-sheet/src/lib/components/attachment-row => settings/features/contact-support-sheet/src/lib}/index.ts (100%) rename libs/mobile/{shared => settings}/features/contact-support-sheet/tsconfig.json (100%) rename libs/mobile/{shared => settings}/features/contact-support-sheet/tsconfig.lib.json (100%) delete mode 100644 libs/mobile/settings/features/settings/src/lib/components/contact-support-sheet/component.tsx delete mode 100644 libs/mobile/shared/features/contact-support-sheet/README.md delete mode 100644 libs/mobile/shared/features/contact-support-sheet/project.json delete mode 100644 libs/mobile/shared/features/contact-support-sheet/src/lib/index.ts diff --git a/libs/mobile/shared/features/contact-support-sheet/.babelrc b/libs/mobile/settings/features/contact-support-sheet/.babelrc similarity index 100% rename from libs/mobile/shared/features/contact-support-sheet/.babelrc rename to libs/mobile/settings/features/contact-support-sheet/.babelrc diff --git a/libs/mobile/settings/features/contact-support-sheet/README.md b/libs/mobile/settings/features/contact-support-sheet/README.md new file mode 100644 index 0000000..5da6ab0 --- /dev/null +++ b/libs/mobile/settings/features/contact-support-sheet/README.md @@ -0,0 +1,7 @@ +# mobile-settings-features-contact-support-sheet + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test mobile-settings-features-contact-support-sheet` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/mobile/shared/features/contact-support-sheet/eslint.config.cjs b/libs/mobile/settings/features/contact-support-sheet/eslint.config.cjs similarity index 100% rename from libs/mobile/shared/features/contact-support-sheet/eslint.config.cjs rename to libs/mobile/settings/features/contact-support-sheet/eslint.config.cjs diff --git a/libs/mobile/settings/features/contact-support-sheet/project.json b/libs/mobile/settings/features/contact-support-sheet/project.json new file mode 100644 index 0000000..b306d75 --- /dev/null +++ b/libs/mobile/settings/features/contact-support-sheet/project.json @@ -0,0 +1,9 @@ +{ + "name": "mobile-settings-features-contact-support-sheet", + "$schema": "../../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/mobile/settings/features/contact-support-sheet/src", + "projectType": "library", + "tags": ["app:mobile", "type:features", "scope:settings"], + "// targets": "to see all targets run: nx show project mobile-settings-features-contact-support-sheet --web", + "targets": {} +} diff --git a/libs/mobile/shared/features/contact-support-sheet/src/index.ts b/libs/mobile/settings/features/contact-support-sheet/src/index.ts similarity index 100% rename from libs/mobile/shared/features/contact-support-sheet/src/index.ts rename to libs/mobile/settings/features/contact-support-sheet/src/index.ts diff --git a/libs/mobile/shared/features/contact-support-sheet/src/lib/component.tsx b/libs/mobile/settings/features/contact-support-sheet/src/lib/component.tsx similarity index 98% rename from libs/mobile/shared/features/contact-support-sheet/src/lib/component.tsx rename to libs/mobile/settings/features/contact-support-sheet/src/lib/component.tsx index 945e702..40c61ae 100644 --- a/libs/mobile/shared/features/contact-support-sheet/src/lib/component.tsx +++ b/libs/mobile/settings/features/contact-support-sheet/src/lib/component.tsx @@ -38,7 +38,7 @@ export type ContactSupportSheetProps = Partial(null); diff --git a/libs/mobile/shared/features/contact-support-sheet/src/lib/components/attachment-row/component.tsx b/libs/mobile/settings/features/contact-support-sheet/src/lib/components/attachment-row/component.tsx similarity index 100% rename from libs/mobile/shared/features/contact-support-sheet/src/lib/components/attachment-row/component.tsx rename to libs/mobile/settings/features/contact-support-sheet/src/lib/components/attachment-row/component.tsx diff --git a/libs/mobile/settings/features/settings/src/lib/components/contact-support-sheet/index.ts b/libs/mobile/settings/features/contact-support-sheet/src/lib/components/attachment-row/index.ts similarity index 100% rename from libs/mobile/settings/features/settings/src/lib/components/contact-support-sheet/index.ts rename to libs/mobile/settings/features/contact-support-sheet/src/lib/components/attachment-row/index.ts diff --git a/libs/mobile/shared/features/contact-support-sheet/src/lib/components/index.ts b/libs/mobile/settings/features/contact-support-sheet/src/lib/components/index.ts similarity index 100% rename from libs/mobile/shared/features/contact-support-sheet/src/lib/components/index.ts rename to libs/mobile/settings/features/contact-support-sheet/src/lib/components/index.ts diff --git a/libs/mobile/shared/features/contact-support-sheet/src/lib/forms/contact-support-form.ts b/libs/mobile/settings/features/contact-support-sheet/src/lib/forms/contact-support-form.ts similarity index 100% rename from libs/mobile/shared/features/contact-support-sheet/src/lib/forms/contact-support-form.ts rename to libs/mobile/settings/features/contact-support-sheet/src/lib/forms/contact-support-form.ts diff --git a/libs/mobile/shared/features/contact-support-sheet/src/lib/forms/index.ts b/libs/mobile/settings/features/contact-support-sheet/src/lib/forms/index.ts similarity index 100% rename from libs/mobile/shared/features/contact-support-sheet/src/lib/forms/index.ts rename to libs/mobile/settings/features/contact-support-sheet/src/lib/forms/index.ts diff --git a/libs/mobile/shared/features/contact-support-sheet/src/lib/components/attachment-row/index.ts b/libs/mobile/settings/features/contact-support-sheet/src/lib/index.ts similarity index 100% rename from libs/mobile/shared/features/contact-support-sheet/src/lib/components/attachment-row/index.ts rename to libs/mobile/settings/features/contact-support-sheet/src/lib/index.ts diff --git a/libs/mobile/shared/features/contact-support-sheet/tsconfig.json b/libs/mobile/settings/features/contact-support-sheet/tsconfig.json similarity index 100% rename from libs/mobile/shared/features/contact-support-sheet/tsconfig.json rename to libs/mobile/settings/features/contact-support-sheet/tsconfig.json diff --git a/libs/mobile/shared/features/contact-support-sheet/tsconfig.lib.json b/libs/mobile/settings/features/contact-support-sheet/tsconfig.lib.json similarity index 100% rename from libs/mobile/shared/features/contact-support-sheet/tsconfig.lib.json rename to libs/mobile/settings/features/contact-support-sheet/tsconfig.lib.json diff --git a/libs/mobile/settings/features/settings/src/lib/components/contact-support-sheet/component.tsx b/libs/mobile/settings/features/settings/src/lib/components/contact-support-sheet/component.tsx deleted file mode 100644 index 1a338f4..0000000 --- a/libs/mobile/settings/features/settings/src/lib/components/contact-support-sheet/component.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import { BottomSheetModal, BottomSheetTextInput } from '@gorhom/bottom-sheet'; -import { useTranslation } from '@ronas-it/react-native-common-modules/i18n'; -import { ForwardedRef, ReactElement, useImperativeHandle, useRef, useState } from 'react'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { - imagePickerService, - ImagePickerSource, -} from '@open-webui-react-native/mobile/shared/data-access/image-picker-service'; -import { colors } from '@open-webui-react-native/mobile/shared/ui/styles'; -import { - AppBottomSheet, - AppBottomSheetPropsType, - AppButton, - AppText, - Icon, - IconButton, - SheetHeader, - View, -} from '@open-webui-react-native/mobile/shared/ui/ui-kit'; -import { SupportAttachment } from '../../types'; - -export type ContactSupportSheetMethods = { - present: () => void; -}; - -export type ContactSupportSheetRef = ForwardedRef; - -export type ContactSupportSheetProps = Partial> & { - ref?: ContactSupportSheetRef; -}; - -export function ContactSupportSheet({ ref, ...props }: ContactSupportSheetProps): ReactElement { - const translate = useTranslation('APP.SETTINGS_SCREEN.CONTACT_SUPPORT_SHEET'); - const { bottom } = useSafeAreaInsets(); - const sheetRef = useRef(null); - - const [message, setMessage] = useState(''); - const [attachments, setAttachments] = useState>([]); - - const closeSheet = (): void => sheetRef.current?.close(); - - useImperativeHandle(ref, () => ({ present: () => sheetRef.current?.present() }), []); - - const handleAttachPress = async (): Promise => { - const media = await imagePickerService.getImage(ImagePickerSource.GALLERY); - const asset = media?.assets?.[0]; - - if (!asset) { - return; - } - - setAttachments((currentAttachments) => [ - ...currentAttachments, - { uri: asset.uri, name: asset.fileName || asset.uri.split('/').pop() || '' }, - ]); - }; - - const handleRemoveAttachmentPress = (uri: string): void => - setAttachments((currentAttachments) => currentAttachments.filter((attachment) => attachment.uri !== uri)); - - const renderAttachment = ({ uri, name }: SupportAttachment): ReactElement => ( - - - - {name} - - handleRemoveAttachmentPress(uri)} - /> - - ); - - return ( - - - - - {attachments.map(renderAttachment)} - - - - } - /> - ); -} diff --git a/libs/mobile/settings/features/settings/src/lib/components/index.ts b/libs/mobile/settings/features/settings/src/lib/components/index.ts index f49e405..091310f 100644 --- a/libs/mobile/settings/features/settings/src/lib/components/index.ts +++ b/libs/mobile/settings/features/settings/src/lib/components/index.ts @@ -1,5 +1,4 @@ export * from './change-password-sheet'; -export * from './contact-support-sheet'; export * from './default-model-sheet'; export * from './profile-avatar-sheet'; export * from './profile-name-sheet'; diff --git a/libs/mobile/shared/features/contact-support-sheet/README.md b/libs/mobile/shared/features/contact-support-sheet/README.md deleted file mode 100644 index b4e01f8..0000000 --- a/libs/mobile/shared/features/contact-support-sheet/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# mobile/shared/features/contact-support-sheet - -This library was generated with [Nx](https://nx.dev). - -## Running unit tests - -Run `nx test mobile/shared/features/contact-support-sheet` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/mobile/shared/features/contact-support-sheet/project.json b/libs/mobile/shared/features/contact-support-sheet/project.json deleted file mode 100644 index 7ee0664..0000000 --- a/libs/mobile/shared/features/contact-support-sheet/project.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "mobile/shared/features/contact-support-sheet", - "$schema": "../../../../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "libs/mobile/shared/features/contact-support-sheet/src", - "projectType": "library", - "tags": ["app:mobile", "scope:shared", "type:features"], - "// targets": "to see all targets run: nx show project mobile/shared/features/contact-support-sheet --web", - "targets": {} -} diff --git a/libs/mobile/shared/features/contact-support-sheet/src/lib/index.ts b/libs/mobile/shared/features/contact-support-sheet/src/lib/index.ts deleted file mode 100644 index bb82484..0000000 --- a/libs/mobile/shared/features/contact-support-sheet/src/lib/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './component'; diff --git a/tsconfig.base.json b/tsconfig.base.json index 46c3c24..d31bc0a 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -152,6 +152,9 @@ "@open-webui-react-native/mobile/profile/features/menu-sheet": [ "libs/mobile/profile/features/menu-sheet/src/index.ts" ], + "@open-webui-react-native/mobile/settings/features/contact-support-sheet": [ + "libs/mobile/settings/features/contact-support-sheet/src/index.ts" + ], "@open-webui-react-native/mobile/settings/features/settings": [ "libs/mobile/settings/features/settings/src/index.ts" ], @@ -180,9 +183,6 @@ "@open-webui-react-native/mobile/shared/features/chat-actions-menu-sheet": [ "libs/mobile/shared/features/chat-actions-menu-sheet/src/index.ts" ], - "@open-webui-react-native/mobile/shared/features/contact-support-sheet": [ - "libs/mobile/shared/features/contact-support-sheet/src/index.ts" - ], "@open-webui-react-native/mobile/shared/features/download-chat": [ "libs/mobile/shared/features/download-chat/src/index.ts" ], From 8bb79f7c8c0e42f7c3282931f4786345ff8aef29 Mon Sep 17 00:00:00 2001 From: vmikhalevskii Date: Fri, 21 Aug 2026 10:15:41 +0300 Subject: [PATCH 14/17] feat: add locales --- i18n/mobile/app/de.json | 7 ++++--- i18n/mobile/app/en.json | 7 ++++--- i18n/mobile/app/es.json | 7 ++++--- i18n/mobile/app/fr.json | 7 ++++--- i18n/mobile/app/ja.json | 7 ++++--- i18n/mobile/app/pt.json | 7 ++++--- i18n/mobile/app/ru.json | 7 ++++--- i18n/mobile/app/zh.json | 7 ++++--- i18n/mobile/profile/en.json | 2 -- i18n/mobile/shared/de.json | 1 + i18n/mobile/shared/en.json | 6 ------ i18n/mobile/shared/es.json | 1 + i18n/mobile/shared/fr.json | 1 + i18n/mobile/shared/ja.json | 1 + i18n/mobile/shared/pt.json | 1 + i18n/mobile/shared/ru.json | 1 + i18n/mobile/shared/zh.json | 1 + 17 files changed, 39 insertions(+), 32 deletions(-) diff --git a/i18n/mobile/app/de.json b/i18n/mobile/app/de.json index f0a0906..786161c 100644 --- a/i18n/mobile/app/de.json +++ b/i18n/mobile/app/de.json @@ -55,9 +55,10 @@ "TEXT_NO_MODELS": "Es gibt keine Modelle." }, "CONTACT_SUPPORT_SHEET": { - "TEXT_TITLE": "Support kontaktieren", - "TEXT_MESSAGE_PLACEHOLDER": "Ihre Nachricht", - "BUTTON_ATTACH": "Screenshots oder Video anhängen" + "TEXT_REPORT_BUG": "Fehler melden", + "TEXT_YOUR_MESSAGE": "Ihre Nachricht", + "TEXT_MESSAGE_SENT": "Vielen Dank für Ihr Feedback! Ihre Nachricht wurde erfolgreich gesendet", + "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Screenshots oder Video anhängen" }, "LANGUAGE_SHEET": { "TEXT_TITLE": "Sprache" diff --git a/i18n/mobile/app/en.json b/i18n/mobile/app/en.json index 3b51335..49cc490 100644 --- a/i18n/mobile/app/en.json +++ b/i18n/mobile/app/en.json @@ -55,9 +55,10 @@ "TEXT_NO_MODELS": "There are no models." }, "CONTACT_SUPPORT_SHEET": { - "TEXT_TITLE": "Contact support", - "TEXT_MESSAGE_PLACEHOLDER": "Your message", - "BUTTON_ATTACH": "Attach screenshots or video" + "TEXT_REPORT_BUG": "Report a bug", + "TEXT_YOUR_MESSAGE": "Your message", + "TEXT_MESSAGE_SENT": "Thank you for your feedback! Your message has been sent successfully", + "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Attach screenshots or video" }, "LANGUAGE_SHEET": { "TEXT_TITLE": "Language" diff --git a/i18n/mobile/app/es.json b/i18n/mobile/app/es.json index 97de869..f0c6d43 100644 --- a/i18n/mobile/app/es.json +++ b/i18n/mobile/app/es.json @@ -55,9 +55,10 @@ "TEXT_NO_MODELS": "No hay modelos." }, "CONTACT_SUPPORT_SHEET": { - "TEXT_TITLE": "Contactar con soporte", - "TEXT_MESSAGE_PLACEHOLDER": "Tu mensaje", - "BUTTON_ATTACH": "Adjuntar capturas de pantalla o vídeo" + "TEXT_REPORT_BUG": "Informar un error", + "TEXT_YOUR_MESSAGE": "Tu mensaje", + "TEXT_MESSAGE_SENT": "¡Gracias por tus comentarios! Tu mensaje se ha enviado correctamente", + "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Adjuntar capturas de pantalla o video" }, "LANGUAGE_SHEET": { "TEXT_TITLE": "Idioma" diff --git a/i18n/mobile/app/fr.json b/i18n/mobile/app/fr.json index 0d1ca58..2891fa9 100644 --- a/i18n/mobile/app/fr.json +++ b/i18n/mobile/app/fr.json @@ -55,9 +55,10 @@ "TEXT_NO_MODELS": "Il n'y a aucun modèle." }, "CONTACT_SUPPORT_SHEET": { - "TEXT_TITLE": "Contacter le support", - "TEXT_MESSAGE_PLACEHOLDER": "Votre message", - "BUTTON_ATTACH": "Joindre des captures d'écran ou une vidéo" + "TEXT_REPORT_BUG": "Signaler un bug", + "TEXT_YOUR_MESSAGE": "Votre message", + "TEXT_MESSAGE_SENT": "Merci pour votre retour ! Votre message a été envoyé avec succès", + "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Joindre des captures d'écran ou une vidéo" }, "LANGUAGE_SHEET": { "TEXT_TITLE": "Langue" diff --git a/i18n/mobile/app/ja.json b/i18n/mobile/app/ja.json index f59a6c0..f5ba81d 100644 --- a/i18n/mobile/app/ja.json +++ b/i18n/mobile/app/ja.json @@ -55,9 +55,10 @@ "TEXT_NO_MODELS": "モデルがありません。" }, "CONTACT_SUPPORT_SHEET": { - "TEXT_TITLE": "サポートに問い合わせる", - "TEXT_MESSAGE_PLACEHOLDER": "メッセージ", - "BUTTON_ATTACH": "スクリーンショットまたは動画を添付" + "TEXT_REPORT_BUG": "不具合を報告", + "TEXT_YOUR_MESSAGE": "メッセージ", + "TEXT_MESSAGE_SENT": "フィードバックをありがとうございます!メッセージが正常に送信されました", + "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "スクリーンショットまたは動画を添付" }, "LANGUAGE_SHEET": { "TEXT_TITLE": "言語" diff --git a/i18n/mobile/app/pt.json b/i18n/mobile/app/pt.json index 278656e..5a82175 100644 --- a/i18n/mobile/app/pt.json +++ b/i18n/mobile/app/pt.json @@ -55,9 +55,10 @@ "TEXT_NO_MODELS": "Não há modelos." }, "CONTACT_SUPPORT_SHEET": { - "TEXT_TITLE": "Falar com o suporte", - "TEXT_MESSAGE_PLACEHOLDER": "Sua mensagem", - "BUTTON_ATTACH": "Anexar capturas de tela ou vídeo" + "TEXT_REPORT_BUG": "Reportar um bug", + "TEXT_YOUR_MESSAGE": "Sua mensagem", + "TEXT_MESSAGE_SENT": "Obrigado pelo seu feedback! Sua mensagem foi enviada com sucesso", + "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Anexar capturas de tela ou vídeo" }, "LANGUAGE_SHEET": { "TEXT_TITLE": "Idioma" diff --git a/i18n/mobile/app/ru.json b/i18n/mobile/app/ru.json index bd1cea6..ed4470e 100644 --- a/i18n/mobile/app/ru.json +++ b/i18n/mobile/app/ru.json @@ -55,9 +55,10 @@ "TEXT_NO_MODELS": "Моделей нет." }, "CONTACT_SUPPORT_SHEET": { - "TEXT_TITLE": "Связаться с поддержкой", - "TEXT_MESSAGE_PLACEHOLDER": "Ваше сообщение", - "BUTTON_ATTACH": "Прикрепить скриншоты или видео" + "TEXT_REPORT_BUG": "Сообщить об ошибке", + "TEXT_YOUR_MESSAGE": "Введите сообщение", + "TEXT_MESSAGE_SENT": "Спасибо за отзыв! Ваше сообщение успешно отправлено", + "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Прикрепить скриншот или видео" }, "LANGUAGE_SHEET": { "TEXT_TITLE": "Язык" diff --git a/i18n/mobile/app/zh.json b/i18n/mobile/app/zh.json index b2a7379..26af9e0 100644 --- a/i18n/mobile/app/zh.json +++ b/i18n/mobile/app/zh.json @@ -55,9 +55,10 @@ "TEXT_NO_MODELS": "暂无模型。" }, "CONTACT_SUPPORT_SHEET": { - "TEXT_TITLE": "联系客服", - "TEXT_MESSAGE_PLACEHOLDER": "您的留言", - "BUTTON_ATTACH": "附加截图或视频" + "TEXT_REPORT_BUG": "报告错误", + "TEXT_YOUR_MESSAGE": "您的消息", + "TEXT_MESSAGE_SENT": "感谢您的反馈!您的消息已成功发送", + "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "添加截图或视频" }, "LANGUAGE_SHEET": { "TEXT_TITLE": "语言" diff --git a/i18n/mobile/profile/en.json b/i18n/mobile/profile/en.json index f71ce19..ae92445 100644 --- a/i18n/mobile/profile/en.json +++ b/i18n/mobile/profile/en.json @@ -4,8 +4,6 @@ "TEXT_LOGOUT": "Logout", "TEXT_ARCHIVED_CHATS": "Archived Chats", "TEXT_SETTINGS": "Settings", - "TEXT_REPORT_BUG": "Report a bug", - "TEXT_RATE_APP": "Rate app", "TEXT_DELETE_ACCOUNT": "Delete Account", "TEXT_DELETE_ACCOUNT_TITLE": "Are you sure you want to delete your account?", "TEXT_DELETE_ACCOUNT_MESSAGE": "This action will result in permanent data loss if we don't hear from you in the next 30 days.", diff --git a/i18n/mobile/shared/de.json b/i18n/mobile/shared/de.json index bdcb2e5..391108a 100644 --- a/i18n/mobile/shared/de.json +++ b/i18n/mobile/shared/de.json @@ -6,6 +6,7 @@ "TEXT_REQUIRED_PASSWORD": "Bitte geben Sie Ihr Passwort ein", "TEXT_INVALID_URL": "Bitte geben Sie eine gültige Server-URL ein, z. B. https://your.domain oder http://192.168.1.10:3000", "TEXT_REQUIRED_FOLDER_NAME": "Bitte geben Sie einen Ordnernamen ein", + "TEXT_REQUIRED_MESSAGE": "Bitte geben Sie eine Nachricht ein", "TEXT_REQUIRED_PROFILE_NAME": "Bitte geben Sie einen Profilnamen ein", "TEXT_REQUIRED_CURRENT_PASSWORD": "Bitte geben Sie Ihr aktuelles Passwort ein", "TEXT_REQUIRED_NEW_PASSWORD": "Bitte geben Sie ein neues Passwort ein", diff --git a/i18n/mobile/shared/en.json b/i18n/mobile/shared/en.json index f139008..5b0c93b 100644 --- a/i18n/mobile/shared/en.json +++ b/i18n/mobile/shared/en.json @@ -109,12 +109,6 @@ "SEARCH_INPUT": { "BUTTON_CANCEL": "Cancel", "TEXT_SEARCH_CHATS": "Search chats" - }, - "CONTACT_SUPPORT_SHEET": { - "TEXT_REPORT_BUG": "Report a bug", - "TEXT_YOUR_MESSAGE": "Your message", - "TEXT_MESSAGE_SENT": "Thank you for your feedback! Your message has been sent successfully", - "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Attach screenshots or video" } } } diff --git a/i18n/mobile/shared/es.json b/i18n/mobile/shared/es.json index 2a605da..c559cff 100644 --- a/i18n/mobile/shared/es.json +++ b/i18n/mobile/shared/es.json @@ -6,6 +6,7 @@ "TEXT_REQUIRED_PASSWORD": "Introduce tu contraseña", "TEXT_INVALID_URL": "Introduce una URL de servidor válida, p. ej. https://your.domain o http://192.168.1.10:3000", "TEXT_REQUIRED_FOLDER_NAME": "Introduce un nombre de carpeta", + "TEXT_REQUIRED_MESSAGE": "Introduce un mensaje", "TEXT_REQUIRED_PROFILE_NAME": "Introduce un nombre de perfil", "TEXT_REQUIRED_CURRENT_PASSWORD": "Introduce tu contraseña actual", "TEXT_REQUIRED_NEW_PASSWORD": "Introduce una nueva contraseña", diff --git a/i18n/mobile/shared/fr.json b/i18n/mobile/shared/fr.json index ef0bc68..eccfb64 100644 --- a/i18n/mobile/shared/fr.json +++ b/i18n/mobile/shared/fr.json @@ -6,6 +6,7 @@ "TEXT_REQUIRED_PASSWORD": "Veuillez saisir votre mot de passe", "TEXT_INVALID_URL": "Veuillez saisir une URL de serveur valide, ex. https://your.domain ou http://192.168.1.10:3000", "TEXT_REQUIRED_FOLDER_NAME": "Veuillez saisir un nom de dossier", + "TEXT_REQUIRED_MESSAGE": "Veuillez saisir un message", "TEXT_REQUIRED_PROFILE_NAME": "Veuillez saisir un nom de profil", "TEXT_REQUIRED_CURRENT_PASSWORD": "Veuillez saisir votre mot de passe actuel", "TEXT_REQUIRED_NEW_PASSWORD": "Veuillez saisir un nouveau mot de passe", diff --git a/i18n/mobile/shared/ja.json b/i18n/mobile/shared/ja.json index 9b3bba4..6868521 100644 --- a/i18n/mobile/shared/ja.json +++ b/i18n/mobile/shared/ja.json @@ -6,6 +6,7 @@ "TEXT_REQUIRED_PASSWORD": "パスワードを入力してください", "TEXT_INVALID_URL": "有効なサーバー URL を入力してください(例: https://your.domain または http://192.168.1.10:3000)", "TEXT_REQUIRED_FOLDER_NAME": "フォルダ名を入力してください", + "TEXT_REQUIRED_MESSAGE": "メッセージを入力してください", "TEXT_REQUIRED_PROFILE_NAME": "プロフィール名を入力してください", "TEXT_REQUIRED_CURRENT_PASSWORD": "現在のパスワードを入力してください", "TEXT_REQUIRED_NEW_PASSWORD": "新しいパスワードを入力してください", diff --git a/i18n/mobile/shared/pt.json b/i18n/mobile/shared/pt.json index 50e0f63..8f1979a 100644 --- a/i18n/mobile/shared/pt.json +++ b/i18n/mobile/shared/pt.json @@ -6,6 +6,7 @@ "TEXT_REQUIRED_PASSWORD": "Digite sua senha", "TEXT_INVALID_URL": "Digite uma URL de servidor válida, ex.: https://your.domain ou http://192.168.1.10:3000", "TEXT_REQUIRED_FOLDER_NAME": "Digite um nome de pasta", + "TEXT_REQUIRED_MESSAGE": "Digite uma mensagem", "TEXT_REQUIRED_PROFILE_NAME": "Digite um nome de perfil", "TEXT_REQUIRED_CURRENT_PASSWORD": "Digite sua senha atual", "TEXT_REQUIRED_NEW_PASSWORD": "Digite uma nova senha", diff --git a/i18n/mobile/shared/ru.json b/i18n/mobile/shared/ru.json index caa587d..f3111f6 100644 --- a/i18n/mobile/shared/ru.json +++ b/i18n/mobile/shared/ru.json @@ -6,6 +6,7 @@ "TEXT_REQUIRED_PASSWORD": "Введите пароль", "TEXT_INVALID_URL": "Введите корректный URL сервера, напр. https://your.domain или http://192.168.1.10:3000", "TEXT_REQUIRED_FOLDER_NAME": "Введите название папки", + "TEXT_REQUIRED_MESSAGE": "Введите сообщение", "TEXT_REQUIRED_PROFILE_NAME": "Введите имя профиля", "TEXT_REQUIRED_CURRENT_PASSWORD": "Введите текущий пароль", "TEXT_REQUIRED_NEW_PASSWORD": "Введите новый пароль", diff --git a/i18n/mobile/shared/zh.json b/i18n/mobile/shared/zh.json index 21e8ed1..7718991 100644 --- a/i18n/mobile/shared/zh.json +++ b/i18n/mobile/shared/zh.json @@ -6,6 +6,7 @@ "TEXT_REQUIRED_PASSWORD": "请输入您的密码", "TEXT_INVALID_URL": "请输入有效的服务器 URL,例如 https://your.domain 或 http://192.168.1.10:3000", "TEXT_REQUIRED_FOLDER_NAME": "请输入文件夹名称", + "TEXT_REQUIRED_MESSAGE": "请输入消息内容", "TEXT_REQUIRED_PROFILE_NAME": "请输入个人资料名称", "TEXT_REQUIRED_CURRENT_PASSWORD": "请输入当前密码", "TEXT_REQUIRED_NEW_PASSWORD": "请输入新密码", From 6aa00a8d17b8621f0ee4811ed518904ff1d77fc9 Mon Sep 17 00:00:00 2001 From: vmikhalevskii Date: Fri, 21 Aug 2026 10:17:06 +0300 Subject: [PATCH 15/17] feat: add ContactSupportSheet to settings --- .../features/settings/src/lib/component.tsx | 13 +++++-- .../profile-menu-sheet/src/lib/component.tsx | 35 ++++--------------- 2 files changed, 16 insertions(+), 32 deletions(-) diff --git a/libs/mobile/settings/features/settings/src/lib/component.tsx b/libs/mobile/settings/features/settings/src/lib/component.tsx index 67a1251..519ef09 100644 --- a/libs/mobile/settings/features/settings/src/lib/component.tsx +++ b/libs/mobile/settings/features/settings/src/lib/component.tsx @@ -1,6 +1,10 @@ import { useSelector } from '@legendapp/state/react'; import { useTranslation } from '@ronas-it/react-native-common-modules/i18n'; import { ReactElement, useRef, useState } from 'react'; +import { + ContactSupportSheet, + ContactSupportSheetMethods, +} from '@open-webui-react-native/mobile/settings/features/contact-support-sheet'; import { SettingsSection, SettingsSectionOption } from '@open-webui-react-native/mobile/settings/ui/section'; import { useLogout } from '@open-webui-react-native/mobile/shared/features/use-logout'; import { Avatar, View } from '@open-webui-react-native/mobile/shared/ui/ui-kit'; @@ -12,12 +16,11 @@ import { LanguageCode, MarkdownRenderer, } from '@open-webui-react-native/shared/utils/config'; +import { storeReviewService } from '@open-webui-react-native/shared/utils/store-review-service'; import { ToastService } from '@open-webui-react-native/shared/utils/toast-service'; import { ChangePasswordSheet, ChangePasswordSheetMethods, - ContactSupportSheet, - ContactSupportSheetMethods, DefaultModelSheet, DefaultModelSheetMethods, ProfileAvatarSheet, @@ -108,13 +111,17 @@ export function Settings(): ReactElement { { type: 'action', label: translate('TEXT_LOGOUT'), iconName: 'logout', isDanger: true, onPress: logout }, ]; + const handleRateAppPress = (): void => { + storeReviewService.openStoreReview(); + }; + const feedbackOptions: Array = [ { label: translate('TEXT_CONTACT_SUPPORT'), iconName: 'message', onPress: () => contactSupportSheetRef.current?.present(), }, - { label: translate('TEXT_RATE_APP'), iconName: 'star', onPress: ToastService.showFeatureNotImplemented }, + { label: translate('TEXT_RATE_APP'), iconName: 'star', onPress: handleRateAppPress }, ]; const chatsOptions: Array = [ diff --git a/libs/mobile/shared/features/profile-menu-sheet/src/lib/component.tsx b/libs/mobile/shared/features/profile-menu-sheet/src/lib/component.tsx index c83fe77..9c83cb3 100644 --- a/libs/mobile/shared/features/profile-menu-sheet/src/lib/component.tsx +++ b/libs/mobile/shared/features/profile-menu-sheet/src/lib/component.tsx @@ -1,11 +1,7 @@ import { BottomSheetModal } from '@gorhom/bottom-sheet'; import { useTranslation } from '@ronas-it/react-native-common-modules/i18n'; -import { Fragment, ReactElement, useRef } from 'react'; +import { ReactElement, useRef } from 'react'; import { Alert } from 'react-native'; -import { - ContactSupportSheet, - ContactSupportSheetMethods, -} from '@open-webui-react-native/mobile/shared/features/contact-support-sheet'; import { useLogout } from '@open-webui-react-native/mobile/shared/features/use-logout'; import { Avatar, @@ -16,7 +12,6 @@ import { } from '@open-webui-react-native/mobile/shared/ui/ui-kit'; import { authApi } from '@open-webui-react-native/shared/data-access/api'; import { FeatureID, isFeatureEnabled } from '@open-webui-react-native/shared/utils/feature-flag'; -import { storeReviewService } from '@open-webui-react-native/shared/utils/store-review-service'; import { ToastService } from '@open-webui-react-native/shared/utils/toast-service'; interface ProfileMenuSheetProps extends Pick { @@ -35,7 +30,6 @@ export function ProfileMenuSheet({ const { data: profile } = authApi.useGetProfile(); const actionsBottomSheetRef = useRef(null); - const contactSupportSheetRef = useRef(null); const closeActionsSheet = (): Promise => new Promise((resolve) => { @@ -53,10 +47,6 @@ export function ProfileMenuSheet({ onSettingsPress(); }; - const handleRateAppPress = (): void => { - storeReviewService.openStoreReview(); - }; - const handleDeleteAccountPress = (): void => { ToastService.show(translate('TEXT_ACCOUNT_DELETION_REQUEST')); }; @@ -89,16 +79,6 @@ export function ProfileMenuSheet({ ? handleArchivedChatsPress : ToastService.showFeatureNotImplemented, }, - { - title: translate('TEXT_REPORT_BUG'), - iconName: 'message', - onPress: () => contactSupportSheetRef.current?.present(), - }, - { - title: translate('TEXT_RATE_APP'), - iconName: 'star', - onPress: handleRateAppPress, - }, { title: translate('TEXT_DELETE_ACCOUNT'), iconName: 'trashCan', @@ -122,13 +102,10 @@ export function ProfileMenuSheet({ ); return ( - - - - + ); } From 8aa4a423a8a10dacd4801830aa7826a27d600267 Mon Sep 17 00:00:00 2001 From: vmikhalevskii Date: Fri, 21 Aug 2026 10:51:09 +0300 Subject: [PATCH 16/17] feat: add attachments limit reached --- i18n/mobile/app/de.json | 3 ++- i18n/mobile/app/en.json | 3 ++- i18n/mobile/app/es.json | 3 ++- i18n/mobile/app/fr.json | 3 ++- i18n/mobile/app/ja.json | 3 ++- i18n/mobile/app/pt.json | 3 ++- i18n/mobile/app/ru.json | 3 ++- i18n/mobile/app/zh.json | 3 ++- .../features/contact-support-sheet/src/lib/component.tsx | 9 +++++++++ .../features/contact-support-sheet/src/lib/config.ts | 3 +++ 10 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 libs/mobile/settings/features/contact-support-sheet/src/lib/config.ts diff --git a/i18n/mobile/app/de.json b/i18n/mobile/app/de.json index 786161c..b03afc1 100644 --- a/i18n/mobile/app/de.json +++ b/i18n/mobile/app/de.json @@ -58,7 +58,8 @@ "TEXT_REPORT_BUG": "Fehler melden", "TEXT_YOUR_MESSAGE": "Ihre Nachricht", "TEXT_MESSAGE_SENT": "Vielen Dank für Ihr Feedback! Ihre Nachricht wurde erfolgreich gesendet", - "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Screenshots oder Video anhängen" + "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Screenshots oder Video anhängen", + "TEXT_ATTACHMENTS_LIMIT_REACHED": "Sie können maximal {{count}} Dateien anhängen" }, "LANGUAGE_SHEET": { "TEXT_TITLE": "Sprache" diff --git a/i18n/mobile/app/en.json b/i18n/mobile/app/en.json index 49cc490..e19bb00 100644 --- a/i18n/mobile/app/en.json +++ b/i18n/mobile/app/en.json @@ -58,7 +58,8 @@ "TEXT_REPORT_BUG": "Report a bug", "TEXT_YOUR_MESSAGE": "Your message", "TEXT_MESSAGE_SENT": "Thank you for your feedback! Your message has been sent successfully", - "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Attach screenshots or video" + "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Attach screenshots or video", + "TEXT_ATTACHMENTS_LIMIT_REACHED": "You can attach up to {{count}} files" }, "LANGUAGE_SHEET": { "TEXT_TITLE": "Language" diff --git a/i18n/mobile/app/es.json b/i18n/mobile/app/es.json index f0c6d43..3719b3c 100644 --- a/i18n/mobile/app/es.json +++ b/i18n/mobile/app/es.json @@ -58,7 +58,8 @@ "TEXT_REPORT_BUG": "Informar un error", "TEXT_YOUR_MESSAGE": "Tu mensaje", "TEXT_MESSAGE_SENT": "¡Gracias por tus comentarios! Tu mensaje se ha enviado correctamente", - "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Adjuntar capturas de pantalla o video" + "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Adjuntar capturas de pantalla o video", + "TEXT_ATTACHMENTS_LIMIT_REACHED": "Puedes adjuntar hasta {{count}} archivos" }, "LANGUAGE_SHEET": { "TEXT_TITLE": "Idioma" diff --git a/i18n/mobile/app/fr.json b/i18n/mobile/app/fr.json index 2891fa9..f390835 100644 --- a/i18n/mobile/app/fr.json +++ b/i18n/mobile/app/fr.json @@ -58,7 +58,8 @@ "TEXT_REPORT_BUG": "Signaler un bug", "TEXT_YOUR_MESSAGE": "Votre message", "TEXT_MESSAGE_SENT": "Merci pour votre retour ! Votre message a été envoyé avec succès", - "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Joindre des captures d'écran ou une vidéo" + "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Joindre des captures d'écran ou une vidéo", + "TEXT_ATTACHMENTS_LIMIT_REACHED": "Vous pouvez joindre jusqu'à {{count}} fichiers" }, "LANGUAGE_SHEET": { "TEXT_TITLE": "Langue" diff --git a/i18n/mobile/app/ja.json b/i18n/mobile/app/ja.json index f5ba81d..ee24d9d 100644 --- a/i18n/mobile/app/ja.json +++ b/i18n/mobile/app/ja.json @@ -58,7 +58,8 @@ "TEXT_REPORT_BUG": "不具合を報告", "TEXT_YOUR_MESSAGE": "メッセージ", "TEXT_MESSAGE_SENT": "フィードバックをありがとうございます!メッセージが正常に送信されました", - "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "スクリーンショットまたは動画を添付" + "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "スクリーンショットまたは動画を添付", + "TEXT_ATTACHMENTS_LIMIT_REACHED": "添付できるファイルは最大{{count}}件までです" }, "LANGUAGE_SHEET": { "TEXT_TITLE": "言語" diff --git a/i18n/mobile/app/pt.json b/i18n/mobile/app/pt.json index 5a82175..6b3c588 100644 --- a/i18n/mobile/app/pt.json +++ b/i18n/mobile/app/pt.json @@ -58,7 +58,8 @@ "TEXT_REPORT_BUG": "Reportar um bug", "TEXT_YOUR_MESSAGE": "Sua mensagem", "TEXT_MESSAGE_SENT": "Obrigado pelo seu feedback! Sua mensagem foi enviada com sucesso", - "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Anexar capturas de tela ou vídeo" + "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Anexar capturas de tela ou vídeo", + "TEXT_ATTACHMENTS_LIMIT_REACHED": "Você pode anexar até {{count}} arquivos" }, "LANGUAGE_SHEET": { "TEXT_TITLE": "Idioma" diff --git a/i18n/mobile/app/ru.json b/i18n/mobile/app/ru.json index ed4470e..2ea2538 100644 --- a/i18n/mobile/app/ru.json +++ b/i18n/mobile/app/ru.json @@ -58,7 +58,8 @@ "TEXT_REPORT_BUG": "Сообщить об ошибке", "TEXT_YOUR_MESSAGE": "Введите сообщение", "TEXT_MESSAGE_SENT": "Спасибо за отзыв! Ваше сообщение успешно отправлено", - "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Прикрепить скриншот или видео" + "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "Прикрепить скриншот или видео", + "TEXT_ATTACHMENTS_LIMIT_REACHED": "Можно прикрепить не более {{count}} вложений" }, "LANGUAGE_SHEET": { "TEXT_TITLE": "Язык" diff --git a/i18n/mobile/app/zh.json b/i18n/mobile/app/zh.json index 26af9e0..9978117 100644 --- a/i18n/mobile/app/zh.json +++ b/i18n/mobile/app/zh.json @@ -58,7 +58,8 @@ "TEXT_REPORT_BUG": "报告错误", "TEXT_YOUR_MESSAGE": "您的消息", "TEXT_MESSAGE_SENT": "感谢您的反馈!您的消息已成功发送", - "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "添加截图或视频" + "BUTTON_ATTACH_SCREENSHOTS_OR_VIDEO": "添加截图或视频", + "TEXT_ATTACHMENTS_LIMIT_REACHED": "最多只能添加 {{count}} 个附件" }, "LANGUAGE_SHEET": { "TEXT_TITLE": "语言" diff --git a/libs/mobile/settings/features/contact-support-sheet/src/lib/component.tsx b/libs/mobile/settings/features/contact-support-sheet/src/lib/component.tsx index 40c61ae..1a52a6f 100644 --- a/libs/mobile/settings/features/contact-support-sheet/src/lib/component.tsx +++ b/libs/mobile/settings/features/contact-support-sheet/src/lib/component.tsx @@ -25,6 +25,7 @@ import { appConfigurationApi, authApi, supabaseApi } from '@open-webui-react-nat import { ImageData } from '@open-webui-react-native/shared/data-access/common'; import { ToastService } from '@open-webui-react-native/shared/utils/toast-service'; import { AttachmentRow } from './components'; +import { contactSupportSheetConfig } from './config'; import { ContactSupportFormSchema } from './forms'; export type ContactSupportSheetMethods = { @@ -64,6 +65,14 @@ export function ContactSupportSheet({ ref, ...props }: ContactSupportSheetProps) const handleOpen = (): void => inputRef.current?.focus(); const handlePickImage = async (): Promise => { + if (images.length >= contactSupportSheetConfig.maxAttachmentsCount) { + ToastService.showError( + translate('TEXT_ATTACHMENTS_LIMIT_REACHED', { count: contactSupportSheetConfig.maxAttachmentsCount }), + ); + + return; + } + const image = await imagePickerService.getImage(ImagePickerSource.GALLERY_WITH_VIDEO); const asset = image?.assets?.[0]; diff --git a/libs/mobile/settings/features/contact-support-sheet/src/lib/config.ts b/libs/mobile/settings/features/contact-support-sheet/src/lib/config.ts new file mode 100644 index 0000000..279b27d --- /dev/null +++ b/libs/mobile/settings/features/contact-support-sheet/src/lib/config.ts @@ -0,0 +1,3 @@ +export const contactSupportSheetConfig = { + maxAttachmentsCount: 5, +}; From b1e88ae61e12250a1996c60b8cf798ae4205f205 Mon Sep 17 00:00:00 2001 From: vmikhalevskii Date: Fri, 21 Aug 2026 11:14:21 +0300 Subject: [PATCH 17/17] fix: locale for report a bug --- i18n/mobile/app/de.json | 2 +- i18n/mobile/app/en.json | 2 +- i18n/mobile/app/es.json | 2 +- i18n/mobile/app/fr.json | 2 +- i18n/mobile/app/ja.json | 2 +- i18n/mobile/app/pt.json | 2 +- i18n/mobile/app/ru.json | 2 +- i18n/mobile/app/zh.json | 2 +- .../features/contact-support-sheet/src/lib/component.tsx | 8 +++++--- .../settings/features/settings/src/lib/component.tsx | 2 +- 10 files changed, 14 insertions(+), 12 deletions(-) diff --git a/i18n/mobile/app/de.json b/i18n/mobile/app/de.json index b03afc1..51d91ec 100644 --- a/i18n/mobile/app/de.json +++ b/i18n/mobile/app/de.json @@ -16,7 +16,7 @@ "TEXT_AVATAR": "Avatar", "TEXT_CHANGE_PASSWORD": "Passwort ändern", "TEXT_LOGOUT": "Abmelden", - "TEXT_CONTACT_SUPPORT": "Support kontaktieren", + "TEXT_REPORT_A_BUG": "Fehler melden", "TEXT_RATE_APP": "App bewerten", "TEXT_ARCHIVED_CHATS": "Archivierte Chats", "TEXT_EXPORT_ALL_CHATS": "Alle Chats exportieren", diff --git a/i18n/mobile/app/en.json b/i18n/mobile/app/en.json index e19bb00..cb55358 100644 --- a/i18n/mobile/app/en.json +++ b/i18n/mobile/app/en.json @@ -16,7 +16,7 @@ "TEXT_AVATAR": "Avatar", "TEXT_CHANGE_PASSWORD": "Change password", "TEXT_LOGOUT": "Logout", - "TEXT_CONTACT_SUPPORT": "Contact support", + "TEXT_REPORT_A_BUG": "Report a bug", "TEXT_RATE_APP": "Rate app", "TEXT_ARCHIVED_CHATS": "Archived chats", "TEXT_EXPORT_ALL_CHATS": "Export all chats", diff --git a/i18n/mobile/app/es.json b/i18n/mobile/app/es.json index 3719b3c..1ed9a61 100644 --- a/i18n/mobile/app/es.json +++ b/i18n/mobile/app/es.json @@ -16,7 +16,7 @@ "TEXT_AVATAR": "Avatar", "TEXT_CHANGE_PASSWORD": "Cambiar contraseña", "TEXT_LOGOUT": "Cerrar sesión", - "TEXT_CONTACT_SUPPORT": "Contactar con soporte", + "TEXT_REPORT_A_BUG": "Informar un error", "TEXT_RATE_APP": "Valorar la aplicación", "TEXT_ARCHIVED_CHATS": "Chats archivados", "TEXT_EXPORT_ALL_CHATS": "Exportar todos los chats", diff --git a/i18n/mobile/app/fr.json b/i18n/mobile/app/fr.json index f390835..80bcf74 100644 --- a/i18n/mobile/app/fr.json +++ b/i18n/mobile/app/fr.json @@ -16,7 +16,7 @@ "TEXT_AVATAR": "Avatar", "TEXT_CHANGE_PASSWORD": "Modifier le mot de passe", "TEXT_LOGOUT": "Déconnexion", - "TEXT_CONTACT_SUPPORT": "Contacter le support", + "TEXT_REPORT_A_BUG": "Signaler un bug", "TEXT_RATE_APP": "Noter l'application", "TEXT_ARCHIVED_CHATS": "Discussions archivées", "TEXT_EXPORT_ALL_CHATS": "Exporter toutes les discussions", diff --git a/i18n/mobile/app/ja.json b/i18n/mobile/app/ja.json index ee24d9d..6a615e1 100644 --- a/i18n/mobile/app/ja.json +++ b/i18n/mobile/app/ja.json @@ -16,7 +16,7 @@ "TEXT_AVATAR": "アバター", "TEXT_CHANGE_PASSWORD": "パスワードを変更", "TEXT_LOGOUT": "ログアウト", - "TEXT_CONTACT_SUPPORT": "サポートに問い合わせる", + "TEXT_REPORT_A_BUG": "不具合を報告", "TEXT_RATE_APP": "アプリを評価", "TEXT_ARCHIVED_CHATS": "アーカイブ済みチャット", "TEXT_EXPORT_ALL_CHATS": "すべてのチャットをエクスポート", diff --git a/i18n/mobile/app/pt.json b/i18n/mobile/app/pt.json index 6b3c588..45f719a 100644 --- a/i18n/mobile/app/pt.json +++ b/i18n/mobile/app/pt.json @@ -16,7 +16,7 @@ "TEXT_AVATAR": "Avatar", "TEXT_CHANGE_PASSWORD": "Alterar senha", "TEXT_LOGOUT": "Sair", - "TEXT_CONTACT_SUPPORT": "Falar com o suporte", + "TEXT_REPORT_A_BUG": "Reportar um bug", "TEXT_RATE_APP": "Avaliar o aplicativo", "TEXT_ARCHIVED_CHATS": "Chats arquivados", "TEXT_EXPORT_ALL_CHATS": "Exportar todos os chats", diff --git a/i18n/mobile/app/ru.json b/i18n/mobile/app/ru.json index 2ea2538..7337bb5 100644 --- a/i18n/mobile/app/ru.json +++ b/i18n/mobile/app/ru.json @@ -16,7 +16,7 @@ "TEXT_AVATAR": "Аватар", "TEXT_CHANGE_PASSWORD": "Изменить пароль", "TEXT_LOGOUT": "Выйти", - "TEXT_CONTACT_SUPPORT": "Связаться с поддержкой", + "TEXT_REPORT_A_BUG": "Сообщить об ошибке", "TEXT_RATE_APP": "Оценить приложение", "TEXT_ARCHIVED_CHATS": "Архивные чаты", "TEXT_EXPORT_ALL_CHATS": "Экспортировать все чаты", diff --git a/i18n/mobile/app/zh.json b/i18n/mobile/app/zh.json index 9978117..651ebf1 100644 --- a/i18n/mobile/app/zh.json +++ b/i18n/mobile/app/zh.json @@ -16,7 +16,7 @@ "TEXT_AVATAR": "头像", "TEXT_CHANGE_PASSWORD": "修改密码", "TEXT_LOGOUT": "退出登录", - "TEXT_CONTACT_SUPPORT": "联系客服", + "TEXT_REPORT_A_BUG": "报告错误", "TEXT_RATE_APP": "为应用评分", "TEXT_ARCHIVED_CHATS": "已归档聊天", "TEXT_EXPORT_ALL_CHATS": "导出所有聊天", diff --git a/libs/mobile/settings/features/contact-support-sheet/src/lib/component.tsx b/libs/mobile/settings/features/contact-support-sheet/src/lib/component.tsx index 1a52a6f..cd52914 100644 --- a/libs/mobile/settings/features/contact-support-sheet/src/lib/component.tsx +++ b/libs/mobile/settings/features/contact-support-sheet/src/lib/component.tsx @@ -58,7 +58,11 @@ export function ContactSupportSheet({ ref, ...props }: ContactSupportSheetProps) resolver: yupResolver(ContactSupportFormSchema.validationSchema), }); - const closeModal = (): void => sheetRef.current?.close(); + const closeModal = (): void => { + sheetRef.current?.close(); + reset(new ContactSupportFormSchema()); + resetAttachments(); + }; const openModal = (): void => sheetRef.current?.present(); @@ -100,8 +104,6 @@ export function ContactSupportSheet({ ref, ...props }: ContactSupportSheetProps) }); closeModal(); - reset(new ContactSupportFormSchema()); - resetAttachments(); ToastService.showSuccess(translate('TEXT_MESSAGE_SENT')); } catch { ToastService.showError(); diff --git a/libs/mobile/settings/features/settings/src/lib/component.tsx b/libs/mobile/settings/features/settings/src/lib/component.tsx index 519ef09..75454a5 100644 --- a/libs/mobile/settings/features/settings/src/lib/component.tsx +++ b/libs/mobile/settings/features/settings/src/lib/component.tsx @@ -117,7 +117,7 @@ export function Settings(): ReactElement { const feedbackOptions: Array = [ { - label: translate('TEXT_CONTACT_SUPPORT'), + label: translate('TEXT_REPORT_A_BUG'), iconName: 'message', onPress: () => contactSupportSheetRef.current?.present(), },