Skip to content

Commit

Permalink
feat: handle basic routes (#1784)
Browse files Browse the repository at this point in the history
  • Loading branch information
moughxyz committed Oct 13, 2022
1 parent 794ed7f commit 3cb016a
Show file tree
Hide file tree
Showing 27 changed files with 391 additions and 140 deletions.
2 changes: 1 addition & 1 deletion packages/ui-services/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test": "jest spec --coverage --passWithNoTests"
"test": "jest spec"
},
"dependencies": {
"@standardnotes/common": "^1.39.0",
Expand Down
14 changes: 14 additions & 0 deletions packages/ui-services/src/Preferences/PreferenceId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const PREFERENCE_IDS = [
'general',
'account',
'security',
'appearance',
'backups',
'listed',
'shortcuts',
'accessibility',
'get-free-month',
'help-feedback',
] as const

export type PreferenceId = typeof PREFERENCE_IDS[number]
18 changes: 18 additions & 0 deletions packages/ui-services/src/Route/RouteParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { PreferenceId } from '../Preferences/PreferenceId'

export type OnboardingParams = {
fromHomepage: boolean
}

export type SettingsParams = {
panel: PreferenceId
}

export type DemoParams = {
token: string
}

export type PurchaseParams = {
plan: string
period: string
}
51 changes: 51 additions & 0 deletions packages/ui-services/src/Route/RouteParser.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { RouteParser } from './RouteParser'
import { RouteType } from './RouteType'

describe('route parser', () => {
it('routes to onboarding', () => {
const url = 'https://app.standardnotes.com/onboard?from_homepage=true'
const parser = new RouteParser(url)

expect(parser.type).toEqual(RouteType.Onboarding)
expect(parser.onboardingParams.fromHomepage).toEqual(true)
})

it('routes to demo', () => {
const url = 'https://app-demo.standardnotes.com/?demo-token=eyJhY2Nlc3NUb2tl'
const parser = new RouteParser(url)

expect(parser.type).toEqual(RouteType.Demo)
expect(parser.demoParams.token).toEqual('eyJhY2Nlc3NUb2tl')
})

it('routes to settings', () => {
const url = 'https://app.standardnotes.com/?settings=account'
const parser = new RouteParser(url)

expect(parser.type).toEqual(RouteType.Settings)
expect(parser.settingsParams.panel).toEqual('account')
})

it('routes to purchase', () => {
const url = 'https://app.standardnotes.com/?purchase=true&plan=PLUS_PLAN&period=year'
const parser = new RouteParser(url)

expect(parser.type).toEqual(RouteType.Purchase)
expect(parser.purchaseParams.period).toEqual('year')
expect(parser.purchaseParams.plan).toEqual('PLUS_PLAN')
})

it('routes to none', () => {
const url = 'https://app.standardnotes.com/unknown?foo=bar'
const parser = new RouteParser(url)

expect(parser.type).toEqual(RouteType.None)
})

it('accessing wrong params should throw', () => {
const url = 'https://app.standardnotes.com/item?uuid=123'
const parser = new RouteParser(url)

expect(() => parser.onboardingParams).toThrowError('Accessing invalid params')
})
})
88 changes: 88 additions & 0 deletions packages/ui-services/src/Route/RouteParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { PreferenceId } from './../Preferences/PreferenceId'
import { DemoParams, OnboardingParams, PurchaseParams, SettingsParams } from './RouteParams'
import { RouteType } from './RouteType'

enum RootRoutes {
Onboarding = '/onboard',
None = '/',
}

enum RootQueryParam {
Purchase = 'purchase',
Settings = 'settings',
DemoToken = 'demo-token',
}

export class RouteParser {
private url: URL
private readonly path: string
public readonly type: RouteType
private readonly searchParams: URLSearchParams

constructor(url: string) {
this.url = new URL(url)
this.path = this.url.pathname
this.searchParams = this.url.searchParams

const pathUsesRootQueryParams = this.path === RootRoutes.None

if (pathUsesRootQueryParams) {
if (this.searchParams.has(RootQueryParam.Purchase)) {
this.type = RouteType.Purchase
} else if (this.searchParams.has(RootQueryParam.Settings)) {
this.type = RouteType.Settings
} else if (this.searchParams.has(RootQueryParam.DemoToken)) {
this.type = RouteType.Demo
} else {
this.type = RouteType.None
}
} else {
if (this.path === RootRoutes.Onboarding) {
this.type = RouteType.Onboarding
} else {
this.type = RouteType.None
}
}
}

get demoParams(): DemoParams {
if (this.type !== RouteType.Demo) {
throw new Error('Accessing invalid params')
}

return {
token: this.searchParams.get(RootQueryParam.DemoToken) as string,
}
}

get settingsParams(): SettingsParams {
if (this.type !== RouteType.Settings) {
throw new Error('Accessing invalid params')
}

return {
panel: this.searchParams.get(RootQueryParam.Settings) as PreferenceId,
}
}

get purchaseParams(): PurchaseParams {
if (this.type !== RouteType.Purchase) {
throw new Error('Accessing invalid params')
}

return {
plan: this.searchParams.get('plan') as string,
period: this.searchParams.get('period') as string,
}
}

get onboardingParams(): OnboardingParams {
if (this.type !== RouteType.Onboarding) {
throw new Error('Accessing invalid params')
}

return {
fromHomepage: !!this.searchParams.get('from_homepage'),
}
}
}
50 changes: 50 additions & 0 deletions packages/ui-services/src/Route/RouteService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
AbstractService,
ApplicationEvent,
ApplicationInterface,
InternalEventBusInterface,
} from '@standardnotes/services'
import { RouteParser } from './RouteParser'
import { RouteServiceEvent } from './RouteServiceEvent'

