diff --git a/src/state/models/me.ts b/src/state/models/me.ts
index 78c6d2e76c..e3405b80dd 100644
--- a/src/state/models/me.ts
+++ b/src/state/models/me.ts
@@ -1,6 +1,7 @@
import {makeAutoObservable, runInAction} from 'mobx'
import {RootStoreModel} from './root-store'
import {MembershipsViewModel} from './memberships-view'
+import {NotificationsViewModel} from './notifications-view'
export class MeModel {
did?: string
@@ -9,9 +10,11 @@ export class MeModel {
description?: string
notificationCount: number = 0
memberships?: MembershipsViewModel
+ notifications: NotificationsViewModel
constructor(public rootStore: RootStoreModel) {
makeAutoObservable(this, {rootStore: false}, {autoBind: true})
+ this.notifications = new NotificationsViewModel(this.rootStore, {})
}
clear() {
@@ -43,7 +46,12 @@ export class MeModel {
this.memberships = new MembershipsViewModel(this.rootStore, {
actor: this.did,
})
- await this.memberships?.setup()
+ await this.memberships?.setup().catch(e => {
+ console.error('Failed to setup memberships model', e)
+ })
+ await this.notifications.setup().catch(e => {
+ console.error('Failed to setup notifications model', e)
+ })
} else {
this.clear()
}
@@ -56,7 +64,12 @@ export class MeModel {
async fetchStateUpdate() {
const res = await this.rootStore.api.app.bsky.notification.getCount()
runInAction(() => {
+ const newNotifications = this.notificationCount !== res.data.count
this.notificationCount = res.data.count
+ if (newNotifications) {
+ // trigger pre-emptive fetch on new notifications
+ this.notifications.refresh()
+ }
})
}
diff --git a/src/state/models/notifications-view.ts b/src/state/models/notifications-view.ts
index f3163822eb..e81f31a254 100644
--- a/src/state/models/notifications-view.ts
+++ b/src/state/models/notifications-view.ts
@@ -203,7 +203,6 @@ export class NotificationsViewModel {
await this._pendingWork()
this._loadPromise = this._initialLoad(isRefreshing)
await this._loadPromise
- this._updateReadState()
this._loadPromise = undefined
}
@@ -240,6 +239,20 @@ export class NotificationsViewModel {
this._updatePromise = undefined
}
+ /**
+ * Update read/unread state
+ */
+ async updateReadState() {
+ try {
+ await this.rootStore.api.app.bsky.notification.updateSeen({
+ seenAt: new Date().toISOString(),
+ })
+ this.rootStore.me.clearNotificationCount()
+ } catch (e) {
+ console.log('Failed to update notifications read state', e)
+ }
+ }
+
// state transitions
// =
@@ -329,11 +342,10 @@ export class NotificationsViewModel {
}
private async _replaceAll(res: ListNotifications.Response) {
- this.notifications.length = 0
- return this._appendAll(res)
+ return this._appendAll(res, true)
}
- private async _appendAll(res: ListNotifications.Response) {
+ private async _appendAll(res: ListNotifications.Response, replace = false) {
this.loadMoreCursor = res.data.cursor
this.hasMore = !!this.loadMoreCursor
let counter = this.notifications.length
@@ -357,7 +369,11 @@ export class NotificationsViewModel {
)
})
runInAction(() => {
- this.notifications = this.notifications.concat(itemModels)
+ if (replace) {
+ this.notifications = itemModels
+ } else {
+ this.notifications = this.notifications.concat(itemModels)
+ }
})
}
@@ -374,17 +390,6 @@ export class NotificationsViewModel {
}
}
}
-
- private async _updateReadState() {
- try {
- await this.rootStore.api.app.bsky.notification.updateSeen({
- seenAt: new Date().toISOString(),
- })
- this.rootStore.me.clearNotificationCount()
- } catch (e) {
- console.log('Failed to update notifications read state', e)
- }
- }
}
function groupNotifications(
diff --git a/src/view/com/notifications/Feed.tsx b/src/view/com/notifications/Feed.tsx
index 90d16604d8..78dcd2fa86 100644
--- a/src/view/com/notifications/Feed.tsx
+++ b/src/view/com/notifications/Feed.tsx
@@ -32,7 +32,7 @@ export const Feed = observer(function Feed({
}
return (
- {view.isLoading && !view.isRefreshing && !view.hasContent && (
+ {view.isLoading && !view.isRefreshing && (
)}
{view.hasError && (
@@ -43,7 +43,7 @@ export const Feed = observer(function Feed({
onPressTryAgain={onPressTryAgain}
/>
)}
- {view.hasContent && (
+ {view.hasLoaded && (
item._reactKey}
@@ -53,7 +53,7 @@ export const Feed = observer(function Feed({
onEndReached={onEndReached}
/>
)}
- {view.isEmpty && (
+ {view.hasLoaded && view.isEmpty && (
)}
diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx
index 1e7abdb904..b168ffaff4 100644
--- a/src/view/screens/Notifications.tsx
+++ b/src/view/screens/Notifications.tsx
@@ -7,43 +7,33 @@ import {NotificationsViewModel} from '../../state/models/notifications-view'
import {ScreenParams} from '../routes'
export const Notifications = ({navIdx, visible}: ScreenParams) => {
- const [hasSetup, setHasSetup] = useState(false)
- const [notesView, setNotesView] = useState<
- NotificationsViewModel | undefined
- >()
const store = useStores()
useEffect(() => {
- let aborted = false
if (!visible) {
return
}
+ console.log('Updating notifications feed')
store.me.refreshMemberships() // needed for the invite notifications
- if (hasSetup) {
- console.log('Updating notifications feed')
- notesView?.update()
- } else {
- store.nav.setTitle(navIdx, 'Notifications')
- const newNotesView = new NotificationsViewModel(store, {})
- setNotesView(newNotesView)
- newNotesView.setup().then(() => {
- if (aborted) return
- setHasSetup(true)
+ store.me.notifications
+ .update()
+ .catch(e => {
+ console.error('Error while updating notifications feed', e)
})
- }
- return () => {
- aborted = true
- }
+ .then(() => {
+ store.me.notifications.updateReadState()
+ })
+ store.nav.setTitle(navIdx, 'Notifications')
}, [visible, store])
const onPressTryAgain = () => {
- notesView?.refresh()
+ store.me.notifications.refresh()
}
return (
- {notesView && }
+
)
}