Sync browser navigation with app
parent
dcd396153c
commit
55a8a8fa4c
|
@ -16,6 +16,7 @@ function App() {
|
||||||
view.setup()
|
view.setup()
|
||||||
setupState().then(store => {
|
setupState().then(store => {
|
||||||
setRootStore(store)
|
setRootStore(store)
|
||||||
|
store.nav.bindWebNavigation()
|
||||||
getInitialURL().then(url => {
|
getInitialURL().then(url => {
|
||||||
if (url) {
|
if (url) {
|
||||||
store.nav.handleLink(url)
|
store.nav.handleLink(url)
|
||||||
|
|
|
@ -7,6 +7,7 @@ const segmentClient = createClient({
|
||||||
writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI',
|
writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI',
|
||||||
trackAppLifecycleEvents: false,
|
trackAppLifecycleEvents: false,
|
||||||
})
|
})
|
||||||
|
export const track = segmentClient.track
|
||||||
|
|
||||||
export {useAnalytics} from '@segment/analytics-react-native'
|
export {useAnalytics} from '@segment/analytics-react-native'
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ const _analytics = {
|
||||||
screen(_name: string) {},
|
screen(_name: string) {},
|
||||||
track(_name: string, _opts: any) {},
|
track(_name: string, _opts: any) {},
|
||||||
}
|
}
|
||||||
|
export const track = _analytics.track
|
||||||
export function useAnalytics() {
|
export function useAnalytics() {
|
||||||
return _analytics
|
return _analytics
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {Linking} from 'react-native'
|
import {Linking} from 'react-native'
|
||||||
import {createBrowserHistory, createMemoryHistory} from 'history'
|
|
||||||
import {isNative, isWeb} from './detection'
|
import {isNative, isWeb} from './detection'
|
||||||
|
|
||||||
export async function getInitialURL(): Promise<string | undefined> {
|
export async function getInitialURL(): Promise<string | undefined> {
|
||||||
|
@ -24,11 +23,3 @@ export function clearHash() {
|
||||||
window.location.hash = ''
|
window.location.hash = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getHistory() {
|
|
||||||
if (isWeb) {
|
|
||||||
return createBrowserHistory()
|
|
||||||
} else {
|
|
||||||
return createMemoryHistory()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import {RootStoreModel} from './root-store'
|
import {RootStoreModel} from './root-store'
|
||||||
import {makeAutoObservable} from 'mobx'
|
import {makeAutoObservable} from 'mobx'
|
||||||
import {TABS_ENABLED} from 'lib/build-flags'
|
import {TABS_ENABLED} from 'lib/build-flags'
|
||||||
import {segmentClient} from 'lib/analytics'
|
import * as analytics from 'lib/analytics'
|
||||||
import {getHistory} from 'platform/urls'
|
import {isNative} from 'platform/detection'
|
||||||
|
|
||||||
let __id = 0
|
let __id = 0
|
||||||
function genId() {
|
function genId() {
|
||||||
|
@ -42,7 +42,6 @@ export type HistoryPtr = string // `{tabId}-{historyId}`
|
||||||
export class NavigationTabModel {
|
export class NavigationTabModel {
|
||||||
id = genId()
|
id = genId()
|
||||||
history: HistoryItem[]
|
history: HistoryItem[]
|
||||||
browserHistory: any
|
|
||||||
index = 0
|
index = 0
|
||||||
isNewTab = false
|
isNewTab = false
|
||||||
|
|
||||||
|
@ -50,13 +49,11 @@ export class NavigationTabModel {
|
||||||
this.history = [
|
this.history = [
|
||||||
{url: TabPurposeMainPath[fixedTabPurpose], ts: Date.now(), id: genId()},
|
{url: TabPurposeMainPath[fixedTabPurpose], ts: Date.now(), id: genId()},
|
||||||
]
|
]
|
||||||
this.browserHistory = getHistory()
|
|
||||||
makeAutoObservable(this, {
|
makeAutoObservable(this, {
|
||||||
serialize: false,
|
serialize: false,
|
||||||
hydrate: false,
|
hydrate: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// accessors
|
// accessors
|
||||||
// =
|
// =
|
||||||
|
|
||||||
|
@ -108,7 +105,7 @@ export class NavigationTabModel {
|
||||||
navigate(url: string, title?: string) {
|
navigate(url: string, title?: string) {
|
||||||
try {
|
try {
|
||||||
const path = url.split('/')[1]
|
const path = url.split('/')[1]
|
||||||
segmentClient.track('Navigation', {
|
analytics.track('Navigation', {
|
||||||
path,
|
path,
|
||||||
})
|
})
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
|
@ -125,8 +122,10 @@ export class NavigationTabModel {
|
||||||
this.history.push({url: fixedUrl, ts: Date.now(), id: genId()})
|
this.history.push({url: fixedUrl, ts: Date.now(), id: genId()})
|
||||||
}
|
}
|
||||||
this.history.push({url, title, ts: Date.now(), id: genId()})
|
this.history.push({url, title, ts: Date.now(), id: genId()})
|
||||||
this.browserHistory.push(url)
|
|
||||||
this.index = this.history.length - 1
|
this.index = this.history.length - 1
|
||||||
|
if (!isNative) {
|
||||||
|
window.history.pushState({hindex: this.index, hurl: url}, '', url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +145,9 @@ export class NavigationTabModel {
|
||||||
goBack() {
|
goBack() {
|
||||||
if (this.canGoBack) {
|
if (this.canGoBack) {
|
||||||
this.index--
|
this.index--
|
||||||
this.browserHistory.back()
|
if (!isNative) {
|
||||||
|
window.history.back()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,14 +161,19 @@ export class NavigationTabModel {
|
||||||
goForward() {
|
goForward() {
|
||||||
if (this.canGoForward) {
|
if (this.canGoForward) {
|
||||||
this.index++
|
this.index++
|
||||||
this.browserHistory.forward()
|
if (!isNative) {
|
||||||
|
window.history.forward()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
goToIndex(index: number) {
|
goToIndex(index: number) {
|
||||||
if (index >= 0 && index <= this.history.length - 1) {
|
if (index >= 0 && index <= this.history.length - 1) {
|
||||||
|
const delta = index - this.index
|
||||||
this.index = index
|
this.index = index
|
||||||
this.browserHistory.go(index)
|
if (!isNative) {
|
||||||
|
window.history.go(delta)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,6 +190,15 @@ export class NavigationTabModel {
|
||||||
this.isNewTab = v
|
this.isNewTab = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// browser only
|
||||||
|
// =
|
||||||
|
|
||||||
|
resetTo(url: string) {
|
||||||
|
this.index = 0
|
||||||
|
this.history.push({url, title: '', ts: Date.now(), id: genId()})
|
||||||
|
this.index = this.history.length - 1
|
||||||
|
}
|
||||||
|
|
||||||
// persistence
|
// persistence
|
||||||
// =
|
// =
|
||||||
|
|
||||||
|
@ -229,11 +244,13 @@ export class NavigationTabModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NavigationModel {
|
export class NavigationModel {
|
||||||
tabs: NavigationTabModel[] = [
|
tabs: NavigationTabModel[] = isNative
|
||||||
new NavigationTabModel(TabPurpose.Default),
|
? [
|
||||||
new NavigationTabModel(TabPurpose.Search),
|
new NavigationTabModel(TabPurpose.Default),
|
||||||
new NavigationTabModel(TabPurpose.Notifs),
|
new NavigationTabModel(TabPurpose.Search),
|
||||||
]
|
new NavigationTabModel(TabPurpose.Notifs),
|
||||||
|
]
|
||||||
|
: [new NavigationTabModel(TabPurpose.Default)]
|
||||||
tabIndex = 0
|
tabIndex = 0
|
||||||
|
|
||||||
constructor(public rootStore: RootStoreModel) {
|
constructor(public rootStore: RootStoreModel) {
|
||||||
|
@ -244,12 +261,39 @@ export class NavigationModel {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used only in the web build to sync with browser history state
|
||||||
|
*/
|
||||||
|
bindWebNavigation() {
|
||||||
|
if (!isNative) {
|
||||||
|
window.addEventListener('popstate', e => {
|
||||||
|
const {hindex, hurl} = e.state
|
||||||
|
if (hindex >= 0 && hindex <= this.tab.history.length - 1) {
|
||||||
|
this.tab.index = hindex
|
||||||
|
}
|
||||||
|
if (this.tab.current.url !== hurl) {
|
||||||
|
// desynced because they went back to an old tab session-
|
||||||
|
// do a reset to match that
|
||||||
|
this.tab.resetTo(hurl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
if (this.tab.current.url !== window.location.pathname) {
|
||||||
|
// state has completely desynced, reload
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.tabs = [
|
this.tabs = isNative
|
||||||
new NavigationTabModel(TabPurpose.Default),
|
? [
|
||||||
new NavigationTabModel(TabPurpose.Search),
|
new NavigationTabModel(TabPurpose.Default),
|
||||||
new NavigationTabModel(TabPurpose.Notifs),
|
new NavigationTabModel(TabPurpose.Search),
|
||||||
]
|
new NavigationTabModel(TabPurpose.Notifs),
|
||||||
|
]
|
||||||
|
: [new NavigationTabModel(TabPurpose.Default)]
|
||||||
this.tabIndex = 0
|
this.tabIndex = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,7 +416,7 @@ export class NavigationModel {
|
||||||
hydrate(_v: unknown) {
|
hydrate(_v: unknown) {
|
||||||
// TODO fixme
|
// TODO fixme
|
||||||
this.clear()
|
this.clear()
|
||||||
/*if (isObj(v)) {
|
/*if (isObj(v)) {
|
||||||
if (hasProp(v, 'tabs') && Array.isArray(v.tabs)) {
|
if (hasProp(v, 'tabs') && Array.isArray(v.tabs)) {
|
||||||
for (const tab of v.tabs) {
|
for (const tab of v.tabs) {
|
||||||
const copy = new NavigationTabModel()
|
const copy = new NavigationTabModel()
|
||||||
|
|
Loading…
Reference in New Issue