Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7892085
feat: onShouldStartLoadWithRequest
amanharwara Oct 21, 2022
dc06f76
feat: component urls
amanharwara Oct 21, 2022
9cab40f
Merge branch 'main' of github.com:standardnotes/app into fix/editor-l…
amanharwara Oct 21, 2022
2bd5ab0
fix: test
amanharwara Oct 21, 2022
beba958
chore: remove comment
amanharwara Oct 21, 2022
e325bc2
Merge branch 'main' into fix/editor-links
amanharwara Oct 23, 2022
7e6424e
Merge branch 'main' into fix/editor-links
amanharwara Oct 23, 2022
e801f58
Merge branch 'main' into fix/editor-links
amanharwara Oct 23, 2022
1e107e6
Merge branch 'main' into fix/editor-links
amanharwara Oct 23, 2022
7bdc96a
Merge branch 'main' into fix/editor-links
amanharwara Oct 23, 2022
14453fd
Merge branch 'main' into fix/editor-links
amanharwara Oct 23, 2022
151b65a
refactor: move to componentManager
amanharwara Oct 24, 2022
688e9ac
refactor: types
amanharwara Oct 24, 2022
610b1c9
fix: e2e test
amanharwara Oct 24, 2022
687d6eb
Merge branch 'main' into fix/editor-links
amanharwara Oct 24, 2022
5f88bd8
fix: tests
amanharwara Oct 24, 2022
03af467
Merge branch 'main' into fix/editor-links
amanharwara Oct 24, 2022
ab0d666
Merge branch 'main' into fix/editor-links
amanharwara Oct 24, 2022
c2342d5
Merge branch 'main' into fix/editor-links
amanharwara Oct 24, 2022
cf5e8e9
refactor: remove mobile web environment type
moughxyz Oct 24, 2022
6e3f1e4
Merge branch 'main' into fix/editor-links
amanharwara Oct 24, 2022
3f77992
Merge branch 'main' into fix/editor-links
amanharwara Oct 24, 2022
db4c59e
refactor: change mobile environment name to account for component-rel…
moughxyz Oct 24, 2022
a73f6a1
Merge github.com:standardnotes/app into fix/editor-links
moughxyz Oct 24, 2022
3a141de
Merge branch 'fix/editor-links' of github.com:standardnotes/app into …
moughxyz Oct 24, 2022
a8f5957
Merge branch 'main' into fix/editor-links
amanharwara Oct 24, 2022
c5b7e8f
fix: prettier
amanharwara Oct 25, 2022
7f7bbc2
Merge branch 'main' of github.com:standardnotes/app into fix/editor-l…
amanharwara Oct 25, 2022
1944694
Merge branch 'main' into fix/editor-links
amanharwara Oct 25, 2022
a889af9
fix: isComponentUrl
amanharwara Oct 25, 2022
40505cf
fix: test
amanharwara Oct 25, 2022
648c370
fix: test
amanharwara Oct 25, 2022
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
2 changes: 1 addition & 1 deletion packages/mobile/WebFrame/DeviceInterface.template.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
class WebProcessDeviceInterface {
constructor(messageSender) {
this.appVersion = '1.2.3'
this.environment = 4
this.environment = 3
this.databases = []
this.messageSender = messageSender
}
Expand Down
14 changes: 14 additions & 0 deletions packages/mobile/src/Lib/Interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
RawKeychainValue,
removeFromArray,
TransferPayload,
UuidString,
} from '@standardnotes/snjs'
import { Alert, Linking, PermissionsAndroid, Platform, StatusBar } from 'react-native'
import FileViewer from 'react-native-file-viewer'
Expand Down Expand Up @@ -79,6 +80,7 @@ export class MobileDevice implements MobileDeviceInterface {
private eventObservers: MobileDeviceEventHandler[] = []
public isDarkMode = false
public statusBarBgColor: string | undefined
private componentUrls: Map<UuidString, string> = new Map()

constructor(
private stateObserverService?: AppStateObserverService,
Expand Down Expand Up @@ -596,4 +598,16 @@ export class MobileDevice implements MobileDeviceInterface {
},
)
}

addComponentUrl(componentUuid: UuidString, componentUrl: string) {
this.componentUrls.set(componentUuid, componentUrl)
}

removeComponentUrl(componentUuid: UuidString) {
this.componentUrls.delete(componentUuid)
}

