Onboarding & feed fixes (#1602)

* Fix: improve the 'end of feed' detection condition

* Fix the feeds link on mobile in the empty state

* Align the following empty state better on web

* Dont autofocus the search input in the search tab

* Fix the error boundary render

* Add 'end of feed' CTA to following feed

* Reduce the default feeds to discover now that we have feed-selection during onboarding

* Fix case where loading spinner fails to stop rendering in bottom of feed

* Fix: dont show loading spinner at footer of feed when refreshing

* Fix: dont fire reminders during onboarding

* Optimize adding feeds and update to mirror the api behaviors more closely

* Use the lock in preferences to avoid clobbering in-flight updates

* Refresh the feed after onboarding to ensure content is visible

* Remove the now-incorrect comment

* Tune copy
This commit is contained in:
Paul Frazee 2023-10-04 08:57:23 -07:00 committed by GitHub
parent a76fb78d53
commit b1a1bae02e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 262 additions and 96 deletions

View file

@ -81,6 +81,7 @@ export class OnboardingModel {
}
finish() {
this.rootStore.me.mainFeed.refresh() // load the selected content
this.step = 'Home'
track('Onboarding:Complete')
}

View file

@ -116,6 +116,10 @@ export class PostsFeedModel {
return this.hasLoaded && !this.hasContent
}
get isLoadingMore() {
return this.isLoading && !this.isRefreshing
}
setHasNewLatest(v: boolean) {
this.hasNewLatest = v
}
@ -307,7 +311,7 @@ export class PostsFeedModel {
}
async _appendAll(res: FeedAPIResponse, replace = false) {
this.hasMore = !!res.cursor
this.hasMore = !!res.cursor && res.feed.length > 0
if (replace) {
this.emptyFetches = 0
}

View file

@ -418,6 +418,7 @@ export class PreferencesModel {
const oldPinned = this.pinnedFeeds
this.savedFeeds = saved
this.pinnedFeeds = pinned
await this.lock.acquireAsync()
try {
const res = await cb()
runInAction(() => {
@ -430,6 +431,8 @@ export class PreferencesModel {
this.pinnedFeeds = oldPinned
})
throw e
} finally {
this.lock.release()
}
}
@ -441,7 +444,7 @@ export class PreferencesModel {
async addSavedFeed(v: string) {
return this._optimisticUpdateSavedFeeds(
[...this.savedFeeds, v],
[...this.savedFeeds.filter(uri => uri !== v), v],
this.pinnedFeeds,
() => this.rootStore.agent.addSavedFeed(v),
)
@ -457,8 +460,8 @@ export class PreferencesModel {
async addPinnedFeed(v: string) {
return this._optimisticUpdateSavedFeeds(
this.savedFeeds,
[...this.pinnedFeeds, v],
[...this.savedFeeds.filter(uri => uri !== v), v],
[...this.pinnedFeeds.filter(uri => uri !== v), v],
() => this.rootStore.agent.addPinnedFeed(v),
)
}
@ -473,71 +476,121 @@ export class PreferencesModel {
async setBirthDate(birthDate: Date) {
this.birthDate = birthDate
await this.rootStore.agent.setPersonalDetails({birthDate})
await this.lock.acquireAsync()
try {
await this.rootStore.agent.setPersonalDetails({birthDate})
} finally {
this.lock.release()
}
}
async toggleHomeFeedHideReplies() {
this.homeFeed.hideReplies = !this.homeFeed.hideReplies
await this.rootStore.agent.setFeedViewPrefs('home', {
hideReplies: this.homeFeed.hideReplies,
})
await this.lock.acquireAsync()
try {
await this.rootStore.agent.setFeedViewPrefs('home', {
hideReplies: this.homeFeed.hideReplies,
})
} finally {
this.lock.release()
}
}
async toggleHomeFeedHideRepliesByUnfollowed() {
this.homeFeed.hideRepliesByUnfollowed =
!this.homeFeed.hideRepliesByUnfollowed
await this.rootStore.agent.setFeedViewPrefs('home', {
hideRepliesByUnfollowed: this.homeFeed.hideRepliesByUnfollowed,
})
await this.lock.acquireAsync()
try {
await this.rootStore.agent.setFeedViewPrefs('home', {
hideRepliesByUnfollowed: this.homeFeed.hideRepliesByUnfollowed,
})
} finally {
this.lock.release()
}
}
async setHomeFeedHideRepliesByLikeCount(threshold: number) {
this.homeFeed.hideRepliesByLikeCount = threshold
await this.rootStore.agent.setFeedViewPrefs('home', {
hideRepliesByLikeCount: this.homeFeed.hideRepliesByLikeCount,
})
await this.lock.acquireAsync()
try {
await this.rootStore.agent.setFeedViewPrefs('home', {
hideRepliesByLikeCount: this.homeFeed.hideRepliesByLikeCount,
})
} finally {
this.lock.release()
}
}
async toggleHomeFeedHideReposts() {
this.homeFeed.hideReposts = !this.homeFeed.hideReposts
await this.rootStore.agent.setFeedViewPrefs('home', {
hideReposts: this.homeFeed.hideReposts,
})
await this.lock.acquireAsync()
try {
await this.rootStore.agent.setFeedViewPrefs('home', {
hideReposts: this.homeFeed.hideReposts,
})
} finally {
this.lock.release()
}
}
async toggleHomeFeedHideQuotePosts() {
this.homeFeed.hideQuotePosts = !this.homeFeed.hideQuotePosts
await this.rootStore.agent.setFeedViewPrefs('home', {
hideQuotePosts: this.homeFeed.hideQuotePosts,
})
await this.lock.acquireAsync()
try {
await this.rootStore.agent.setFeedViewPrefs('home', {
hideQuotePosts: this.homeFeed.hideQuotePosts,
})
} finally {
this.lock.release()
}
}
async toggleHomeFeedMergeFeedEnabled() {
this.homeFeed.lab_mergeFeedEnabled = !this.homeFeed.lab_mergeFeedEnabled
await this.rootStore.agent.setFeedViewPrefs('home', {
lab_mergeFeedEnabled: this.homeFeed.lab_mergeFeedEnabled,
})
await this.lock.acquireAsync()
try {
await this.rootStore.agent.setFeedViewPrefs('home', {
lab_mergeFeedEnabled: this.homeFeed.lab_mergeFeedEnabled,
})
} finally {
this.lock.release()
}
}
async setThreadSort(v: string) {
if (THREAD_SORT_VALUES.includes(v)) {
this.thread.sort = v
await this.rootStore.agent.setThreadViewPrefs({sort: v})
await this.lock.acquireAsync()
try {
await this.rootStore.agent.setThreadViewPrefs({sort: v})
} finally {
this.lock.release()
}
}
}
async togglePrioritizedFollowedUsers() {
this.thread.prioritizeFollowedUsers = !this.thread.prioritizeFollowedUsers
await this.rootStore.agent.setThreadViewPrefs({
prioritizeFollowedUsers: this.thread.prioritizeFollowedUsers,
})
await this.lock.acquireAsync()
try {
await this.rootStore.agent.setThreadViewPrefs({
prioritizeFollowedUsers: this.thread.prioritizeFollowedUsers,
})
} finally {
this.lock.release()
}
}
async toggleThreadTreeViewEnabled() {
this.thread.lab_treeViewEnabled = !this.thread.lab_treeViewEnabled
await this.rootStore.agent.setThreadViewPrefs({
lab_treeViewEnabled: this.thread.lab_treeViewEnabled,
})
await this.lock.acquireAsync()
try {
await this.rootStore.agent.setThreadViewPrefs({
lab_treeViewEnabled: this.thread.lab_treeViewEnabled,
})
} finally {
this.lock.release()
}
}
toggleRequireAltTextEnabled() {

View file

@ -6,10 +6,6 @@ import {toHashCode} from 'lib/strings/helpers'
const DAY = 60e3 * 24 * 1 // 1 day (ms)
export class Reminders {
// NOTE
// by defaulting to the current date, we ensure that the user won't be nagged
// on first run (aka right after creating an account)
// -prf
lastEmailConfirm: Date = new Date()
constructor(public rootStore: RootStoreModel) {
@ -46,6 +42,9 @@ export class Reminders {
if (sess.emailConfirmed) {
return false
}
if (this.rootStore.onboarding.isActive) {
return false
}
const today = new Date()
// shard the users into 2 day of the week buckets
// (this is to avoid a sudden influx of email updates when