Reorganize state models for clarity (#378)

zio/stable
Paul Frazee 2023-04-03 15:21:17 -05:00 committed by GitHub
parent 9652d994dd
commit 2045c615a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 163 additions and 171 deletions

View File

@ -2,7 +2,7 @@ import {LikelyType, LinkMeta} from './link-meta'
// import {match as matchRoute} from 'view/routes' // import {match as matchRoute} from 'view/routes'
import {convertBskyAppUrlIfNeeded, makeRecordUri} from '../strings/url-helpers' import {convertBskyAppUrlIfNeeded, makeRecordUri} from '../strings/url-helpers'
import {RootStoreModel} from 'state/index' import {RootStoreModel} from 'state/index'
import {PostThreadViewModel} from 'state/models/post-thread-view' import {PostThreadModel} from 'state/models/content/post-thread'
import {ComposerOptsQuote} from 'state/models/ui/shell' import {ComposerOptsQuote} from 'state/models/ui/shell'
// TODO // TODO
@ -108,7 +108,7 @@ export async function getPostAsQuote(
const [_0, user, _1, rkey] = url.split('/').filter(Boolean) const [_0, user, _1, rkey] = url.split('/').filter(Boolean)
const threadUri = makeRecordUri(user, 'app.bsky.feed.post', rkey) const threadUri = makeRecordUri(user, 'app.bsky.feed.post', rkey)
const threadView = new PostThreadViewModel(store, { const threadView = new PostThreadModel(store, {
uri: threadUri, uri: threadUri,
depth: 0, depth: 0,
}) })

View File

@ -1,7 +1,7 @@
import notifee, {EventType} from '@notifee/react-native' import notifee, {EventType} from '@notifee/react-native'
import {AppBskyEmbedImages} from '@atproto/api' import {AppBskyEmbedImages} from '@atproto/api'
import {RootStoreModel} from 'state/models/root-store' import {RootStoreModel} from 'state/models/root-store'
import {NotificationsViewItemModel} from 'state/models/notifications-view' import {NotificationsFeedItemModel} from 'state/models/feeds/notifications'
import {enforceLen} from 'lib/strings/helpers' import {enforceLen} from 'lib/strings/helpers'
import {resetToTab} from '../Navigation' import {resetToTab} from '../Navigation'
@ -40,7 +40,7 @@ export function displayNotification(
} }
export function displayNotificationFromModel( export function displayNotificationFromModel(
notif: NotificationsViewItemModel, notif: NotificationsFeedItemModel,
) { ) {
let author = notif.author.displayName || notif.author.handle let author = notif.author.displayName || notif.author.handle
let title: string let title: string

View File

@ -1,10 +1,10 @@
import {makeAutoObservable} from 'mobx' import {makeAutoObservable} from 'mobx'
import {LRUMap} from 'lru_map' import {LRUMap} from 'lru_map'
import {RootStoreModel} from './root-store' import {RootStoreModel} from '../root-store'
import {AppBskyActorGetProfile as GetProfile} from '@atproto/api' import {AppBskyActorGetProfile as GetProfile} from '@atproto/api'
type CacheValue = Promise<GetProfile.Response> | GetProfile.Response type CacheValue = Promise<GetProfile.Response> | GetProfile.Response
export class ProfilesViewModel { export class ProfilesCache {
cache: LRUMap<string, CacheValue> = new LRUMap(100) cache: LRUMap<string, CacheValue> = new LRUMap(100)
constructor(public rootStore: RootStoreModel) { constructor(public rootStore: RootStoreModel) {

View File

@ -5,8 +5,8 @@ import {
AppBskyFeedDefs, AppBskyFeedDefs,
RichText, RichText,
} from '@atproto/api' } from '@atproto/api'
import {AtUri} from '../../third-party/uri' import {AtUri} from '../../../third-party/uri'
import {RootStoreModel} from './root-store' import {RootStoreModel} from '../root-store'
import * as apilib from 'lib/api/index' import * as apilib from 'lib/api/index'
import {cleanError} from 'lib/strings/errors' import {cleanError} from 'lib/strings/errors'
@ -17,7 +17,7 @@ function* reactKeyGenerator(): Generator<string> {
} }
} }
export class PostThreadViewPostModel { export class PostThreadItemModel {
// ui state // ui state
_reactKey: string = '' _reactKey: string = ''
_depth = 0 _depth = 0
@ -29,8 +29,8 @@ export class PostThreadViewPostModel {
// data // data
post: AppBskyFeedDefs.PostView post: AppBskyFeedDefs.PostView
postRecord?: FeedPost.Record postRecord?: FeedPost.Record
parent?: PostThreadViewPostModel | AppBskyFeedDefs.NotFoundPost parent?: PostThreadItemModel | AppBskyFeedDefs.NotFoundPost
replies?: (PostThreadViewPostModel | AppBskyFeedDefs.NotFoundPost)[] replies?: (PostThreadItemModel | AppBskyFeedDefs.NotFoundPost)[]
richText?: RichText richText?: RichText
get uri() { get uri() {
@ -79,7 +79,7 @@ export class PostThreadViewPostModel {
// parents // parents
if (includeParent && v.parent) { if (includeParent && v.parent) {
if (AppBskyFeedDefs.isThreadViewPost(v.parent)) { if (AppBskyFeedDefs.isThreadViewPost(v.parent)) {
const parentModel = new PostThreadViewPostModel( const parentModel = new PostThreadItemModel(
this.rootStore, this.rootStore,
keyGen.next().value, keyGen.next().value,
v.parent, v.parent,
@ -106,7 +106,7 @@ export class PostThreadViewPostModel {
const replies = [] const replies = []
for (const item of v.replies) { for (const item of v.replies) {
if (AppBskyFeedDefs.isThreadViewPost(item)) { if (AppBskyFeedDefs.isThreadViewPost(item)) {
const itemModel = new PostThreadViewPostModel( const itemModel = new PostThreadItemModel(
this.rootStore, this.rootStore,
keyGen.next().value, keyGen.next().value,
item, item,
@ -182,7 +182,7 @@ export class PostThreadViewPostModel {
} }
} }
export class PostThreadViewModel { export class PostThreadModel {
// state // state
isLoading = false isLoading = false
isRefreshing = false isRefreshing = false
@ -193,7 +193,7 @@ export class PostThreadViewModel {
params: GetPostThread.QueryParams params: GetPostThread.QueryParams
// data // data
thread?: PostThreadViewPostModel thread?: PostThreadItemModel
constructor( constructor(
public rootStore: RootStoreModel, public rootStore: RootStoreModel,
@ -321,7 +321,7 @@ export class PostThreadViewModel {
_replaceAll(res: GetPostThread.Response) { _replaceAll(res: GetPostThread.Response) {
sortThread(res.data.thread) sortThread(res.data.thread)
const keyGen = reactKeyGenerator() const keyGen = reactKeyGenerator()
const thread = new PostThreadViewPostModel( const thread = new PostThreadItemModel(
this.rootStore, this.rootStore,
keyGen.next().value, keyGen.next().value,
res.data.thread as AppBskyFeedDefs.ThreadViewPost, res.data.thread as AppBskyFeedDefs.ThreadViewPost,

View File

@ -1,7 +1,7 @@
import {makeAutoObservable} from 'mobx' import {makeAutoObservable} from 'mobx'
import {AppBskyFeedPost as Post} from '@atproto/api' import {AppBskyFeedPost as Post} from '@atproto/api'
import {AtUri} from '../../third-party/uri' import {AtUri} from '../../../third-party/uri'
import {RootStoreModel} from './root-store' import {RootStoreModel} from '../root-store'
import {cleanError} from 'lib/strings/errors' import {cleanError} from 'lib/strings/errors'
type RemoveIndex<T> = { type RemoveIndex<T> = {

View File

@ -5,13 +5,13 @@ import {
AppBskyActorProfile, AppBskyActorProfile,
RichText, RichText,
} from '@atproto/api' } from '@atproto/api'
import {RootStoreModel} from './root-store' import {RootStoreModel} from '../root-store'
import * as apilib from 'lib/api/index' import * as apilib from 'lib/api/index'
import {cleanError} from 'lib/strings/errors' import {cleanError} from 'lib/strings/errors'
export const ACTOR_TYPE_USER = 'app.bsky.system.actorUser' export const ACTOR_TYPE_USER = 'app.bsky.system.actorUser'
export class ProfileViewViewerModel { export class ProfileViewerModel {
muted?: boolean muted?: boolean
following?: string following?: string
followedBy?: string followedBy?: string
@ -21,7 +21,7 @@ export class ProfileViewViewerModel {
} }
} }
export class ProfileViewModel { export class ProfileModel {
// state // state
isLoading = false isLoading = false
isRefreshing = false isRefreshing = false
@ -40,7 +40,7 @@ export class ProfileViewModel {
followersCount: number = 0 followersCount: number = 0
followsCount: number = 0 followsCount: number = 0
postsCount: number = 0 postsCount: number = 0
viewer = new ProfileViewViewerModel() viewer = new ProfileViewerModel()
// added data // added data
descriptionRichText?: RichText = new RichText({text: ''}) descriptionRichText?: RichText = new RichText({text: ''})

View File

@ -1,6 +1,6 @@
import {makeAutoObservable, runInAction} from 'mobx' import {makeAutoObservable, runInAction} from 'mobx'
import {RootStoreModel} from './root-store' import {RootStoreModel} from '../root-store'
import {FeedItemModel} from './feed-view' import {PostsFeedItemModel} from '../feeds/posts'
import {cleanError} from 'lib/strings/errors' import {cleanError} from 'lib/strings/errors'
import {TEAM_HANDLES} from 'lib/constants' import {TEAM_HANDLES} from 'lib/constants'
import { import {
@ -8,14 +8,14 @@ import {
mergePosts, mergePosts,
} from 'lib/api/build-suggested-posts' } from 'lib/api/build-suggested-posts'
export class SuggestedPostsView { export class SuggestedPostsModel {
// state // state
isLoading = false isLoading = false
hasLoaded = false hasLoaded = false
error = '' error = ''
// data // data
posts: FeedItemModel[] = [] posts: PostsFeedItemModel[] = []
constructor(public rootStore: RootStoreModel) { constructor(public rootStore: RootStoreModel) {
makeAutoObservable( makeAutoObservable(
@ -57,7 +57,7 @@ export class SuggestedPostsView {
this.posts = finalPosts.map((post, i) => { this.posts = finalPosts.map((post, i) => {
// strip the reasons to hide that these are reposts // strip the reasons to hide that these are reposts
delete post.reason delete post.reason
return new FeedItemModel(this.rootStore, `post-${i}`, post) return new PostsFeedItemModel(this.rootStore, `post-${i}`, post)
}) })
}) })
this._xIdle() this._xIdle()

View File

@ -1,9 +1,9 @@
import {makeAutoObservable, runInAction} from 'mobx' import {makeAutoObservable, runInAction} from 'mobx'
import {AppBskyActorDefs} from '@atproto/api' import {AppBskyActorDefs} from '@atproto/api'
import AwaitLock from 'await-lock' import AwaitLock from 'await-lock'
import {RootStoreModel} from './root-store' import {RootStoreModel} from '../root-store'
export class UserAutocompleteViewModel { export class UserAutocompleteModel {
// state // state
isLoading = false isLoading = false
isActive = false isActive = false

View File

@ -9,8 +9,8 @@ import {
} from '@atproto/api' } from '@atproto/api'
import AwaitLock from 'await-lock' import AwaitLock from 'await-lock'
import {bundleAsync} from 'lib/async/bundle' import {bundleAsync} from 'lib/async/bundle'
import {RootStoreModel} from './root-store' import {RootStoreModel} from '../root-store'
import {PostThreadViewModel} from './post-thread-view' import {PostThreadModel} from '../content/post-thread'
import {cleanError} from 'lib/strings/errors' import {cleanError} from 'lib/strings/errors'
const GROUPABLE_REASONS = ['like', 'repost', 'follow'] const GROUPABLE_REASONS = ['like', 'repost', 'follow']
@ -30,7 +30,7 @@ type SupportedRecord =
| AppBskyFeedLike.Record | AppBskyFeedLike.Record
| AppBskyGraphFollow.Record | AppBskyGraphFollow.Record
export class NotificationsViewItemModel { export class NotificationsFeedItemModel {
// ui state // ui state
_reactKey: string = '' _reactKey: string = ''
@ -47,10 +47,10 @@ export class NotificationsViewItemModel {
record?: SupportedRecord record?: SupportedRecord
isRead: boolean = false isRead: boolean = false
indexedAt: string = '' indexedAt: string = ''
additional?: NotificationsViewItemModel[] additional?: NotificationsFeedItemModel[]
// additional data // additional data
additionalPost?: PostThreadViewModel additionalPost?: PostThreadModel
constructor( constructor(
public rootStore: RootStoreModel, public rootStore: RootStoreModel,
@ -75,7 +75,7 @@ export class NotificationsViewItemModel {
this.additional = [] this.additional = []
for (const add of v.additional) { for (const add of v.additional) {
this.additional.push( this.additional.push(
new NotificationsViewItemModel(this.rootStore, '', add), new NotificationsFeedItemModel(this.rootStore, '', add),
) )
} }
} else if (!preserve) { } else if (!preserve) {
@ -171,7 +171,7 @@ export class NotificationsViewItemModel {
postUri = this.subjectUri postUri = this.subjectUri
} }
if (postUri) { if (postUri) {
this.additionalPost = new PostThreadViewModel(this.rootStore, { this.additionalPost = new PostThreadModel(this.rootStore, {
uri: postUri, uri: postUri,
depth: 0, depth: 0,
}) })
@ -185,7 +185,7 @@ export class NotificationsViewItemModel {
} }
} }
export class NotificationsViewModel { export class NotificationsFeedModel {
// state // state
isLoading = false isLoading = false
isRefreshing = false isRefreshing = false
@ -199,7 +199,7 @@ export class NotificationsViewModel {
lock = new AwaitLock() lock = new AwaitLock()
// data // data
notifications: NotificationsViewItemModel[] = [] notifications: NotificationsFeedItemModel[] = []
unreadCount = 0 unreadCount = 0
// this is used to help trigger push notifications // this is used to help trigger push notifications
@ -416,7 +416,7 @@ export class NotificationsViewModel {
} }
} }
async getNewMostRecent(): Promise<NotificationsViewItemModel | undefined> { async getNewMostRecent(): Promise<NotificationsFeedItemModel | undefined> {
let old = this.mostRecentNotificationUri let old = this.mostRecentNotificationUri
const res = await this.rootStore.agent.listNotifications({ const res = await this.rootStore.agent.listNotifications({
limit: 1, limit: 1,
@ -425,7 +425,7 @@ export class NotificationsViewModel {
return return
} }
this.mostRecentNotificationUri = res.data.notifications[0].uri this.mostRecentNotificationUri = res.data.notifications[0].uri
const notif = new NotificationsViewItemModel( const notif = new NotificationsFeedItemModel(
this.rootStore, this.rootStore,
'mostRecent', 'mostRecent',
res.data.notifications[0], res.data.notifications[0],
@ -467,9 +467,9 @@ export class NotificationsViewModel {
this.loadMoreCursor = res.data.cursor this.loadMoreCursor = res.data.cursor
this.hasMore = !!this.loadMoreCursor this.hasMore = !!this.loadMoreCursor
const promises = [] const promises = []
const itemModels: NotificationsViewItemModel[] = [] const itemModels: NotificationsFeedItemModel[] = []
for (const item of groupNotifications(res.data.notifications)) { for (const item of groupNotifications(res.data.notifications)) {
const itemModel = new NotificationsViewItemModel( const itemModel = new NotificationsFeedItemModel(
this.rootStore, this.rootStore,
`item-${_idCounter++}`, `item-${_idCounter++}`,
item, item,
@ -496,7 +496,7 @@ export class NotificationsViewModel {
async _prependAll(res: ListNotifications.Response) { async _prependAll(res: ListNotifications.Response) {
const promises = [] const promises = []
const itemModels: NotificationsViewItemModel[] = [] const itemModels: NotificationsFeedItemModel[] = []
const dedupedNotifs = res.data.notifications.filter( const dedupedNotifs = res.data.notifications.filter(
n1 => n1 =>
!this.notifications.find( !this.notifications.find(
@ -504,7 +504,7 @@ export class NotificationsViewModel {
), ),
) )
for (const item of groupNotifications(dedupedNotifs)) { for (const item of groupNotifications(dedupedNotifs)) {
const itemModel = new NotificationsViewItemModel( const itemModel = new NotificationsFeedItemModel(
this.rootStore, this.rootStore,
`item-${_idCounter++}`, `item-${_idCounter++}`,
item, item,
@ -565,7 +565,7 @@ function groupNotifications(
return items2 return items2
} }
type N = ListNotifications.Notification | NotificationsViewItemModel type N = ListNotifications.Notification | NotificationsFeedItemModel
function isEq(a: N, b: N) { function isEq(a: N, b: N) {
// this function has a key subtlety- the indexedAt comparison // this function has a key subtlety- the indexedAt comparison
// the reason for this is reposts: they set the URI of the original post, not of the repost record // the reason for this is reposts: they set the URI of the original post, not of the repost record

View File

@ -10,7 +10,7 @@ import {
import AwaitLock from 'await-lock' import AwaitLock from 'await-lock'
import {bundleAsync} from 'lib/async/bundle' import {bundleAsync} from 'lib/async/bundle'
import sampleSize from 'lodash.samplesize' import sampleSize from 'lodash.samplesize'
import {RootStoreModel} from './root-store' import {RootStoreModel} from '../root-store'
import {cleanError} from 'lib/strings/errors' import {cleanError} from 'lib/strings/errors'
import {SUGGESTED_FOLLOWS} from 'lib/constants' import {SUGGESTED_FOLLOWS} from 'lib/constants'
import { import {
@ -27,7 +27,7 @@ type PostView = AppBskyFeedDefs.PostView
const PAGE_SIZE = 30 const PAGE_SIZE = 30
let _idCounter = 0 let _idCounter = 0
export class FeedItemModel { export class PostsFeedItemModel {
// ui state // ui state
_reactKey: string = '' _reactKey: string = ''
@ -139,12 +139,12 @@ export class FeedItemModel {
} }
} }
export class FeedSliceModel { export class PostsFeedSliceModel {
// ui state // ui state
_reactKey: string = '' _reactKey: string = ''
// data // data
items: FeedItemModel[] = [] items: PostsFeedItemModel[] = []
constructor( constructor(
public rootStore: RootStoreModel, public rootStore: RootStoreModel,
@ -154,7 +154,7 @@ export class FeedSliceModel {
this._reactKey = reactKey this._reactKey = reactKey
for (const item of slice.items) { for (const item of slice.items) {
this.items.push( this.items.push(
new FeedItemModel(rootStore, `item-${_idCounter++}`, item), new PostsFeedItemModel(rootStore, `item-${_idCounter++}`, item),
) )
} }
makeAutoObservable(this, {rootStore: false}) makeAutoObservable(this, {rootStore: false})
@ -206,7 +206,7 @@ export class FeedSliceModel {
} }
} }
export class FeedModel { export class PostsFeedModel {
// state // state
isLoading = false isLoading = false
isRefreshing = false isRefreshing = false
@ -223,8 +223,8 @@ export class FeedModel {
lock = new AwaitLock() lock = new AwaitLock()
// data // data
slices: FeedSliceModel[] = [] slices: PostsFeedSliceModel[] = []
nextSlices: FeedSliceModel[] = [] nextSlices: PostsFeedSliceModel[] = []
constructor( constructor(
public rootStore: RootStoreModel, public rootStore: RootStoreModel,
@ -445,7 +445,11 @@ export class FeedModel {
if (nextSlices[0]?.uri !== this.slices[0]?.uri) { if (nextSlices[0]?.uri !== this.slices[0]?.uri) {
const nextSlicesModels = nextSlices.map( const nextSlicesModels = nextSlices.map(
slice => slice =>
new FeedSliceModel(this.rootStore, `item-${_idCounter++}`, slice), new PostsFeedSliceModel(
this.rootStore,
`item-${_idCounter++}`,
slice,
),
) )
if (autoPrepend) { if (autoPrepend) {
runInAction(() => { runInAction(() => {
@ -526,9 +530,9 @@ export class FeedModel {
const slices = this.tuner.tune(res.data.feed, this.feedTuners) const slices = this.tuner.tune(res.data.feed, this.feedTuners)
const toAppend: FeedSliceModel[] = [] const toAppend: PostsFeedSliceModel[] = []
for (const slice of slices) { for (const slice of slices) {
const sliceModel = new FeedSliceModel( const sliceModel = new PostsFeedSliceModel(
this.rootStore, this.rootStore,
`item-${_idCounter++}`, `item-${_idCounter++}`,
slice, slice,

View File

@ -1,7 +1,7 @@
import {makeAutoObservable, runInAction} from 'mobx' import {makeAutoObservable, runInAction} from 'mobx'
import {AtUri} from '../../third-party/uri' import {AtUri} from '../../../third-party/uri'
import {AppBskyFeedGetLikes as GetLikes} from '@atproto/api' import {AppBskyFeedGetLikes as GetLikes} from '@atproto/api'
import {RootStoreModel} from './root-store' import {RootStoreModel} from '../root-store'
import {cleanError} from 'lib/strings/errors' import {cleanError} from 'lib/strings/errors'
import {bundleAsync} from 'lib/async/bundle' import {bundleAsync} from 'lib/async/bundle'
import * as apilib from 'lib/api/index' import * as apilib from 'lib/api/index'
@ -10,7 +10,7 @@ const PAGE_SIZE = 30
export type LikeItem = GetLikes.Like export type LikeItem = GetLikes.Like
export class LikesViewModel { export class LikesModel {
// state // state
isLoading = false isLoading = false
isRefreshing = false isRefreshing = false

View File

@ -1,10 +1,10 @@
import {makeAutoObservable, runInAction} from 'mobx' import {makeAutoObservable, runInAction} from 'mobx'
import {AtUri} from '../../third-party/uri' import {AtUri} from '../../../third-party/uri'
import { import {
AppBskyFeedGetRepostedBy as GetRepostedBy, AppBskyFeedGetRepostedBy as GetRepostedBy,
AppBskyActorDefs, AppBskyActorDefs,
} from '@atproto/api' } from '@atproto/api'
import {RootStoreModel} from './root-store' import {RootStoreModel} from '../root-store'
import {bundleAsync} from 'lib/async/bundle' import {bundleAsync} from 'lib/async/bundle'
import {cleanError} from 'lib/strings/errors' import {cleanError} from 'lib/strings/errors'
import * as apilib from 'lib/api/index' import * as apilib from 'lib/api/index'
@ -13,7 +13,7 @@ const PAGE_SIZE = 30
export type RepostedByItem = AppBskyActorDefs.ProfileViewBasic export type RepostedByItem = AppBskyActorDefs.ProfileViewBasic
export class RepostedByViewModel { export class RepostedByModel {
// state // state
isLoading = false isLoading = false
isRefreshing = false isRefreshing = false

View File

@ -3,7 +3,7 @@ import {
AppBskyGraphGetFollowers as GetFollowers, AppBskyGraphGetFollowers as GetFollowers,
AppBskyActorDefs as ActorDefs, AppBskyActorDefs as ActorDefs,
} from '@atproto/api' } from '@atproto/api'
import {RootStoreModel} from './root-store' import {RootStoreModel} from '../root-store'
import {cleanError} from 'lib/strings/errors' import {cleanError} from 'lib/strings/errors'
import {bundleAsync} from 'lib/async/bundle' import {bundleAsync} from 'lib/async/bundle'
@ -11,7 +11,7 @@ const PAGE_SIZE = 30
export type FollowerItem = ActorDefs.ProfileViewBasic export type FollowerItem = ActorDefs.ProfileViewBasic
export class UserFollowersViewModel { export class UserFollowersModel {
// state // state
isLoading = false isLoading = false
isRefreshing = false isRefreshing = false

View File

@ -3,7 +3,7 @@ import {
AppBskyGraphGetFollows as GetFollows, AppBskyGraphGetFollows as GetFollows,
AppBskyActorDefs as ActorDefs, AppBskyActorDefs as ActorDefs,
} from '@atproto/api' } from '@atproto/api'
import {RootStoreModel} from './root-store' import {RootStoreModel} from '../root-store'
import {cleanError} from 'lib/strings/errors' import {cleanError} from 'lib/strings/errors'
import {bundleAsync} from 'lib/async/bundle' import {bundleAsync} from 'lib/async/bundle'
@ -11,7 +11,7 @@ const PAGE_SIZE = 30
export type FollowItem = ActorDefs.ProfileViewBasic export type FollowItem = ActorDefs.ProfileViewBasic
export class UserFollowsViewModel { export class UserFollowsModel {
// state // state
isLoading = false isLoading = false
isRefreshing = false isRefreshing = false

View File

@ -1,7 +1,7 @@
import {makeAutoObservable, runInAction} from 'mobx' import {makeAutoObservable, runInAction} from 'mobx'
import {RootStoreModel} from './root-store' import {RootStoreModel} from './root-store'
import {FeedModel} from './feed-view' import {PostsFeedModel} from './feeds/posts'
import {NotificationsViewModel} from './notifications-view' import {NotificationsFeedModel} from './feeds/notifications'
import {MyFollowsCache} from './cache/my-follows' import {MyFollowsCache} from './cache/my-follows'
import {isObj, hasProp} from 'lib/type-guards' import {isObj, hasProp} from 'lib/type-guards'
@ -13,8 +13,8 @@ export class MeModel {
avatar: string = '' avatar: string = ''
followsCount: number | undefined followsCount: number | undefined
followersCount: number | undefined followersCount: number | undefined
mainFeed: FeedModel mainFeed: PostsFeedModel
notifications: NotificationsViewModel notifications: NotificationsFeedModel
follows: MyFollowsCache follows: MyFollowsCache
constructor(public rootStore: RootStoreModel) { constructor(public rootStore: RootStoreModel) {
@ -23,10 +23,10 @@ export class MeModel {
{rootStore: false, serialize: false, hydrate: false}, {rootStore: false, serialize: false, hydrate: false},
{autoBind: true}, {autoBind: true},
) )
this.mainFeed = new FeedModel(this.rootStore, 'home', { this.mainFeed = new PostsFeedModel(this.rootStore, 'home', {
algorithm: 'reverse-chronological', algorithm: 'reverse-chronological',
}) })
this.notifications = new NotificationsViewModel(this.rootStore, {}) this.notifications = new NotificationsFeedModel(this.rootStore, {})
this.follows = new MyFollowsCache(this.rootStore) this.follows = new MyFollowsCache(this.rootStore)
} }

View File

@ -12,9 +12,9 @@ import {isObj, hasProp} from 'lib/type-guards'
import {LogModel} from './log' import {LogModel} from './log'
import {SessionModel} from './session' import {SessionModel} from './session'
import {ShellUiModel} from './ui/shell' import {ShellUiModel} from './ui/shell'
import {ProfilesViewModel} from './profiles-view' import {ProfilesCache} from './cache/profiles-view'
import {LinkMetasCache} from './cache/link-metas' import {LinkMetasCache} from './cache/link-metas'
import {NotificationsViewItemModel} from './notifications-view' import {NotificationsFeedItemModel} from './feeds/notifications'
import {MeModel} from './me' import {MeModel} from './me'
import {PreferencesModel} from './ui/preferences' import {PreferencesModel} from './ui/preferences'
import {resetToTab} from '../../Navigation' import {resetToTab} from '../../Navigation'
@ -36,7 +36,7 @@ export class RootStoreModel {
shell = new ShellUiModel(this) shell = new ShellUiModel(this)
preferences = new PreferencesModel() preferences = new PreferencesModel()
me = new MeModel(this) me = new MeModel(this)
profiles = new ProfilesViewModel(this) profiles = new ProfilesCache(this)
linkMetas = new LinkMetasCache(this) linkMetas = new LinkMetasCache(this)
imageSizes = new ImageSizesCache() imageSizes = new ImageSizesCache()
@ -205,11 +205,11 @@ export class RootStoreModel {
// a notification has been queued for push // a notification has been queued for push
onPushNotification( onPushNotification(
handler: (notif: NotificationsViewItemModel) => void, handler: (notif: NotificationsFeedItemModel) => void,
): EmitterSubscription { ): EmitterSubscription {
return DeviceEventEmitter.addListener('push-notification', handler) return DeviceEventEmitter.addListener('push-notification', handler)
} }
emitPushNotification(notif: NotificationsViewItemModel) { emitPushNotification(notif: NotificationsFeedItemModel) {
DeviceEventEmitter.emit('push-notification', notif) DeviceEventEmitter.emit('push-notification', notif)
} }

View File

@ -1,7 +1,7 @@
import {makeAutoObservable} from 'mobx' import {makeAutoObservable} from 'mobx'
import {RootStoreModel} from '../root-store' import {RootStoreModel} from '../root-store'
import {ProfileViewModel} from '../profile-view' import {ProfileModel} from '../content/profile'
import {FeedModel} from '../feed-view' import {PostsFeedModel} from '../feeds/posts'
export enum Sections { export enum Sections {
Posts = 'Posts', Posts = 'Posts',
@ -20,8 +20,8 @@ export class ProfileUiModel {
static EMPTY_ITEM = {_reactKey: '__empty__'} static EMPTY_ITEM = {_reactKey: '__empty__'}
// data // data
profile: ProfileViewModel profile: ProfileModel
feed: FeedModel feed: PostsFeedModel
// ui state // ui state
selectedViewIndex = 0 selectedViewIndex = 0
@ -38,14 +38,14 @@ export class ProfileUiModel {
}, },
{autoBind: true}, {autoBind: true},
) )
this.profile = new ProfileViewModel(rootStore, {actor: params.user}) this.profile = new ProfileModel(rootStore, {actor: params.user})
this.feed = new FeedModel(rootStore, 'author', { this.feed = new PostsFeedModel(rootStore, 'author', {
actor: params.user, actor: params.user,
limit: 10, limit: 10,
}) })
} }
get currentView(): FeedModel { get currentView(): PostsFeedModel {
if ( if (
this.selectedView === Sections.Posts || this.selectedView === Sections.Posts ||
this.selectedView === Sections.PostsWithReplies this.selectedView === Sections.PostsWithReplies
@ -137,7 +137,7 @@ export class ProfileUiModel {
async update() { async update() {
const view = this.currentView const view = this.currentView
if (view instanceof FeedModel) { if (view instanceof PostsFeedModel) {
await view.update() await view.update()
} }
} }

View File

@ -1,7 +1,7 @@
import {AppBskyEmbedRecord} from '@atproto/api' import {AppBskyEmbedRecord} from '@atproto/api'
import {RootStoreModel} from '../root-store' import {RootStoreModel} from '../root-store'
import {makeAutoObservable} from 'mobx' import {makeAutoObservable} from 'mobx'
import {ProfileViewModel} from '../profile-view' import {ProfileModel} from '../content/profile'
import {isObj, hasProp} from 'lib/type-guards' import {isObj, hasProp} from 'lib/type-guards'
import {PickedMedia} from 'lib/media/types' import {PickedMedia} from 'lib/media/types'
@ -14,7 +14,7 @@ export interface ConfirmModal {
export interface EditProfileModal { export interface EditProfileModal {
name: 'edit-profile' name: 'edit-profile'
profileView: ProfileViewModel profileView: ProfileModel
onUpdate?: () => void onUpdate?: () => void
} }
@ -77,7 +77,7 @@ interface LightboxModel {}
export class ProfileImageLightbox implements LightboxModel { export class ProfileImageLightbox implements LightboxModel {
name = 'profile-image' name = 'profile-image'
constructor(public profileView: ProfileViewModel) { constructor(public profileView: ProfileModel) {
makeAutoObservable(this) makeAutoObservable(this)
} }
} }

View File

@ -15,7 +15,7 @@ import LinearGradient from 'react-native-linear-gradient'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {RichText} from '@atproto/api' import {RichText} from '@atproto/api'
import {useAnalytics} from 'lib/analytics' import {useAnalytics} from 'lib/analytics'
import {UserAutocompleteViewModel} from 'state/models/user-autocomplete-view' import {UserAutocompleteModel} from 'state/models/discovery/user-autocomplete'
import {ExternalEmbed} from './ExternalEmbed' import {ExternalEmbed} from './ExternalEmbed'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
import * as Toast from '../util/Toast' import * as Toast from '../util/Toast'
@ -69,8 +69,8 @@ export const ComposePost = observer(function ComposePost({
) )
const [selectedPhotos, setSelectedPhotos] = React.useState<string[]>([]) const [selectedPhotos, setSelectedPhotos] = React.useState<string[]>([])
const autocompleteView = React.useMemo<UserAutocompleteViewModel>( const autocompleteView = React.useMemo<UserAutocompleteModel>(
() => new UserAutocompleteViewModel(store), () => new UserAutocompleteModel(store),
[store], [store],
) )

View File

@ -11,7 +11,7 @@ import PasteInput, {
} from '@mattermost/react-native-paste-input' } from '@mattermost/react-native-paste-input'
import {AppBskyRichtextFacet, RichText} from '@atproto/api' import {AppBskyRichtextFacet, RichText} from '@atproto/api'
import isEqual from 'lodash.isequal' import isEqual from 'lodash.isequal'
import {UserAutocompleteViewModel} from 'state/models/user-autocomplete-view' import {UserAutocompleteModel} from 'state/models/discovery/user-autocomplete'
import {Autocomplete} from './mobile/Autocomplete' import {Autocomplete} from './mobile/Autocomplete'
import {Text} from 'view/com/util/text/Text' import {Text} from 'view/com/util/text/Text'
import {useStores} from 'state/index' import {useStores} from 'state/index'
@ -36,7 +36,7 @@ interface TextInputProps {
richtext: RichText richtext: RichText
placeholder: string placeholder: string
suggestedLinks: Set<string> suggestedLinks: Set<string>
autocompleteView: UserAutocompleteViewModel autocompleteView: UserAutocompleteModel
setRichText: (v: RichText) => void setRichText: (v: RichText) => void
onPhotoPasted: (uri: string) => void onPhotoPasted: (uri: string) => void
onSuggestedLinksChanged: (uris: Set<string>) => void onSuggestedLinksChanged: (uris: Set<string>) => void

View File

@ -9,7 +9,7 @@ import {Paragraph} from '@tiptap/extension-paragraph'
import {Placeholder} from '@tiptap/extension-placeholder' import {Placeholder} from '@tiptap/extension-placeholder'
import {Text} from '@tiptap/extension-text' import {Text} from '@tiptap/extension-text'
import isEqual from 'lodash.isequal' import isEqual from 'lodash.isequal'
import {UserAutocompleteViewModel} from 'state/models/user-autocomplete-view' import {UserAutocompleteModel} from 'state/models/discovery/user-autocomplete'
import {createSuggestion} from './web/Autocomplete' import {createSuggestion} from './web/Autocomplete'
export interface TextInputRef { export interface TextInputRef {
@ -21,7 +21,7 @@ interface TextInputProps {
richtext: RichText richtext: RichText
placeholder: string placeholder: string
suggestedLinks: Set<string> suggestedLinks: Set<string>
autocompleteView: UserAutocompleteViewModel autocompleteView: UserAutocompleteModel
setRichText: (v: RichText) => void setRichText: (v: RichText) => void
onPhotoPasted: (uri: string) => void onPhotoPasted: (uri: string) => void
onSuggestedLinksChanged: (uris: Set<string>) => void onSuggestedLinksChanged: (uris: Set<string>) => void

View File

@ -1,7 +1,7 @@
import React, {useEffect} from 'react' import React, {useEffect} from 'react'
import {Animated, TouchableOpacity, StyleSheet, View} from 'react-native' import {Animated, TouchableOpacity, StyleSheet, View} from 'react-native'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {UserAutocompleteViewModel} from 'state/models/user-autocomplete-view' import {UserAutocompleteModel} from 'state/models/discovery/user-autocomplete'
import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
import {Text} from 'view/com/util/text/Text' import {Text} from 'view/com/util/text/Text'
@ -11,7 +11,7 @@ export const Autocomplete = observer(
view, view,
onSelect, onSelect,
}: { }: {
view: UserAutocompleteViewModel view: UserAutocompleteModel
onSelect: (item: string) => void onSelect: (item: string) => void
}) => { }) => {
const pal = usePalette('default') const pal = usePalette('default')

View File

@ -11,7 +11,7 @@ import {
SuggestionProps, SuggestionProps,
SuggestionKeyDownProps, SuggestionKeyDownProps,
} from '@tiptap/suggestion' } from '@tiptap/suggestion'
import {UserAutocompleteViewModel} from 'state/models/user-autocomplete-view' import {UserAutocompleteModel} from 'state/models/discovery/user-autocomplete'
interface MentionListRef { interface MentionListRef {
onKeyDown: (props: SuggestionKeyDownProps) => boolean onKeyDown: (props: SuggestionKeyDownProps) => boolean
@ -20,7 +20,7 @@ interface MentionListRef {
export function createSuggestion({ export function createSuggestion({
autocompleteView, autocompleteView,
}: { }: {
autocompleteView: UserAutocompleteViewModel autocompleteView: UserAutocompleteModel
}): Omit<SuggestionOptions, 'editor'> { }): Omit<SuggestionOptions, 'editor'> {
return { return {
async items({query}) { async items({query}) {

View File

@ -2,7 +2,7 @@ import React from 'react'
import {ActivityIndicator, StyleSheet, View} from 'react-native' import {ActivityIndicator, StyleSheet, View} from 'react-native'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {SuggestedPostsView} from 'state/models/suggested-posts-view' import {SuggestedPostsModel} from 'state/models/discovery/suggested-posts'
import {s} from 'lib/styles' import {s} from 'lib/styles'
import {FeedItem as Post} from '../posts/FeedItem' import {FeedItem as Post} from '../posts/FeedItem'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
@ -11,8 +11,8 @@ import {usePalette} from 'lib/hooks/usePalette'
export const SuggestedPosts = observer(() => { export const SuggestedPosts = observer(() => {
const pal = usePalette('default') const pal = usePalette('default')
const store = useStores() const store = useStores()
const suggestedPostsView = React.useMemo<SuggestedPostsView>( const suggestedPostsView = React.useMemo<SuggestedPostsModel>(
() => new SuggestedPostsView(store), () => new SuggestedPostsModel(store),
[store], [store],
) )

View File

@ -12,7 +12,7 @@ import {PickedMedia} from '../../../lib/media/picker'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
import {ErrorMessage} from '../util/error/ErrorMessage' import {ErrorMessage} from '../util/error/ErrorMessage'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {ProfileViewModel} from 'state/models/profile-view' import {ProfileModel} from 'state/models/content/profile'
import {s, colors, gradients} from 'lib/styles' import {s, colors, gradients} from 'lib/styles'
import {enforceLen} from 'lib/strings/helpers' import {enforceLen} from 'lib/strings/helpers'
import {MAX_DISPLAY_NAME, MAX_DESCRIPTION} from 'lib/constants' import {MAX_DISPLAY_NAME, MAX_DESCRIPTION} from 'lib/constants'
@ -30,7 +30,7 @@ export function Component({
profileView, profileView,
onUpdate, onUpdate,
}: { }: {
profileView: ProfileViewModel profileView: ProfileModel
onUpdate?: () => void onUpdate?: () => void
}) { }) {
const store = useStores() const store = useStores()

View File

@ -2,7 +2,7 @@ import React, {MutableRefObject} from 'react'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {CenteredView, FlatList} from '../util/Views' import {CenteredView, FlatList} from '../util/Views'
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native' import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
import {NotificationsViewModel} from 'state/models/notifications-view' import {NotificationsFeedModel} from 'state/models/feeds/notifications'
import {FeedItem} from './FeedItem' import {FeedItem} from './FeedItem'
import {NotificationFeedLoadingPlaceholder} from '../util/LoadingPlaceholder' import {NotificationFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
import {ErrorMessage} from '../util/error/ErrorMessage' import {ErrorMessage} from '../util/error/ErrorMessage'
@ -19,7 +19,7 @@ export const Feed = observer(function Feed({
onPressTryAgain, onPressTryAgain,
onScroll, onScroll,
}: { }: {
view: NotificationsViewModel view: NotificationsFeedModel
scrollElRef?: MutableRefObject<FlatList<any> | null> scrollElRef?: MutableRefObject<FlatList<any> | null>
onPressTryAgain?: () => void onPressTryAgain?: () => void
onScroll?: OnScrollCb onScroll?: OnScrollCb

View File

@ -14,8 +14,8 @@ import {
FontAwesomeIconStyle, FontAwesomeIconStyle,
Props, Props,
} from '@fortawesome/react-native-fontawesome' } from '@fortawesome/react-native-fontawesome'
import {NotificationsViewItemModel} from 'state/models/notifications-view' import {NotificationsFeedItemModel} from 'state/models/feeds/notifications'
import {PostThreadViewModel} from 'state/models/post-thread-view' import {PostThreadModel} from 'state/models/content/post-thread'
import {s, colors} from 'lib/styles' import {s, colors} from 'lib/styles'
import {ago} from 'lib/strings/time' import {ago} from 'lib/strings/time'
import {pluralize} from 'lib/strings/helpers' import {pluralize} from 'lib/strings/helpers'
@ -42,7 +42,7 @@ interface Author {
export const FeedItem = observer(function FeedItem({ export const FeedItem = observer(function FeedItem({
item, item,
}: { }: {
item: NotificationsViewItemModel item: NotificationsFeedItemModel
}) { }) {
const pal = usePalette('default') const pal = usePalette('default')
const [isAuthorsExpanded, setAuthorsExpanded] = React.useState<boolean>(false) const [isAuthorsExpanded, setAuthorsExpanded] = React.useState<boolean>(false)
@ -338,7 +338,7 @@ function ExpandedAuthorsList({
function AdditionalPostText({ function AdditionalPostText({
additionalPost, additionalPost,
}: { }: {
additionalPost?: PostThreadViewModel additionalPost?: PostThreadModel
}) { }) {
const pal = usePalette('default') const pal = usePalette('default')
if ( if (

View File

@ -2,7 +2,7 @@ import React, {useEffect} from 'react'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native' import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
import {CenteredView, FlatList} from '../util/Views' import {CenteredView, FlatList} from '../util/Views'
import {LikesViewModel, LikeItem} from 'state/models/likes-view' import {LikesModel, LikeItem} from 'state/models/lists/likes'
import {ErrorMessage} from '../util/error/ErrorMessage' import {ErrorMessage} from '../util/error/ErrorMessage'
import {ProfileCardWithFollowBtn} from '../profile/ProfileCard' import {ProfileCardWithFollowBtn} from '../profile/ProfileCard'
import {useStores} from 'state/index' import {useStores} from 'state/index'
@ -11,10 +11,7 @@ import {usePalette} from 'lib/hooks/usePalette'
export const PostLikedBy = observer(function ({uri}: {uri: string}) { export const PostLikedBy = observer(function ({uri}: {uri: string}) {
const pal = usePalette('default') const pal = usePalette('default')
const store = useStores() const store = useStores()
const view = React.useMemo( const view = React.useMemo(() => new LikesModel(store, {uri}), [store, uri])
() => new LikesViewModel(store, {uri}),
[store, uri],
)
useEffect(() => { useEffect(() => {
view.loadMore().catch(err => store.log.error('Failed to fetch likes', err)) view.loadMore().catch(err => store.log.error('Failed to fetch likes', err))

View File

@ -2,10 +2,7 @@ import React, {useEffect} from 'react'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native' import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
import {CenteredView, FlatList} from '../util/Views' import {CenteredView, FlatList} from '../util/Views'
import { import {RepostedByModel, RepostedByItem} from 'state/models/lists/reposted-by'
RepostedByViewModel,
RepostedByItem,
} from 'state/models/reposted-by-view'
import {ProfileCardWithFollowBtn} from '../profile/ProfileCard' import {ProfileCardWithFollowBtn} from '../profile/ProfileCard'
import {ErrorMessage} from '../util/error/ErrorMessage' import {ErrorMessage} from '../util/error/ErrorMessage'
import {useStores} from 'state/index' import {useStores} from 'state/index'
@ -19,7 +16,7 @@ export const PostRepostedBy = observer(function PostRepostedBy({
const pal = usePalette('default') const pal = usePalette('default')
const store = useStores() const store = useStores()
const view = React.useMemo( const view = React.useMemo(
() => new RepostedByViewModel(store, {uri}), () => new RepostedByModel(store, {uri}),
[store, uri], [store, uri],
) )

View File

@ -9,9 +9,9 @@ import {
} from 'react-native' } from 'react-native'
import {CenteredView, FlatList} from '../util/Views' import {CenteredView, FlatList} from '../util/Views'
import { import {
PostThreadViewModel, PostThreadModel,
PostThreadViewPostModel, PostThreadItemModel,
} from 'state/models/post-thread-view' } from 'state/models/content/post-thread'
import { import {
FontAwesomeIcon, FontAwesomeIcon,
FontAwesomeIconStyle, FontAwesomeIconStyle,
@ -31,7 +31,7 @@ const BOTTOM_BORDER = {
_reactKey: '__bottom_border__', _reactKey: '__bottom_border__',
_isHighlightedPost: false, _isHighlightedPost: false,
} }
type YieldedItem = PostThreadViewPostModel | typeof REPLY_PROMPT type YieldedItem = PostThreadItemModel | typeof REPLY_PROMPT
export const PostThread = observer(function PostThread({ export const PostThread = observer(function PostThread({
uri, uri,
@ -39,7 +39,7 @@ export const PostThread = observer(function PostThread({
onPressReply, onPressReply,
}: { }: {
uri: string uri: string
view: PostThreadViewModel view: PostThreadModel
onPressReply: () => void onPressReply: () => void
}) { }) {
const pal = usePalette('default') const pal = usePalette('default')
@ -109,7 +109,7 @@ export const PostThread = observer(function PostThread({
// I could find to get a border positioned directly under the last item // I could find to get a border positioned directly under the last item
// -prf // -prf
return <View style={[styles.bottomBorder, pal.border]} /> return <View style={[styles.bottomBorder, pal.border]} />
} else if (item instanceof PostThreadViewPostModel) { } else if (item instanceof PostThreadItemModel) {
return <PostThreadItem item={item} onPostReply={onRefresh} /> return <PostThreadItem item={item} onPostReply={onRefresh} />
} }
return <></> return <></>
@ -187,14 +187,14 @@ export const PostThread = observer(function PostThread({
}) })
function* flattenThread( function* flattenThread(
post: PostThreadViewPostModel, post: PostThreadItemModel,
isAscending = false, isAscending = false,
): Generator<YieldedItem, void> { ): Generator<YieldedItem, void> {
if (post.parent) { if (post.parent) {
if ('notFound' in post.parent && post.parent.notFound) { if ('notFound' in post.parent && post.parent.notFound) {
// TODO render not found // TODO render not found
} else { } else {
yield* flattenThread(post.parent as PostThreadViewPostModel, true) yield* flattenThread(post.parent as PostThreadItemModel, true)
} }
} }
yield post yield post
@ -206,7 +206,7 @@ function* flattenThread(
if ('notFound' in reply && reply.notFound) { if ('notFound' in reply && reply.notFound) {
// TODO render not found // TODO render not found
} else { } else {
yield* flattenThread(reply as PostThreadViewPostModel) yield* flattenThread(reply as PostThreadItemModel)
} }
} }
} else if (!isAscending && !post.parent && post.post.replyCount) { } else if (!isAscending && !post.parent && post.post.replyCount) {

View File

@ -7,7 +7,7 @@ import {
FontAwesomeIcon, FontAwesomeIcon,
FontAwesomeIconStyle, FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome' } from '@fortawesome/react-native-fontawesome'
import {PostThreadViewPostModel} from 'state/models/post-thread-view' import {PostThreadItemModel} from 'state/models/content/post-thread'
import {Link} from '../util/Link' import {Link} from '../util/Link'
import {RichText} from '../util/text/RichText' import {RichText} from '../util/text/RichText'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
@ -31,7 +31,7 @@ export const PostThreadItem = observer(function PostThreadItem({
item, item,
onPostReply, onPostReply,
}: { }: {
item: PostThreadViewPostModel item: PostThreadItemModel
onPostReply: () => void onPostReply: () => void
}) { }) {
const pal = usePalette('default') const pal = usePalette('default')

View File

@ -11,7 +11,7 @@ import {observer} from 'mobx-react-lite'
import Clipboard from '@react-native-clipboard/clipboard' import Clipboard from '@react-native-clipboard/clipboard'
import {AtUri} from '../../../third-party/uri' import {AtUri} from '../../../third-party/uri'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {PostThreadViewModel} from 'state/models/post-thread-view' import {PostThreadModel} from 'state/models/content/post-thread'
import {Link} from '../util/Link' import {Link} from '../util/Link'
import {UserInfoText} from '../util/UserInfoText' import {UserInfoText} from '../util/UserInfoText'
import {PostMeta} from '../util/PostMeta' import {PostMeta} from '../util/PostMeta'
@ -34,21 +34,21 @@ export const Post = observer(function Post({
style, style,
}: { }: {
uri: string uri: string
initView?: PostThreadViewModel initView?: PostThreadModel
showReplyLine?: boolean showReplyLine?: boolean
hideError?: boolean hideError?: boolean
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
}) { }) {
const pal = usePalette('default') const pal = usePalette('default')
const store = useStores() const store = useStores()
const [view, setView] = useState<PostThreadViewModel | undefined>(initView) const [view, setView] = useState<PostThreadModel | undefined>(initView)
const [deleted, setDeleted] = useState(false) const [deleted, setDeleted] = useState(false)
useEffect(() => { useEffect(() => {
if (initView || view?.params.uri === uri) { if (initView || view?.params.uri === uri) {
return // no change needed? or trigger refresh? return // no change needed? or trigger refresh?
} }
const newView = new PostThreadViewModel(store, {uri, depth: 0}) const newView = new PostThreadModel(store, {uri, depth: 0})
setView(newView) setView(newView)
newView.setup().catch(err => store.log.error('Failed to fetch post', err)) newView.setup().catch(err => store.log.error('Failed to fetch post', err))
}, [initView, uri, view?.params.uri, store]) }, [initView, uri, view?.params.uri, store])

View File

@ -4,7 +4,7 @@ import {StyleProp, StyleSheet, TextStyle, View} from 'react-native'
import {LoadingPlaceholder} from '../util/LoadingPlaceholder' import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
import {ErrorMessage} from '../util/error/ErrorMessage' import {ErrorMessage} from '../util/error/ErrorMessage'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
import {PostModel} from 'state/models/post' import {PostModel} from 'state/models/content/post'
import {useStores} from 'state/index' import {useStores} from 'state/index'
export const PostText = observer(function PostText({ export const PostText = observer(function PostText({

View File

@ -11,7 +11,7 @@ import {
import {FlatList} from '../util/Views' import {FlatList} from '../util/Views'
import {PostFeedLoadingPlaceholder} from '../util/LoadingPlaceholder' import {PostFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
import {ErrorMessage} from '../util/error/ErrorMessage' import {ErrorMessage} from '../util/error/ErrorMessage'
import {FeedModel} from 'state/models/feed-view' import {PostsFeedModel} from 'state/models/feeds/posts'
import {FeedSlice} from './FeedSlice' import {FeedSlice} from './FeedSlice'
import {OnScrollCb} from 'lib/hooks/useOnMainScroll' import {OnScrollCb} from 'lib/hooks/useOnMainScroll'
import {s} from 'lib/styles' import {s} from 'lib/styles'
@ -33,7 +33,7 @@ export const Feed = observer(function Feed({
testID, testID,
headerOffset = 0, headerOffset = 0,
}: { }: {
feed: FeedModel feed: PostsFeedModel
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
showPostFollowBtn?: boolean showPostFollowBtn?: boolean
scrollElRef?: MutableRefObject<FlatList<any> | null> scrollElRef?: MutableRefObject<FlatList<any> | null>

View File

@ -7,7 +7,7 @@ import {
FontAwesomeIcon, FontAwesomeIcon,
FontAwesomeIconStyle, FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome' } from '@fortawesome/react-native-fontawesome'
import {FeedItemModel} from 'state/models/feed-view' import {PostsFeedItemModel} from 'state/models/feeds/posts'
import {Link, DesktopWebTextLink} from '../util/Link' import {Link, DesktopWebTextLink} from '../util/Link'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
import {UserInfoText} from '../util/UserInfoText' import {UserInfoText} from '../util/UserInfoText'
@ -30,7 +30,7 @@ export const FeedItem = observer(function ({
showFollowBtn, showFollowBtn,
ignoreMuteFor, ignoreMuteFor,
}: { }: {
item: FeedItemModel item: PostsFeedItemModel
isThreadChild?: boolean isThreadChild?: boolean
isThreadParent?: boolean isThreadParent?: boolean
showReplyLine?: boolean showReplyLine?: boolean

View File

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import {StyleSheet, View} from 'react-native' import {StyleSheet, View} from 'react-native'
import {FeedSliceModel} from 'state/models/feed-view' import {PostsFeedSliceModel} from 'state/models/feeds/posts'
import {AtUri} from '../../../third-party/uri' import {AtUri} from '../../../third-party/uri'
import {Link} from '../util/Link' import {Link} from '../util/Link'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
@ -13,7 +13,7 @@ export function FeedSlice({
showFollowBtn, showFollowBtn,
ignoreMuteFor, ignoreMuteFor,
}: { }: {
slice: FeedSliceModel slice: PostsFeedSliceModel
showFollowBtn?: boolean showFollowBtn?: boolean
ignoreMuteFor?: string ignoreMuteFor?: string
}) { }) {
@ -66,7 +66,7 @@ export function FeedSlice({
) )
} }
function ViewFullThread({slice}: {slice: FeedSliceModel}) { function ViewFullThread({slice}: {slice: PostsFeedSliceModel}) {
const pal = usePalette('default') const pal = usePalette('default')
const itemHref = React.useMemo(() => { const itemHref = React.useMemo(() => {
const urip = new AtUri(slice.rootItem.post.uri) const urip = new AtUri(slice.rootItem.post.uri)

View File

@ -2,9 +2,9 @@ import React, {useEffect} from 'react'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native' import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
import { import {
UserFollowersViewModel, UserFollowersModel,
FollowerItem, FollowerItem,
} from 'state/models/user-followers-view' } from 'state/models/lists/user-followers'
import {CenteredView, FlatList} from '../util/Views' import {CenteredView, FlatList} from '../util/Views'
import {ErrorMessage} from '../util/error/ErrorMessage' import {ErrorMessage} from '../util/error/ErrorMessage'
import {ProfileCardWithFollowBtn} from './ProfileCard' import {ProfileCardWithFollowBtn} from './ProfileCard'
@ -19,7 +19,7 @@ export const ProfileFollowers = observer(function ProfileFollowers({
const pal = usePalette('default') const pal = usePalette('default')
const store = useStores() const store = useStores()
const view = React.useMemo( const view = React.useMemo(
() => new UserFollowersViewModel(store, {actor: name}), () => new UserFollowersModel(store, {actor: name}),
[store, name], [store, name],
) )

View File

@ -2,7 +2,7 @@ import React, {useEffect} from 'react'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native' import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
import {CenteredView, FlatList} from '../util/Views' import {CenteredView, FlatList} from '../util/Views'
import {UserFollowsViewModel, FollowItem} from 'state/models/user-follows-view' import {UserFollowsModel, FollowItem} from 'state/models/lists/user-follows'
import {ErrorMessage} from '../util/error/ErrorMessage' import {ErrorMessage} from '../util/error/ErrorMessage'
import {ProfileCardWithFollowBtn} from './ProfileCard' import {ProfileCardWithFollowBtn} from './ProfileCard'
import {useStores} from 'state/index' import {useStores} from 'state/index'
@ -16,7 +16,7 @@ export const ProfileFollows = observer(function ProfileFollows({
const pal = usePalette('default') const pal = usePalette('default')
const store = useStores() const store = useStores()
const view = React.useMemo( const view = React.useMemo(
() => new UserFollowsViewModel(store, {actor: name}), () => new UserFollowsModel(store, {actor: name}),
[store, name], [store, name],
) )

View File

@ -13,7 +13,7 @@ import {
} from '@fortawesome/react-native-fontawesome' } from '@fortawesome/react-native-fontawesome'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {BlurView} from '../util/BlurView' import {BlurView} from '../util/BlurView'
import {ProfileViewModel} from 'state/models/profile-view' import {ProfileModel} from 'state/models/content/profile'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {ProfileImageLightbox} from 'state/models/ui/shell' import {ProfileImageLightbox} from 'state/models/ui/shell'
import {pluralize} from 'lib/strings/helpers' import {pluralize} from 'lib/strings/helpers'
@ -34,13 +34,7 @@ import {isDesktopWeb} from 'platform/detection'
const BACK_HITSLOP = {left: 30, top: 30, right: 30, bottom: 30} const BACK_HITSLOP = {left: 30, top: 30, right: 30, bottom: 30}
export const ProfileHeader = observer( export const ProfileHeader = observer(
({ ({view, onRefreshAll}: {view: ProfileModel; onRefreshAll: () => void}) => {
view,
onRefreshAll,
}: {
view: ProfileViewModel
onRefreshAll: () => void
}) => {
const pal = usePalette('default') const pal = usePalette('default')
// loading // loading
@ -91,7 +85,7 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoaded({
view, view,
onRefreshAll, onRefreshAll,
}: { }: {
view: ProfileViewModel view: ProfileModel
onRefreshAll: () => void onRefreshAll: () => void
}) { }) {
const pal = usePalette('default') const pal = usePalette('default')

View File

@ -4,7 +4,7 @@ import {useFocusEffect, useIsFocused} from '@react-navigation/native'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import useAppState from 'react-native-appstate-hook' import useAppState from 'react-native-appstate-hook'
import {NativeStackScreenProps, HomeTabNavigatorParams} from 'lib/routes/types' import {NativeStackScreenProps, HomeTabNavigatorParams} from 'lib/routes/types'
import {FeedModel} from 'state/models/feed-view' import {PostsFeedModel} from 'state/models/feeds/posts'
import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {Feed} from '../com/posts/Feed' import {Feed} from '../com/posts/Feed'
import {FollowingEmptyState} from 'view/com/posts/FollowingEmptyState' import {FollowingEmptyState} from 'view/com/posts/FollowingEmptyState'
@ -26,7 +26,7 @@ export const HomeScreen = withAuthRequired((_opts: Props) => {
const [selectedPage, setSelectedPage] = React.useState(0) const [selectedPage, setSelectedPage] = React.useState(0)
const algoFeed = React.useMemo(() => { const algoFeed = React.useMemo(() => {
const feed = new FeedModel(store, 'goodstuff', {}) const feed = new PostsFeedModel(store, 'goodstuff', {})
feed.setup() feed.setup()
return feed return feed
}, [store]) }, [store])
@ -104,7 +104,7 @@ const FeedPage = observer(
renderEmptyState, renderEmptyState,
}: { }: {
testID?: string testID?: string
feed: FeedModel feed: PostsFeedModel
isPageFocused: boolean isPageFocused: boolean
renderEmptyState?: () => JSX.Element renderEmptyState?: () => JSX.Element
}) => { }) => {

View File

@ -7,7 +7,7 @@ import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewHeader} from '../com/util/ViewHeader' import {ViewHeader} from '../com/util/ViewHeader'
import {PostThread as PostThreadComponent} from '../com/post-thread/PostThread' import {PostThread as PostThreadComponent} from '../com/post-thread/PostThread'
import {ComposePrompt} from 'view/com/composer/Prompt' import {ComposePrompt} from 'view/com/composer/Prompt'
import {PostThreadViewModel} from 'state/models/post-thread-view' import {PostThreadModel} from 'state/models/content/post-thread'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {s} from 'lib/styles' import {s} from 'lib/styles'
import {useSafeAreaInsets} from 'react-native-safe-area-context' import {useSafeAreaInsets} from 'react-native-safe-area-context'
@ -22,8 +22,8 @@ export const PostThreadScreen = withAuthRequired(({route}: Props) => {
const safeAreaInsets = useSafeAreaInsets() const safeAreaInsets = useSafeAreaInsets()
const {name, rkey} = route.params const {name, rkey} = route.params
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey) const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
const view = useMemo<PostThreadViewModel>( const view = useMemo<PostThreadModel>(
() => new PostThreadViewModel(store, {uri}), () => new PostThreadModel(store, {uri}),
[store, uri], [store, uri],
) )

View File

@ -8,7 +8,7 @@ import {ViewSelector} from '../com/util/ViewSelector'
import {CenteredView} from '../com/util/Views' import {CenteredView} from '../com/util/Views'
import {ProfileUiModel} from 'state/models/ui/profile' import {ProfileUiModel} from 'state/models/ui/profile'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {FeedSliceModel} from 'state/models/feed-view' import {PostsFeedSliceModel} from 'state/models/feeds/posts'
import {ProfileHeader} from '../com/profile/ProfileHeader' import {ProfileHeader} from '../com/profile/ProfileHeader'
import {FeedSlice} from '../com/posts/FeedSlice' import {FeedSlice} from '../com/posts/FeedSlice'
import {PostFeedLoadingPlaceholder} from '../com/util/LoadingPlaceholder' import {PostFeedLoadingPlaceholder} from '../com/util/LoadingPlaceholder'
@ -124,7 +124,7 @@ export const ProfileScreen = withAuthRequired(
style={styles.emptyState} style={styles.emptyState}
/> />
) )
} else if (item instanceof FeedSliceModel) { } else if (item instanceof PostsFeedSliceModel) {
return <FeedSlice slice={item} ignoreMuteFor={uiState.profile.did} /> return <FeedSlice slice={item} ignoreMuteFor={uiState.profile.did} />
} }
return <View /> return <View />

View File

@ -16,7 +16,7 @@ import {
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {Text} from 'view/com/util/text/Text' import {Text} from 'view/com/util/text/Text'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {UserAutocompleteViewModel} from 'state/models/user-autocomplete-view' import {UserAutocompleteModel} from 'state/models/discovery/user-autocomplete'
import {SearchUIModel} from 'state/models/ui/search' import {SearchUIModel} from 'state/models/ui/search'
import {FoafsModel} from 'state/models/discovery/foafs' import {FoafsModel} from 'state/models/discovery/foafs'
import {SuggestedActorsModel} from 'state/models/discovery/suggested-actors' import {SuggestedActorsModel} from 'state/models/discovery/suggested-actors'
@ -37,8 +37,8 @@ export const SearchScreen = withAuthRequired(
const onMainScroll = useOnMainScroll(store) const onMainScroll = useOnMainScroll(store)
const [isInputFocused, setIsInputFocused] = React.useState<boolean>(false) const [isInputFocused, setIsInputFocused] = React.useState<boolean>(false)
const [query, setQuery] = React.useState<string>('') const [query, setQuery] = React.useState<string>('')
const autocompleteView = React.useMemo<UserAutocompleteViewModel>( const autocompleteView = React.useMemo<UserAutocompleteModel>(
() => new UserAutocompleteViewModel(store), () => new UserAutocompleteModel(store),
[store], [store],
) )
const foafs = React.useMemo<FoafsModel>( const foafs = React.useMemo<FoafsModel>(

View File

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import {TextInput, View, StyleSheet, TouchableOpacity} from 'react-native' import {TextInput, View, StyleSheet, TouchableOpacity} from 'react-native'
import {useNavigation, StackActions} from '@react-navigation/native' import {useNavigation, StackActions} from '@react-navigation/native'
import {UserAutocompleteViewModel} from 'state/models/user-autocomplete-view' import {UserAutocompleteModel} from 'state/models/discovery/user-autocomplete'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
@ -16,8 +16,8 @@ export const DesktopSearch = observer(function DesktopSearch() {
const textInput = React.useRef<TextInput>(null) const textInput = React.useRef<TextInput>(null)
const [isInputFocused, setIsInputFocused] = React.useState<boolean>(false) const [isInputFocused, setIsInputFocused] = React.useState<boolean>(false)
const [query, setQuery] = React.useState<string>('') const [query, setQuery] = React.useState<string>('')
const autocompleteView = React.useMemo<UserAutocompleteViewModel>( const autocompleteView = React.useMemo<UserAutocompleteModel>(
() => new UserAutocompleteViewModel(store), () => new UserAutocompleteModel(store),
[store], [store],
) )
const navigation = useNavigation<NavigationProp>() const navigation = useNavigation<NavigationProp>()