Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": ["react-native-compressor"]
}
35 changes: 17 additions & 18 deletions apps/mobile/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ const createConfig = (): Omit<ExpoConfig, 'extra'> & { extra: { eas: EASConfig }
slug: process.env.EXPO_PUBLIC_APP_SLUG as string,
scheme: process.env.EXPO_PUBLIC_APP_SCHEME as string,
owner: process.env.EXPO_PUBLIC_APP_OWNER as string,
version: '1.6.5',
version: '1.8.1',
userInterfaceStyle: 'automatic',
orientation: 'portrait',
icon: './assets/icon.png',
runtimeVersion: '1.6.5',
runtimeVersion: '1.8.1',
experiments: {
reactCompiler: true,
},
Expand All @@ -46,17 +46,25 @@ const createConfig = (): Omit<ExpoConfig, 'extra'> & { extra: { eas: EASConfig }
supportsTablet: false,
buildNumber: appEnv.select({
default: '18',
production: '31',
production: '35',
}),
config: {
usesNonExemptEncryption: false,
},
infoPlist: {
// Allow connecting to self-hosted Open WebUI servers that are reachable
// only over plain HTTP (local network, Docker, Raspberry Pi, Tailscale).
NSAppTransportSecurity: {
NSAllowsArbitraryLoads: true,
NSAllowsLocalNetworking: true,
},
},
},
android: {
package: appId,
versionCode: appEnv.select({
default: 15,
production: 31,
production: 35,
}),
adaptiveIcon: {
foregroundImage: './assets/adaptive-icon.png',
Expand Down Expand Up @@ -103,29 +111,20 @@ const createConfig = (): Omit<ExpoConfig, 'extra'> & { extra: { eas: EASConfig }
{
microphonePermission:
'Open MobileUI uses your microphone to let you record and send voice messages in chat conversations.',
enableBackgroundPlayback: false,
enableBackgroundRecording: false,
},
],
[
'expo-build-properties',
{
android: {
androidGradlePluginVersion: '8.3.2',
compileSdkVersion: 36,
targetSdkVersion: 35,
buildToolsVersion: '36.0.0',
ndkVersion: '27.1.12297006',
packagingOptions: {
jniLibs: {
useLegacyPackaging: false,
},
},
},
ios: {
useFrameworks: 'static',
// Allow plain HTTP connections to self-hosted servers (local network,
// Tailscale, etc.). Mirrors iOS NSAllowsArbitraryLoads above.
usesCleartextTraffic: true,
},
},
],
['./plugins/with-remove-media-playback-permission'],
]),
newArchEnabled: true,
extra,
Expand Down
92 changes: 61 additions & 31 deletions apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,65 +24,94 @@
"ios": "expo run:ios"
},
"dependencies": {
"@expo/ui": "~56.0.16",
"@expo/metro-config": "~57.0.0",
"@expo/metro-runtime": "~57.0.7",
"@expo/ui": "~57.0.7",
"@gorhom/bottom-sheet": "^5.2.8",
"@hookform/resolvers": "^5.1.1",
"@legendapp/state": "2.1.15",
"@likashefqet/react-native-image-zoom": "^4.3.0",
"@lukemorales/query-key-factory": "^1.3.4",
"@pusher/pusher-websocket-react-native": "^1.3.1",
"@react-native-community/hooks": "^100.1.0",
"@react-native-community/netinfo": "12.0.1",
"@ronas-it/axios-api-client": "^0.1.2",
"@ronas-it/react-native-common-modules": "^1.1.0",
"@ronas-it/rtkq-entity-api": "^1.1.1",
"@sentry/react-native": "~7.11.0",
"@shopify/flash-list": "2.0.2",
"@tanstack/react-query": "^5.80.6",
"@tanstack/react-query-persist-client": "^5.87.4",
"axios-observable": "^2.1.0",
"babel-plugin-module-resolver": "^5.0.2",
"babel-plugin-react-compiler": "^19.0.0-beta-af1b7da-20250417",
"class-transformer": "^0.5.1",
"clsx": "^2.1.1",
"expo": "^56.0.0",
"expo-asset": "~56.0.15",
"expo-audio": "~56.0.11",
"expo-build-properties": "~56.0.16",
"expo-clipboard": "~56.0.3",
"expo-constants": "~56.0.16",
"expo-crypto": "~56.0.4",
"expo-dev-client": "~56.0.18",
"expo-document-picker": "~56.0.4",
"expo-file-system": "~56.0.7",
"expo-haptics": "~56.0.3",
"expo-image": "~56.0.9",
"expo-image-picker": "~56.0.15",
"expo-insights": "~56.0.15",
"expo-linear-gradient": "~56.0.4",
"expo-linking": "~56.0.13",
"expo-localization": "~56.0.6",
"expo-media-library": "~56.0.6",
"expo-router": "~56.2.8",
"expo-sharing": "~56.0.15",
"expo-speech": "~56.0.3",
"expo-splash-screen": "~56.0.10",
"expo-status-bar": "~56.0.4",
"expo-updates": "~56.0.17",
"dayjs": "^1.11.13",
"expo": "^57.0.0",
"expo-asset": "~57.0.7",
"expo-audio": "~57.0.3",
"expo-build-properties": "~57.0.7",
"expo-clipboard": "~57.0.1",
"expo-constants": "~57.0.7",
"expo-crypto": "~57.0.1",
"expo-dev-client": "~57.0.9",
"expo-document-picker": "~57.0.1",
"expo-file-system": "~57.0.1",
"expo-haptics": "~57.0.1",
"expo-image": "~57.0.1",
"expo-image-picker": "~57.0.6",
"expo-insights": "~57.0.6",
"expo-linear-gradient": "~57.0.1",
"expo-linking": "~57.0.4",
"expo-localization": "~57.0.1",
"expo-media-library": "~57.0.3",
"expo-router": "~57.0.8",
"expo-sharing": "~57.0.7",
"expo-speech": "~57.0.1",
"expo-splash-screen": "~57.0.5",
"expo-status-bar": "~57.0.1",
"expo-updates": "~57.0.10",
"html-entities": "^2.6.0",
"i18n-js": "^4.5.1",
"immer": "^10.1.1",
"lodash-es": "^4.17.21",
"luxon": "^3.6.1",
"markdown-it-math": "^5.2.1",
"nativewind": "^4.1.23",
"patch-package": "^8.0.0",
"pusher-js": "^8.4.0",
"react": "19.2.3",
"react-compiler-runtime": "^19.0.0-beta-ebf51a3-20250411",
"react-content-loader": "^7.0.2",
"react-dom": "19.2.3",
"react-hook-form": "^7.57.0",
"react-native": "0.85.3",
"react-native-compressor": "^1.12.0",
"react-native": "0.86.0",
"react-native-code-highlighter": "^1.3.0",
"react-native-compressor": "^2.0.3",
"react-native-extended-stylesheet": "^0.12.0",
"react-native-gesture-handler": "~2.31.1",
"react-native-keyboard-controller": "1.21.6",
"react-native-gesture-handler": "~2.32.0",
"react-native-keyboard-controller": "1.21.9",
"react-native-markdown-display": "^7.0.2",
"react-native-mathjax-svg": "^0.9.9",
"react-native-mmkv": "^3.2.0",
"react-native-modal": "^14.0.0-rc.1",
"react-native-nitro-cookies": "^1.1.0",
"react-native-nitro-modules": "^0.35.9",
"react-native-reanimated": "4.3.1",
"react-native-reanimated": "4.5.0",
"react-native-safe-area-context": "~5.7.0",
"react-native-screens": "4.25.2",
"react-native-screens": "~4.26.0",
"react-native-svg": "15.15.4",
"react-native-svg-transformer": "~1.5.0",
"react-native-toast-message": "^2.3.0",
"react-native-uuid": "^2.0.3",
"react-native-web": "^0.21.0",
"react-native-webview": "13.16.1",
"react-native-worklets": "0.10.0",
"reflect-metadata": "^0.2.2",
"socket.io-client": "^4.8.1",
"tailwind-merge": "^3.3.0",
"tailwind-variants": "^1.0.0",
"tailwindcss": "^3.4.17",
"yup": "^1.6.1"
},
Expand All @@ -91,6 +120,7 @@
"@babel/plugin-transform-class-properties": "^7.29.7",
"@types/lodash-es": "^4.17.12",
"@types/luxon": "^3.6.2",
"babel-preset-expo": "~57.0.0",
"react-refresh": "^0.18.0"
},
"main": "expo-router/entry",
Expand Down
31 changes: 0 additions & 31 deletions apps/mobile/plugins/with-remove-media-playback-permission.js