isUrlComponentUrl(url: string): boolean {
return Array.from(this.componentUrls.values()).includes(url)
}
}
33 changes: 32 additions & 1 deletion packages/mobile/src/MobileWebAppContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Keyboard, Platform } from 'react-native'
import VersionInfo from 'react-native-version-info'
import { WebView, WebViewMessageEvent } from 'react-native-webview'
import { OnShouldStartLoadWithRequest } from 'react-native-webview/lib/WebViewTypes'
import pjson from '../package.json'
import { AndroidBackHandlerService } from './AndroidBackHandlerService'
import { AppStateObserverService } from './AppStateObserverService'
Expand All @@ -23,6 +24,7 @@ export const MobileWebAppContainer = () => {

const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => void }) => {
const webViewRef = useRef<WebView>(null)

const sourceUri = (Platform.OS === 'android' ? 'file:///android_asset/' : '') + 'Web.bundle/src/index.html'
const stateService = useMemo(() => new AppStateObserverService(), [])
const androidBackHandlerService = useMemo(() => new AndroidBackHandlerService(), [])
Expand Down Expand Up @@ -99,7 +101,7 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
class WebProcessDeviceInterface {
constructor(messageSender) {
this.appVersion = '${pjson.version} (${VersionInfo.buildVersion})'
this.environment = 4
this.environment = 3
this.platform = ${device.platform}
this.databases = []
this.messageSender = messageSender
Expand Down Expand Up @@ -195,6 +197,34 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
webViewRef.current?.postMessage(JSON.stringify({ messageId, returnValue, messageType: 'reply' }))
}

const onShouldStartLoadWithRequest: OnShouldStartLoadWithRequest = (request) => {
/**
* We want to handle link clicks within an editor by opening the browser
* instead of loading inline. On iOS, onShouldStartLoadWithRequest is
* called for all requests including the initial request to load the editor.
* On iOS, clicks in the editors have a navigationType of 'click', but on
* Android, this is not the case (no navigationType).
* However, on Android, this function is not called for the initial request.
* So that might be one way to determine if this request is a click or the
* actual editor load request. But I don't think it's safe to rely on this
* being the case in the future. So on Android, we'll handle url loads only
* if the url isn't equal to the editor url.
*/

const shouldStopRequest =
(Platform.OS === 'ios' && request.navigationType === 'click') ||
(Platform.OS === 'android' && request.url !== sourceUri)

const isComponentUrl = device.isUrlComponentUrl(request.url)

if (shouldStopRequest && !isComponentUrl) {
device.openUrl(request.url)
return false
}

return true
}

return (
<WebView
ref={webViewRef}
Expand All @@ -210,6 +240,7 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
onRenderProcessGone={() => {
webViewRef.current?.reload()
}}
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
allowFileAccess={true}
allowUniversalAccessFromFileURLs={true}
injectedJavaScriptBeforeContentLoaded={injectedJS}
Expand Down
1 change: 0 additions & 1 deletion packages/models/src/Domain/Device/Environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@ export enum Environment {
Web = 1,
Desktop = 2,
Mobile = 3,
NativeMobileWeb = 4,
}
3 changes: 3 additions & 0 deletions packages/services/src/Domain/Device/MobileDeviceInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@ export interface MobileDeviceInterface extends DeviceInterface {
downloadBase64AsFile(base64: string, filename: string, saveInTempLocation?: boolean): Promise<string | undefined>
previewFile(base64: string, filename: string): Promise<boolean>
exitApp(confirm?: boolean): void
addComponentUrl(componentUuid: string, componentUrl: string): void
removeComponentUrl(componentUuid: string): void
isUrlComponentUrl(url: string): boolean
}
4 changes: 4 additions & 0 deletions packages/snjs/lib/Application/Application.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/**
* @jest-environment jsdom
*/

import { SNLog } from './../Log'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { AlertService, DeviceInterface, namespacedKey, RawStorageKey } from '@standardnotes/services'
Expand Down
17 changes: 3 additions & 14 deletions packages/snjs/lib/Application/Application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -940,7 +940,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
}

isNativeMobileWeb() {
return this.environment === Environment.NativeMobileWeb
return this.environment === Environment.Mobile
}

getDeinitMode(): DeinitMode {
Expand Down Expand Up @@ -1353,10 +1353,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
}

private createComponentManager() {
const MaybeSwappedComponentManager = this.getClass<typeof InternalServices.SNComponentManager>(
InternalServices.SNComponentManager,
)
this.componentManagerService = new MaybeSwappedComponentManager(
this.componentManagerService = new InternalServices.SNComponentManager(
this.itemManager,
this.syncService,
this.featuresService,
Expand All @@ -1365,6 +1362,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
this.environment,
this.platform,
this.internalEventBus,
this.deviceInterface,
)
this.services.push(this.componentManagerService)
}
Expand Down Expand Up @@ -1647,13 +1645,4 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
this.statusService = new ExternalServices.StatusService(this.internalEventBus)
this.services.push(this.statusService)
}

private getClass<T>(base: T) {
const swapClass = this.options.swapClasses?.find((candidate) => candidate.swap === base)
if (swapClass) {
return swapClass.with as T
} else {
return base
}
}
}
8 changes: 0 additions & 8 deletions packages/snjs/lib/Application/Options/OptionalOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,6 @@ export interface ApplicationDisplayOptions {
}

export interface ApplicationOptionalConfiguratioOptions {
/**
* Gives consumers the ability to provide their own custom
* subclass for a service. swapClasses should be an array of key/value pairs
* consisting of keys 'swap' and 'with'. 'swap' is the base class you wish to replace,
* and 'with' is the custom subclass to use.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
swapClasses?: { swap: any; with: any }[]
/**
* URL for WebSocket providing permissions and roles information.
*/
Expand Down
13 changes: 1 addition & 12 deletions packages/snjs/lib/Application/Platforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,11 @@ export function platformToString(platform: Platform) {
return map[platform]
}

export function environmentFromString(string: string) {
const map: Record<string, Environment> = {
web: Environment.Web,
desktop: Environment.Desktop,
mobile: Environment.Mobile,
nativeMobileWeb: Environment.NativeMobileWeb,
}
return map[string]
}

export function environmentToString(environment: Environment) {
const map = {
[Environment.Web]: 'web',
[Environment.Desktop]: 'desktop',
[Environment.Mobile]: 'mobile',
[Environment.NativeMobileWeb]: 'native-mobile-web',
[Environment.Mobile]: 'native-mobile-web',
}
return map[environment]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ import {
} from '@standardnotes/features'
import { ContentType } from '@standardnotes/common'
import { GenericItem, SNComponent, Environment, Platform } from '@standardnotes/models'
import { DesktopManagerInterface, InternalEventBusInterface, AlertService } from '@standardnotes/services'
import {
DesktopManagerInterface,
InternalEventBusInterface,
AlertService,
DeviceInterface,
} from '@standardnotes/services'
import { ItemManager } from '@Lib/Services/Items/ItemManager'
import { SNFeaturesService } from '@Lib/Services/Features/FeaturesService'
import { SNComponentManager } from './ComponentManager'
Expand All @@ -27,6 +32,7 @@ describe('featuresService', () => {
let syncService: SNSyncService
let prefsService: SNPreferencesService
let internalEventBus: InternalEventBusInterface
let device: DeviceInterface

const desktopExtHost = 'http://localhost:123'

Expand All @@ -53,6 +59,7 @@ describe('featuresService', () => {
environment,
platform,
internalEventBus,
device,
)
manager.setDesktopManager(desktopManager)
return manager
Expand Down Expand Up @@ -81,6 +88,8 @@ describe('featuresService', () => {

internalEventBus = {} as jest.Mocked<InternalEventBusInterface>
internalEventBus.publish = jest.fn()

device = {} as jest.Mocked<DeviceInterface>
})

const nativeComponent = (identifier?: FeatureIdentifier, file_type?: FeatureDescription['file_type']) => {
Expand Down
49 changes: 28 additions & 21 deletions packages/snjs/lib/Services/ComponentManager/ComponentManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import {
DesktopManagerInterface,
InternalEventBusInterface,
AlertService,
DeviceInterface,
isMobileDevice,
} from '@standardnotes/services'

const DESKTOP_URL_PREFIX = 'sn://'
Expand Down Expand Up @@ -82,23 +84,21 @@ export class SNComponentManager
private environment: Environment,
private platform: Platform,
protected override internalEventBus: InternalEventBusInterface,
private device: DeviceInterface,
) {
super(internalEventBus)
this.loggingEnabled = false

this.addItemObserver()

/* On mobile, events listeners are handled by a respective component */
if (environment !== Environment.Mobile) {
window.addEventListener
? window.addEventListener('focus', this.detectFocusChange, true)
: window.attachEvent('onfocusout', this.detectFocusChange)
window.addEventListener
? window.addEventListener('blur', this.detectFocusChange, true)
: window.attachEvent('onblur', this.detectFocusChange)
window.addEventListener
? window.addEventListener('focus', this.detectFocusChange, true)
: window.attachEvent('onfocusout', this.detectFocusChange)
window.addEventListener
? window.addEventListener('blur', this.detectFocusChange, true)
: window.attachEvent('onblur', this.detectFocusChange)

window.addEventListener('message', this.onWindowMessage, true)
}
window.addEventListener('message', this.onWindowMessage, true)
}

get isDesktop(): boolean {
Expand Down Expand Up @@ -143,7 +143,7 @@ export class SNComponentManager
this.removeItemObserver?.()
;(this.removeItemObserver as unknown) = undefined

if (window && !this.isMobile) {
if (window) {
window.removeEventListener('focus', this.detectFocusChange, true)
window.removeEventListener('blur', this.detectFocusChange, true)
window.removeEventListener('message', this.onWindowMessage, true)
Expand Down Expand Up @@ -221,9 +221,23 @@ export class SNComponentManager
addItemObserver(): void {
this.removeItemObserver = this.itemManager.addObserver<SNComponent>(
[ContentType.Component, ContentType.Theme],
({ changed, inserted, source }) => {
({ changed, inserted, removed, source }) => {
const items = [...changed, ...inserted]
this.handleChangedComponents(items, source)

const device = this.device
if (isMobileDevice(device) && 'addComponentUrl' in device) {
inserted.forEach((component) => {
const url = this.urlForComponent(component)
if (url) {
device.addComponentUrl(component.uuid, url)
}
})

removed.forEach((component) => {
device.removeComponentUrl(component.uuid)
})
}
},
)
}
Expand Down Expand Up @@ -271,9 +285,6 @@ export class SNComponentManager
}

getActiveThemes(): SNTheme[] {
if (this.environment === Environment.Mobile) {
throw Error('getActiveThemes must be handled separately by mobile')
}
return this.componentsForArea(ComponentArea.Themes).filter((theme) => {
return theme.active
}) as SNTheme[]
Expand Down Expand Up @@ -301,14 +312,10 @@ export class SNComponentManager
}
}

const isWeb = this.environment === Environment.Web
const isMobileWebView = this.environment === Environment.NativeMobileWeb
const isMobile = this.environment === Environment.Mobile
if (nativeFeature) {
if (!isWeb && !isMobileWebView) {
throw Error('Mobile must override urlForComponent to handle native paths')
}
let baseUrlRequiredForThemesInsideEditors = window.location.origin
if (isMobileWebView) {
if (isMobile) {
baseUrlRequiredForThemesInsideEditors = window.location.href.split('/index.html')[0]
}
return `${baseUrlRequiredForThemesInsideEditors}/components/assets/${component.identifier}/${nativeFeature.index_path}`
Expand Down
2 changes: 1 addition & 1 deletion packages/snjs/lib/Services/Storage/DiskStorageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export class DiskStorageService extends Services.AbstractService implements Serv
public setEncryptionPolicy(encryptionPolicy: Services.StorageEncryptionPolicy, persist = true): void {
if (
encryptionPolicy === Services.StorageEncryptionPolicy.Disabled &&
![Environment.Mobile, Environment.NativeMobileWeb].includes(this.environment)
![Environment.Mobile].includes(this.environment)
) {
throw Error('Disabling storage encryption is only available on mobile.')
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class MobileWebReceiver {
this.handleNativeEvent(nativeEvent)
}
} catch (error) {
console.log('Error parsing message from React Native', error)
console.log('[MobileWebReceiver] Error parsing message from React Native', error)
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/javascripts/Utils/ManageSubscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export async function openSubscriptionDashboard(application: SNApplication) {

const url = `${window.dashboardUrl}?subscription_token=${token}`

if (application.deviceInterface.environment === Environment.NativeMobileWeb) {
if (application.deviceInterface.environment === Environment.Mobile) {
application.deviceInterface.openUrl(url)
return
}
Expand Down