Improve reliability of screen titles
parent
b2160ae159
commit
a3bca154c4
|
@ -1,20 +1,23 @@
|
||||||
import {makeAutoObservable} from 'mobx'
|
import {makeAutoObservable} from 'mobx'
|
||||||
import {isObj, hasProp} from '../lib/type-guards'
|
import {isObj, hasProp} from '../lib/type-guards'
|
||||||
|
|
||||||
let __tabId = 0
|
let __id = 0
|
||||||
function genTabId() {
|
function genId() {
|
||||||
return ++__tabId
|
return ++__id
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HistoryItem {
|
interface HistoryItem {
|
||||||
url: string
|
url: string
|
||||||
ts: number
|
ts: number
|
||||||
title?: string
|
title?: string
|
||||||
|
id: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type HistoryPtr = [number, number]
|
||||||
|
|
||||||
export class NavigationTabModel {
|
export class NavigationTabModel {
|
||||||
id = genTabId()
|
id = genId()
|
||||||
history: HistoryItem[] = [{url: '/', ts: Date.now()}]
|
history: HistoryItem[] = [{url: '/', ts: Date.now(), id: genId()}]
|
||||||
index = 0
|
index = 0
|
||||||
isNewTab = false
|
isNewTab = false
|
||||||
|
|
||||||
|
@ -47,6 +50,7 @@ export class NavigationTabModel {
|
||||||
url: item.url,
|
url: item.url,
|
||||||
title: item.title,
|
title: item.title,
|
||||||
index: start + i,
|
index: start + i,
|
||||||
|
id: item.id,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +65,7 @@ export class NavigationTabModel {
|
||||||
url: item.url,
|
url: item.url,
|
||||||
title: item.title,
|
title: item.title,
|
||||||
index: start + i,
|
index: start + i,
|
||||||
|
id: item.id,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +83,7 @@ export class NavigationTabModel {
|
||||||
if (this.index < this.history.length - 1) {
|
if (this.index < this.history.length - 1) {
|
||||||
this.history.length = this.index + 1
|
this.history.length = this.index + 1
|
||||||
}
|
}
|
||||||
this.history.push({url, title, ts: Date.now()})
|
this.history.push({url, title, ts: Date.now(), id: genId()})
|
||||||
this.index = this.history.length - 1
|
this.index = this.history.length - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +91,12 @@ export class NavigationTabModel {
|
||||||
refresh() {
|
refresh() {
|
||||||
this.history = [
|
this.history = [
|
||||||
...this.history.slice(0, this.index),
|
...this.history.slice(0, this.index),
|
||||||
{url: this.current.url, title: this.current.title, ts: Date.now()},
|
{
|
||||||
|
url: this.current.url,
|
||||||
|
title: this.current.title,
|
||||||
|
ts: Date.now(),
|
||||||
|
id: this.current.id,
|
||||||
|
},
|
||||||
...this.history.slice(this.index + 1),
|
...this.history.slice(this.index + 1),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -109,8 +119,13 @@ export class NavigationTabModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setTitle(title: string) {
|
setTitle(id: number, title: string) {
|
||||||
this.current.title = title
|
this.history = this.history.map(h => {
|
||||||
|
if (h.id === id) {
|
||||||
|
return {...h, title}
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsNewTab(v: boolean) {
|
setIsNewTab(v: boolean) {
|
||||||
|
@ -203,8 +218,8 @@ export class NavigationModel {
|
||||||
this.tab.refresh()
|
this.tab.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
setTitle(title: string) {
|
setTitle(ptr: HistoryPtr, title: string) {
|
||||||
this.tab.setTitle(title)
|
this.tabs.find(t => t.id === ptr[0])?.setTitle(ptr[1], title)
|
||||||
}
|
}
|
||||||
|
|
||||||
// tab management
|
// tab management
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {ProfileMembers} from './screens/ProfileMembers'
|
||||||
import {Settings} from './screens/Settings'
|
import {Settings} from './screens/Settings'
|
||||||
|
|
||||||
export type ScreenParams = {
|
export type ScreenParams = {
|
||||||
|
navIdx: [number, number]
|
||||||
params: Record<string, any>
|
params: Record<string, any>
|
||||||
visible: boolean
|
visible: boolean
|
||||||
scrollElRef?: MutableRefObject<FlatList<any> | undefined>
|
scrollElRef?: MutableRefObject<FlatList<any> | undefined>
|
||||||
|
|
|
@ -8,13 +8,13 @@ import {colors} from '../lib/styles'
|
||||||
import {ScreenParams} from '../routes'
|
import {ScreenParams} from '../routes'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
|
|
||||||
export const Contacts = ({visible, params}: ScreenParams) => {
|
export const Contacts = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const selectorInterp = useSharedValue(0)
|
const selectorInterp = useSharedValue(0)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
store.nav.setTitle(`Contacts`)
|
store.nav.setTitle(navIdx, `Contacts`)
|
||||||
}
|
}
|
||||||
}, [store, visible])
|
}, [store, visible])
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {ScreenParams} from '../routes'
|
||||||
import {s, colors} from '../lib/styles'
|
import {s, colors} from '../lib/styles'
|
||||||
|
|
||||||
export const Home = observer(function Home({
|
export const Home = observer(function Home({
|
||||||
|
navIdx,
|
||||||
visible,
|
visible,
|
||||||
scrollElRef,
|
scrollElRef,
|
||||||
}: ScreenParams) {
|
}: ScreenParams) {
|
||||||
|
@ -51,7 +52,7 @@ export const Home = observer(function Home({
|
||||||
console.log('Updating home feed')
|
console.log('Updating home feed')
|
||||||
defaultFeedView.update()
|
defaultFeedView.update()
|
||||||
} else {
|
} else {
|
||||||
store.nav.setTitle('Home')
|
store.nav.setTitle(navIdx, 'Home')
|
||||||
console.log('Fetching home feed')
|
console.log('Fetching home feed')
|
||||||
defaultFeedView.setup().then(() => {
|
defaultFeedView.setup().then(() => {
|
||||||
if (aborted) return
|
if (aborted) return
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {useStores} from '../../state'
|
||||||
import {NotificationsViewModel} from '../../state/models/notifications-view'
|
import {NotificationsViewModel} from '../../state/models/notifications-view'
|
||||||
import {ScreenParams} from '../routes'
|
import {ScreenParams} from '../routes'
|
||||||
|
|
||||||
export const Notifications = ({visible}: ScreenParams) => {
|
export const Notifications = ({navIdx, visible}: ScreenParams) => {
|
||||||
const [hasSetup, setHasSetup] = useState<boolean>(false)
|
const [hasSetup, setHasSetup] = useState<boolean>(false)
|
||||||
const [notesView, setNotesView] = useState<
|
const [notesView, setNotesView] = useState<
|
||||||
NotificationsViewModel | undefined
|
NotificationsViewModel | undefined
|
||||||
|
@ -24,7 +24,7 @@ export const Notifications = ({visible}: ScreenParams) => {
|
||||||
console.log('Updating notifications feed')
|
console.log('Updating notifications feed')
|
||||||
notesView?.update()
|
notesView?.update()
|
||||||
} else {
|
} else {
|
||||||
store.nav.setTitle('Notifications')
|
store.nav.setTitle(navIdx, 'Notifications')
|
||||||
const newNotesView = new NotificationsViewModel(store, {})
|
const newNotesView = new NotificationsViewModel(store, {})
|
||||||
setNotesView(newNotesView)
|
setNotesView(newNotesView)
|
||||||
newNotesView.setup().then(() => {
|
newNotesView.setup().then(() => {
|
||||||
|
|
|
@ -6,14 +6,14 @@ import {ScreenParams} from '../routes'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
import {makeRecordUri} from '../lib/strings'
|
import {makeRecordUri} from '../lib/strings'
|
||||||
|
|
||||||
export const PostDownvotedBy = ({visible, params}: ScreenParams) => {
|
export const PostDownvotedBy = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const {name, rkey} = params
|
const {name, rkey} = params
|
||||||
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
|
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
store.nav.setTitle('Downvoted by')
|
store.nav.setTitle(navIdx, 'Downvoted by')
|
||||||
}
|
}
|
||||||
}, [store, visible])
|
}, [store, visible])
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,14 @@ import {ScreenParams} from '../routes'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
import {makeRecordUri} from '../lib/strings'
|
import {makeRecordUri} from '../lib/strings'
|
||||||
|
|
||||||
export const PostRepostedBy = ({visible, params}: ScreenParams) => {
|
export const PostRepostedBy = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const {name, rkey} = params
|
const {name, rkey} = params
|
||||||
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
|
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
store.nav.setTitle('Reposted by')
|
store.nav.setTitle(navIdx, 'Reposted by')
|
||||||
}
|
}
|
||||||
}, [store, visible])
|
}, [store, visible])
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,14 @@ import {PostThread as PostThreadComponent} from '../com/post-thread/PostThread'
|
||||||
import {ScreenParams} from '../routes'
|
import {ScreenParams} from '../routes'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
|
|
||||||
export const PostThread = ({visible, params}: ScreenParams) => {
|
export const PostThread = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const {name, rkey} = params
|
const {name, rkey} = params
|
||||||
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
|
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
store.nav.setTitle(`Post by ${name}`)
|
store.nav.setTitle(navIdx, `Post by ${name}`)
|
||||||
}
|
}
|
||||||
}, [visible, store.nav, name])
|
}, [visible, store.nav, name])
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,14 @@ import {ScreenParams} from '../routes'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
import {makeRecordUri} from '../lib/strings'
|
import {makeRecordUri} from '../lib/strings'
|
||||||
|
|
||||||
export const PostUpvotedBy = ({visible, params}: ScreenParams) => {
|
export const PostUpvotedBy = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const {name, rkey} = params
|
const {name, rkey} = params
|
||||||
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
|
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
store.nav.setTitle('Upvoted by')
|
store.nav.setTitle(navIdx, 'Upvoted by')
|
||||||
}
|
}
|
||||||
}, [store, visible])
|
}, [store, visible])
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ const LOADING_ITEM = {_reactKey: '__loading__'}
|
||||||
const END_ITEM = {_reactKey: '__end__'}
|
const END_ITEM = {_reactKey: '__end__'}
|
||||||
const EMPTY_ITEM = {_reactKey: '__empty__'}
|
const EMPTY_ITEM = {_reactKey: '__empty__'}
|
||||||
|
|
||||||
export const Profile = observer(({visible, params}: ScreenParams) => {
|
export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const [hasSetup, setHasSetup] = useState<boolean>(false)
|
const [hasSetup, setHasSetup] = useState<boolean>(false)
|
||||||
const uiState = useMemo(
|
const uiState = useMemo(
|
||||||
|
@ -41,7 +41,7 @@ export const Profile = observer(({visible, params}: ScreenParams) => {
|
||||||
uiState.update()
|
uiState.update()
|
||||||
} else {
|
} else {
|
||||||
console.log('Fetching profile for', params.name)
|
console.log('Fetching profile for', params.name)
|
||||||
store.nav.setTitle(params.name)
|
store.nav.setTitle(navIdx, params.name)
|
||||||
uiState.setup().then(() => {
|
uiState.setup().then(() => {
|
||||||
if (aborted) return
|
if (aborted) return
|
||||||
setHasSetup(true)
|
setHasSetup(true)
|
||||||
|
|
|
@ -5,13 +5,13 @@ import {ProfileFollowers as ProfileFollowersComponent} from '../com/profile/Prof
|
||||||
import {ScreenParams} from '../routes'
|
import {ScreenParams} from '../routes'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
|
|
||||||
export const ProfileFollowers = ({visible, params}: ScreenParams) => {
|
export const ProfileFollowers = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const {name} = params
|
const {name} = params
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
store.nav.setTitle(`Followers of ${name}`)
|
store.nav.setTitle(navIdx, `Followers of ${name}`)
|
||||||
}
|
}
|
||||||
}, [store, visible, name])
|
}, [store, visible, name])
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,13 @@ import {ProfileFollows as ProfileFollowsComponent} from '../com/profile/ProfileF
|
||||||
import {ScreenParams} from '../routes'
|
import {ScreenParams} from '../routes'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
|
|
||||||
export const ProfileFollows = ({visible, params}: ScreenParams) => {
|
export const ProfileFollows = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const {name} = params
|
const {name} = params
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
store.nav.setTitle(`Followed by ${name}`)
|
store.nav.setTitle(navIdx, `Followed by ${name}`)
|
||||||
}
|
}
|
||||||
}, [store, visible, name])
|
}, [store, visible, name])
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,13 @@ import {ProfileMembers as ProfileMembersComponent} from '../com/profile/ProfileM
|
||||||
import {ScreenParams} from '../routes'
|
import {ScreenParams} from '../routes'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
|
|
||||||
export const ProfileMembers = ({visible, params}: ScreenParams) => {
|
export const ProfileMembers = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const {name} = params
|
const {name} = params
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
store.nav.setTitle(`Members of ${name}`)
|
store.nav.setTitle(navIdx, `Members of ${name}`)
|
||||||
}
|
}
|
||||||
}, [store, visible, name])
|
}, [store, visible, name])
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,13 @@ import {ScreenParams} from '../routes'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
import {colors} from '../lib/styles'
|
import {colors} from '../lib/styles'
|
||||||
|
|
||||||
export const Search = ({visible, params}: ScreenParams) => {
|
export const Search = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const {name} = params
|
const {name} = params
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
store.nav.setTitle(`Search`)
|
store.nav.setTitle(navIdx, `Search`)
|
||||||
}
|
}
|
||||||
}, [store, visible, name])
|
}, [store, visible, name])
|
||||||
const onComposePress = () => {
|
const onComposePress = () => {
|
||||||
|
|
|
@ -8,14 +8,17 @@ import {ViewHeader} from '../com/util/ViewHeader'
|
||||||
import {Link} from '../com/util/Link'
|
import {Link} from '../com/util/Link'
|
||||||
import {UserAvatar} from '../com/util/UserAvatar'
|
import {UserAvatar} from '../com/util/UserAvatar'
|
||||||
|
|
||||||
export const Settings = observer(function Settings({visible}: ScreenParams) {
|
export const Settings = observer(function Settings({
|
||||||
|
navIdx,
|
||||||
|
visible,
|
||||||
|
}: ScreenParams) {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
store.nav.setTitle('Settings')
|
store.nav.setTitle(navIdx, 'Settings')
|
||||||
}, [visible, store])
|
}, [visible, store])
|
||||||
|
|
||||||
const onPressSignout = () => {
|
const onPressSignout = () => {
|
||||||
|
|
|
@ -268,7 +268,7 @@ export const MobileShell: React.FC = observer(() => {
|
||||||
<GestureDetector gesture={swipeGesture}>
|
<GestureDetector gesture={swipeGesture}>
|
||||||
<ScreenContainer style={styles.screenContainer}>
|
<ScreenContainer style={styles.screenContainer}>
|
||||||
{screenRenderDesc.screens.map(
|
{screenRenderDesc.screens.map(
|
||||||
({Com, params, key, current, previous}) => {
|
({Com, navIdx, params, key, current, previous}) => {
|
||||||
return (
|
return (
|
||||||
<Screen
|
<Screen
|
||||||
key={key}
|
key={key}
|
||||||
|
@ -293,6 +293,7 @@ export const MobileShell: React.FC = observer(() => {
|
||||||
]}>
|
]}>
|
||||||
<Com
|
<Com
|
||||||
params={params}
|
params={params}
|
||||||
|
navIdx={navIdx}
|
||||||
visible={current}
|
visible={current}
|
||||||
scrollElRef={current ? scrollElRef : undefined}
|
scrollElRef={current ? scrollElRef : undefined}
|
||||||
/>
|
/>
|
||||||
|
@ -361,6 +362,7 @@ export const MobileShell: React.FC = observer(() => {
|
||||||
*/
|
*/
|
||||||
type ScreenRenderDesc = MatchResult & {
|
type ScreenRenderDesc = MatchResult & {
|
||||||
key: string
|
key: string
|
||||||
|
navIdx: [number, number]
|
||||||
current: boolean
|
current: boolean
|
||||||
previous: boolean
|
previous: boolean
|
||||||
isNewTab: boolean
|
isNewTab: boolean
|
||||||
|
@ -388,6 +390,7 @@ function constructScreenRenderDesc(nav: NavigationModel): {
|
||||||
hasNewTab = hasNewTab || tab.isNewTab
|
hasNewTab = hasNewTab || tab.isNewTab
|
||||||
return Object.assign(matchRes, {
|
return Object.assign(matchRes, {
|
||||||
key: `t${tab.id}-s${screen.index}`,
|
key: `t${tab.id}-s${screen.index}`,
|
||||||
|
navIdx: [tab.id, screen.id],
|
||||||
current: isCurrent,
|
current: isCurrent,
|
||||||
previous: isPrevious,
|
previous: isPrevious,
|
||||||
isNewTab: tab.isNewTab,
|
isNewTab: tab.isNewTab,
|
||||||
|
|
Loading…
Reference in New Issue