This file was deleted.

13 changes: 8 additions & 5 deletions i18n/mobile/auth/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
"BUTTON_SIGN_IN": "Sign In"
},
"GOOGLE_FORM": {
"BUTTON_CONTINUE_WITH_GOOGLE": "Continue with Google",
"OAUTH_WEB_VIEW_MODAL": {
"BUTTON_CLOSE": "Close",
"TEXT_THIS_SIGN_IN_METHOD_IS_UNAVAILABLE": "This sign-in method is unavailable. Please use another option to continue."
}
"BUTTON_CONTINUE_WITH_GOOGLE": "Continue with Google"
},
"OIDC_FORM": {
"BUTTON_CONTINUE_WITH": "Continue with {{provider}}"
},
"OAUTH_WEB_VIEW_MODAL": {
"BUTTON_CLOSE": "Close",
"TEXT_THIS_SIGN_IN_METHOD_IS_UNAVAILABLE": "This sign-in method is unavailable. Please use another option to continue."
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion i18n/mobile/shared/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"TEXT_INVALID_EMAIL": "Please enter a valid email address",
"TEXT_REQUIRED_EMAIL": "Please enter your email",
"TEXT_REQUIRED_PASSWORD": "Please enter your password",
"TEXT_INVALID_URL": "Please enter a valid URL in format https://your.domain",
"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"
},
"COMMON": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { EmailFormSchema } from './forms';
interface EmailSignInFormProps {
onSuccess: () => void;
onApiUrlChange?: (url: string) => void;
setOauthProviders: (providers: Array<Provider>) => void;
setOauthProviders: (providers: Partial<Record<Provider, string>>) => void;
}

