diff --git a/packages/mobile/.gitignore b/packages/mobile/.gitignore index 4f1c51b36..2d6a96f24 100644 --- a/packages/mobile/.gitignore +++ b/packages/mobile/.gitignore @@ -21,6 +21,8 @@ DerivedData *.ipa *.xcuserstate +html/Web.bundle/src/web-src + .env # Android/IntelliJ @@ -75,4 +77,4 @@ ios-release.bundle.map codeqldb -vendor/bundle \ No newline at end of file +vendor/bundle diff --git a/packages/mobile/MobileWebAppContainer.tsx b/packages/mobile/MobileWebAppContainer.tsx new file mode 100644 index 000000000..c50b04d70 --- /dev/null +++ b/packages/mobile/MobileWebAppContainer.tsx @@ -0,0 +1,29 @@ +import React from 'react' +import { Platform } from 'react-native' +import { WebView } from 'react-native-webview' + +export const MobileWebAppContainer = () => { + const sourceUri = (Platform.OS === 'android' ? 'file:///android_asset/' : '') + 'Web.bundle/loader.html' + const params = 'platform=' + Platform.OS + const injectedJS = ` + if (!window.location.search) { + var link = document.getElementById('web-bundle-progress-bar'); + link.href = './src/index.html?${params}'; + link.click(); + }` + + /* eslint-disable @typescript-eslint/no-empty-function */ + return ( + {}} + onError={(err) => console.error('An error has occurred', err)} + onHttpError={() => console.error('An HTTP error occurred')} + onMessage={() => {}} + allowFileAccess={true} + injectedJavaScript={injectedJS} + /> + ) + /* eslint-enable @typescript-eslint/no-empty-function */ +} diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index ad2d539ed..f481efb40 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -219,6 +219,9 @@ android { } } } + sourceSets { + main { assets.srcDirs = ['src/main/assets', '../../html'] } + } } dependencies { diff --git a/packages/mobile/html/Web.bundle/loader.html b/packages/mobile/html/Web.bundle/loader.html new file mode 100644 index 000000000..b5fd807a2 --- /dev/null +++ b/packages/mobile/html/Web.bundle/loader.html @@ -0,0 +1 @@ + diff --git a/packages/mobile/html/Web.bundle/src/index.html b/packages/mobile/html/Web.bundle/src/index.html new file mode 100644 index 000000000..89ed8f450 --- /dev/null +++ b/packages/mobile/html/Web.bundle/src/index.html @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/mobile/ios/Podfile.lock b/packages/mobile/ios/Podfile.lock index 2e751bd44..3d0eb0469 100644 --- a/packages/mobile/ios/Podfile.lock +++ b/packages/mobile/ios/Podfile.lock @@ -434,7 +434,7 @@ PODS: - RNZipArchive/Core (6.0.8): - React-Core - SSZipArchive (= 2.2.3) - - sn-textview (1.0.2): + - sn-textview (1.1.0): - React-Core - SNReactNative (1.0.1): - React-Core @@ -749,7 +749,7 @@ SPEC CHECKSUMS: RNSVG: 302bfc9905bd8122f08966dc2ce2d07b7b52b9f8 RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8 RNZipArchive: 3f89b114cfeb89dac027fc5c7afd7e58d3dc4ebf - sn-textview: aaeeb41791e7d1784072adfb6b610db9b764acda + sn-textview: 494186ad03660daa52b1d3fd7b50b116b550f925 SNReactNative: b5e9e529c175c13f3a618e27c76cf3071213d5e1 SSZipArchive: 62d4947b08730e4cda640473b0066d209ff033c9 TrustKit: 073855e3adecd317417bda4ac9e9ac54a2e3b9f2 diff --git a/packages/mobile/ios/StandardNotes.xcodeproj/project.pbxproj b/packages/mobile/ios/StandardNotes.xcodeproj/project.pbxproj index 9a51ccc7a..2bc066131 100644 --- a/packages/mobile/ios/StandardNotes.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/StandardNotes.xcodeproj/project.pbxproj @@ -26,6 +26,8 @@ CD7D8400277FF8CB00915D89 /* Red@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = CD7D83FD277FF8CB00915D89 /* Red@3x.png */; }; CD7D8401277FF8CB00915D89 /* Red.png in Resources */ = {isa = PBXBuildFile; fileRef = CD7D83FE277FF8CB00915D89 /* Red.png */; }; CD7D8402277FF8CB00915D89 /* Red@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = CD7D83FF277FF8CB00915D89 /* Red@2x.png */; }; + D261494528699DCE00B17102 /* Web.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D261494428699DCE00B17102 /* Web.bundle */; }; + D261494628699DCE00B17102 /* Web.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D261494428699DCE00B17102 /* Web.bundle */; }; DD3D1CE428EC1C8BA0C49211 /* libPods-StandardNotes.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 41388EB4F886116157DA092C /* libPods-StandardNotes.a */; }; /* End PBXBuildFile section */ @@ -67,6 +69,7 @@ CD7D83FD277FF8CB00915D89 /* Red@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Red@3x.png"; sourceTree = ""; }; CD7D83FE277FF8CB00915D89 /* Red.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Red.png; sourceTree = ""; }; CD7D83FF277FF8CB00915D89 /* Red@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Red@2x.png"; sourceTree = ""; }; + D261494428699DCE00B17102 /* Web.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = Web.bundle; path = ../html/Web.bundle; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -132,6 +135,7 @@ 13B07FAE1A68108700A75B9A /* StandardNotes */ = { isa = PBXGroup; children = ( + D261494428699DCE00B17102 /* Web.bundle */, 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 13B07FB01A68108700A75B9A /* AppDelegate.m */, CD7D5EE527801F10005FE1BF /* StandardNotes.entitlements */, @@ -308,6 +312,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + D261494528699DCE00B17102 /* Web.bundle in Resources */, CD7D8401277FF8CB00915D89 /* Red.png in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, CD7D8402277FF8CB00915D89 /* Red@2x.png in Resources */, @@ -321,6 +326,7 @@ buildActionMask = 2147483647; files = ( CD7D5ED4278015D2005FE1BF /* Red.png in Resources */, + D261494628699DCE00B17102 /* Web.bundle in Resources */, CD7D5EE3278016E3005FE1BF /* Dev.xcassets in Resources */, CD7D5ED5278015D2005FE1BF /* Images.xcassets in Resources */, CD7D5ED6278015D2005FE1BF /* Red@2x.png in Resources */, diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 30aefb401..8bc6340e8 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -15,7 +15,8 @@ "lint:fix": "yarn lint --fix", "format": "prettier ./src", "format:fix": "yarn format --write", - "build": "yarn android:bundle && yarn install:pods", + "build": "yarn web:bundle && yarn install:pods && yarn android:bundle", + "web:bundle": "cp -r ../web/dist/. html/Web.bundle/src/web-src/", "pods": "yarn install:pods", "tsc": "tsc --noEmit", "start": "react-native start", @@ -43,6 +44,7 @@ "@standardnotes/sncrypto-common": "1.9.0", "@standardnotes/snjs": "^2.118.3", "@standardnotes/stylekit": "5.29.3", + "@standardnotes/web": "workspace:^", "@types/styled-components-react-native": "5.1.3", "js-base64": "^3.7.2", "moment": "^2.29.2", diff --git a/packages/mobile/src/Lib/constants.ts b/packages/mobile/src/Lib/constants.ts index 4d24934b9..a080e2635 100644 --- a/packages/mobile/src/Lib/constants.ts +++ b/packages/mobile/src/Lib/constants.ts @@ -1,3 +1,7 @@ +import { IsDev } from '@Lib/Utils' + export enum ErrorMessage { GeneralText = 'An error occurred. Please try again later.', } + +export const WebAppOptionEnabled = IsDev diff --git a/packages/mobile/src/ModalStack.tsx b/packages/mobile/src/ModalStack.tsx index b9e2e2bd0..913efeb0e 100644 --- a/packages/mobile/src/ModalStack.tsx +++ b/packages/mobile/src/ModalStack.tsx @@ -18,6 +18,7 @@ import { SCREEN_MANAGE_SESSIONS, SCREEN_SETTINGS, SCREEN_UPLOADED_FILES_LIST, + SCREEN_WEB_APP, } from '@Root/Screens/screens' import { Settings } from '@Root/Screens/Settings/Settings' import { UploadedFilesList } from '@Root/Screens/UploadedFilesList/UploadedFilesList' @@ -29,6 +30,7 @@ import React, { memo, useContext } from 'react' import { Platform } from 'react-native' import { HeaderButtons, Item } from 'react-navigation-header-buttons' import { ThemeContext } from 'styled-components' +import { MobileWebAppContainer } from '../MobileWebAppContainer' import { HeaderTitleParams, TEnvironment } from './App' import { ApplicationContext } from './ApplicationContext' import { AppStackComponent } from './AppStack' @@ -63,6 +65,7 @@ export type ModalStackNavigatorParamList = { title?: string text: string } + [SCREEN_WEB_APP]: undefined } export type ModalStackNavigationProp = { @@ -303,6 +306,7 @@ export const MainStackComponent = ({ env }: { env: TEnvironment }) => { })} component={WorkspaceInputModal} /> + ({ title: 'App' })} component={MobileWebAppContainer} /> ) } diff --git a/packages/mobile/src/Screens/Settings/Sections/OptionsSection.tsx b/packages/mobile/src/Screens/Settings/Sections/OptionsSection.tsx index 18011857b..c8d99b5da 100644 --- a/packages/mobile/src/Screens/Settings/Sections/OptionsSection.tsx +++ b/packages/mobile/src/Screens/Settings/Sections/OptionsSection.tsx @@ -1,3 +1,4 @@ +import { WebAppOptionEnabled } from '@Lib/constants' import { useSignedIn } from '@Lib/SnjsHelperHooks' import { useNavigation } from '@react-navigation/native' import { ButtonCell } from '@Root/Components/ButtonCell' @@ -7,7 +8,7 @@ import { SectionHeader } from '@Root/Components/SectionHeader' import { TableSection } from '@Root/Components/TableSection' import { useSafeApplicationContext } from '@Root/Hooks/useSafeApplicationContext' import { ModalStackNavigationProp } from '@Root/ModalStack' -import { SCREEN_MANAGE_SESSIONS, SCREEN_SETTINGS } from '@Root/Screens/screens' +import { SCREEN_MANAGE_SESSIONS, SCREEN_SETTINGS, SCREEN_WEB_APP } from '@Root/Screens/screens' import { ButtonType, PrefKey } from '@standardnotes/snjs' import moment from 'moment' import React, { useCallback, useMemo, useState } from 'react' @@ -182,6 +183,10 @@ export const OptionsSection = ({ title, encryptionAvailable }: Props) => { ) }, [application.alertService]) + const openWebApp = useCallback(() => { + navigation.push(SCREEN_WEB_APP) + }, [navigation]) + return ( @@ -221,6 +226,10 @@ export const OptionsSection = ({ title, encryptionAvailable }: Props) => { onPress={onExportPress} /> + {WebAppOptionEnabled && ( + openWebApp()} /> + )} + {!signedIn && (