export class RouteService extends AbstractService<RouteServiceEvent, RouteParser> {
private unsubApp!: () => void

constructor(
private application: ApplicationInterface,
protected override internalEventBus: InternalEventBusInterface,
) {
super(internalEventBus)
this.addAppEventObserver()
}

override deinit() {
super.deinit()
;(this.application as unknown) = undefined
this.unsubApp()
}

public getRoute(): RouteParser {
return new RouteParser(window.location.href)
}

public removeSettingsFromURLQueryParameters() {
const urlSearchParams = new URLSearchParams(window.location.search)
urlSearchParams.delete('settings')

const newUrl = `${window.location.origin}${window.location.pathname}${urlSearchParams.toString()}`
window.history.replaceState(null, document.title, newUrl)
}

private addAppEventObserver() {
this.unsubApp = this.application.addEventObserver(async (event: ApplicationEvent) => {
if (event === ApplicationEvent.LocalDataLoaded) {
void this.notifyRouteChange()
}
})
}

private notifyRouteChange() {
void this.notifyEvent(RouteServiceEvent.RouteChanged, this.getRoute())
}
}
3 changes: 3 additions & 0 deletions packages/ui-services/src/Route/RouteServiceEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export enum RouteServiceEvent {
RouteChanged = 'route-changed',
}
7 changes: 7 additions & 0 deletions packages/ui-services/src/Route/RouteType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export enum RouteType {
Onboarding = 'onboarding',
Settings = 'settings',
Purchase = 'purchase',
Demo = 'demo',
None = 'none',
}
6 changes: 6 additions & 0 deletions packages/ui-services/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ export * from './Alert/Functions'
export * from './Alert/WebAlertService'
export * from './Archive/ArchiveManager'
export * from './IO/IOService'
export * from './Preferences/PreferenceId'
export * from './Route/RouteParams'
export * from './Route/RouteParser'
export * from './Route/RouteType'
export * from './Route/RouteService'
export * from './Route/RouteServiceEvent'
export * from './Security/AutolockService'
export * from './Storage/LocalStorage'
export * from './Theme/ThemeManager'
39 changes: 4 additions & 35 deletions packages/web/src/javascripts/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
'use strict'

import { disableIosTextFieldZoom, isDev } from '@/Utils'

declare global {
interface Window {
dashboardUrl?: string
Expand All @@ -25,8 +23,9 @@ declare global {
}
}

import { disableIosTextFieldZoom } from '@/Utils'
import { IsWebPlatform, WebAppVersion } from '@/Constants/Version'
import { DesktopManagerInterface, Environment, Platform, SNLog } from '@standardnotes/snjs'
import { DesktopManagerInterface, Environment, SNLog } from '@standardnotes/snjs'
import ApplicationGroupView from './Components/ApplicationGroupView/ApplicationGroupView'
import { WebDevice } from './Application/Device/WebDevice'
import { StartApplication } from './Application/Device/StartApplication'
Expand All @@ -36,44 +35,14 @@ import { WebApplication } from './Application/Application'
import { createRoot, Root } from 'react-dom/client'
import { ElementIds } from './Constants/ElementIDs'
import { MediaQueryBreakpoints } from './Hooks/useMediaQuery'
import { setViewportHeightWithFallback } from './setViewportHeightWithFallback'
import { setDefaultMonospaceFont } from './setDefaultMonospaceFont'

let keyCount = 0
const getKey = () => {
return keyCount++
}

const ViewportHeightKey = '--viewport-height'

export const setViewportHeightWithFallback = () => {
const currentHeight = parseInt(document.documentElement.style.getPropertyValue(ViewportHeightKey))
const newValue = visualViewport && visualViewport.height > 0 ? visualViewport.height : window.innerHeight

if (isDev) {
// eslint-disable-next-line no-console
console.log(`currentHeight: ${currentHeight}, newValue: ${newValue}`)
}

if (currentHeight && newValue < currentHeight) {
return
}

if (!newValue) {
document.documentElement.style.setProperty(ViewportHeightKey, '100vh')
return
}

document.documentElement.style.setProperty(ViewportHeightKey, `${newValue}px`)
}

const setDefaultMonospaceFont = (platform?: Platform) => {
if (platform === Platform.Android) {
document.documentElement.style.setProperty(
'--sn-stylekit-monospace-font',
'"Roboto Mono", "Droid Sans Mono", monospace',
)
}
}

const startApplication: StartApplication = async function startApplication(
defaultSyncServerHost: string,
device: WebOrDesktopDevice,
Expand Down

0 comments on commit 3cb016a

Please sign in to comment.