export function EmailSignInForm({ onSuccess, onApiUrlChange, setOauthProviders }: EmailSignInFormProps): ReactElement {
Expand All @@ -33,7 +33,7 @@ export function EmailSignInForm({ onSuccess, onApiUrlChange, setOauthProviders }
handleSubmit,
setValue,
formState: { isValid, errors },
} = useForm({
} = useForm<EmailFormSchema>({
defaultValues: new EmailFormSchema(),
resolver: yupResolver(EmailFormSchema.validationSchema),
});
Expand Down Expand Up @@ -61,7 +61,7 @@ export function EmailSignInForm({ onSuccess, onApiUrlChange, setOauthProviders }
useEffect(() => {
setValue('url', query, { shouldValidate: true });
onApiUrlChange?.(query);
setOauthProviders([]);
setOauthProviders({});
}, [query]);

useEffect(() => {
Expand All @@ -71,7 +71,7 @@ export function EmailSignInForm({ onSuccess, onApiUrlChange, setOauthProviders }

if (res?.name && res?.version) {
appStorageService.apiUrl.set(debouncedQuery);
setOauthProviders(Object.values(res.oauth.providers));
setOauthProviders(res.oauth.providers);
}
}
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { i18n } from '@ronas-it/react-native-common-modules/i18n';
import * as Yup from 'yup';
import { emailValidator } from '@open-webui-react-native/mobile/shared/utils/validation';
import { emailValidator, serverUrlValidator } from '@open-webui-react-native/mobile/shared/utils/validation';

export class EmailFormSchema {
public email: string;
Expand All @@ -17,21 +17,7 @@ export class EmailFormSchema {
return Yup.object().shape({
email: emailValidator().required(i18n.t('SHARED.VALIDATION.TEXT_REQUIRED_EMAIL')),
password: Yup.string().required(i18n.t('SHARED.VALIDATION.TEXT_REQUIRED_PASSWORD')),
url: Yup.string()
.url(i18n.t('SHARED.VALIDATION.TEXT_INVALID_URL'))
.test('no-path', i18n.t('SHARED.VALIDATION.TEXT_INVALID_URL'), (value) => {
if (!value) return true;

try {
const normalizedValue = value.trim();
const url = new URL(normalizedValue);
const normalizedPath = url.pathname.replace(/\/+$/, '/');

return normalizedPath === '/' && !url.search && !url.hash;
} catch {
return false;
}
}),
url: serverUrlValidator(),
});
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useTranslation } from '@ronas-it/react-native-common-modules/i18n';
import { Fragment, ReactElement, useState } from 'react';
import { OauthWebView } from '@open-webui-react-native/mobile/auth/features/oauth-web-view';
import { AppButton } from '@open-webui-react-native/mobile/shared/ui/ui-kit';
import { Provider } from '@open-webui-react-native/shared/data-access/api';
import { authState$ } from '@open-webui-react-native/shared/data-access/auth';
import { OauthWebViewModal } from './components';

interface GoogleSignInFormProps {
onSuccess?: () => void;
Expand Down Expand Up @@ -34,10 +35,12 @@ export function GoogleSignInForm({ onSuccess }: GoogleSignInFormProps): ReactEle
iconName='googleLogo'
onPress={handleSignInWithGooglePress}
/>
<OauthWebViewModal
<OauthWebView
isVisible={isModalVisible}
provider={Provider.GOOGLE}
onClose={handleCloseModal}
onGetToken={handleToken} />
onGetToken={handleToken}
/>
</Fragment>
);
}

This file was deleted.

12 changes: 12 additions & 0 deletions libs/mobile/auth/features/oauth-web-view/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"presets": [
[
"@nx/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
}
7 changes: 7 additions & 0 deletions libs/mobile/auth/features/oauth-web-view/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# mobile/auth/features/oauth-web-view

This library was generated with [Nx](https://nx.dev).

## Running unit tests

Run `nx test mobile/auth/features/oauth-web-view` to execute the unit tests via [Jest](https://jestjs.io).
Loading
Loading