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:
parent
a76fb78d53
commit
b1a1bae02e
12 changed files with 262 additions and 96 deletions
|
@ -81,6 +81,7 @@ export class OnboardingModel {
|
|||
}
|
||||
|
||||
finish() {
|
||||
this.rootStore.me.mainFeed.refresh() // load the selected content
|
||||
this.step = 'Home'
|
||||
track('Onboarding:Complete')
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue