about summary refs log tree commit diff
path: root/src/state/models/lists
diff options
context:
space:
mode:
Diffstat (limited to 'src/state/models/lists')
-rw-r--r--src/state/models/lists/actor-feeds.ts123
-rw-r--r--src/state/models/lists/blocked-accounts.ts107
-rw-r--r--src/state/models/lists/likes.ts135
-rw-r--r--src/state/models/lists/lists-list.ts244
-rw-r--r--src/state/models/lists/muted-accounts.ts107
-rw-r--r--src/state/models/lists/reposted-by.ts136
-rw-r--r--src/state/models/lists/user-followers.ts121
-rw-r--r--src/state/models/lists/user-follows.ts121
8 files changed, 0 insertions, 1094 deletions
diff --git a/src/state/models/lists/actor-feeds.ts b/src/state/models/lists/actor-feeds.ts
deleted file mode 100644
index 29c01e536..000000000
--- a/src/state/models/lists/actor-feeds.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-import {makeAutoObservable} from 'mobx'
-import {AppBskyFeedGetActorFeeds as GetActorFeeds} from '@atproto/api'
-import {RootStoreModel} from '../root-store'
-import {bundleAsync} from 'lib/async/bundle'
-import {cleanError} from 'lib/strings/errors'
-import {FeedSourceModel} from '../content/feed-source'
-import {logger} from '#/logger'
-
-const PAGE_SIZE = 30
-
-export class ActorFeedsModel {
-  // state
-  isLoading = false
-  isRefreshing = false
-  hasLoaded = false
-  error = ''
-  hasMore = true
-  loadMoreCursor?: string
-
-  // data
-  feeds: FeedSourceModel[] = []
-
-  constructor(
-    public rootStore: RootStoreModel,
-    public params: GetActorFeeds.QueryParams,
-  ) {
-    makeAutoObservable(
-      this,
-      {
-        rootStore: false,
-      },
-      {autoBind: true},
-    )
-  }
-
-  get hasContent() {
-    return this.feeds.length > 0
-  }
-
-  get hasError() {
-    return this.error !== ''
-  }
-
-  get isEmpty() {
-    return this.hasLoaded && !this.hasContent
-  }
-
-  // public api
-  // =
-
-  async refresh() {
-    return this.loadMore(true)
-  }
-
-  clear() {
-    this.isLoading = false
-    this.isRefreshing = false
-    this.hasLoaded = false
-    this.error = ''
-    this.hasMore = true
-    this.loadMoreCursor = undefined
-    this.feeds = []
-  }
-
-  loadMore = bundleAsync(async (replace: boolean = false) => {
-    if (!replace && !this.hasMore) {
-      return
-    }
-    this._xLoading(replace)
-    try {
-      const res = await this.rootStore.agent.app.bsky.feed.getActorFeeds({
-        actor: this.params.actor,
-        limit: PAGE_SIZE,
-        cursor: replace ? undefined : this.loadMoreCursor,
-      })
-      if (replace) {
-        this._replaceAll(res)
-      } else {
-        this._appendAll(res)
-      }
-      this._xIdle()
-    } catch (e: any) {
-      this._xIdle(e)
-    }
-  })
-
-  // state transitions
-  // =
-
-  _xLoading(isRefreshing = false) {
-    this.isLoading = true
-    this.isRefreshing = isRefreshing
-    this.error = ''
-  }
-
-  _xIdle(err?: any) {
-    this.isLoading = false
-    this.isRefreshing = false
-    this.hasLoaded = true
-    this.error = cleanError(err)
-    if (err) {
-      logger.error('Failed to fetch user followers', {error: err})
-    }
-  }
-
-  // helper functions
-  // =
-
-  _replaceAll(res: GetActorFeeds.Response) {
-    this.feeds = []
-    this._appendAll(res)
-  }
-
-  _appendAll(res: GetActorFeeds.Response) {
-    this.loadMoreCursor = res.data.cursor
-    this.hasMore = !!this.loadMoreCursor
-    for (const f of res.data.feeds) {
-      const model = new FeedSourceModel(this.rootStore, f.uri)
-      model.hydrateFeedGenerator(f)
-      this.feeds.push(model)
-    }
-  }
-}
diff --git a/src/state/models/lists/blocked-accounts.ts b/src/state/models/lists/blocked-accounts.ts
deleted file mode 100644
index 5c3dbe7ce..000000000
--- a/src/state/models/lists/blocked-accounts.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-import {makeAutoObservable} from 'mobx'
-import {
-  AppBskyGraphGetBlocks as GetBlocks,
-  AppBskyActorDefs as ActorDefs,
-} from '@atproto/api'
-import {RootStoreModel} from '../root-store'
-import {cleanError} from 'lib/strings/errors'
-import {bundleAsync} from 'lib/async/bundle'
-import {logger} from '#/logger'
-
-const PAGE_SIZE = 30
-
-export class BlockedAccountsModel {
-  // state
-  isLoading = false
-  isRefreshing = false
-  hasLoaded = false
-  error = ''
-  hasMore = true
-  loadMoreCursor?: string
-
-  // data
-  blocks: ActorDefs.ProfileView[] = []
-
-  constructor(public rootStore: RootStoreModel) {
-    makeAutoObservable(
-      this,
-      {
-        rootStore: false,
-      },
-      {autoBind: true},
-    )
-  }
-
-  get hasContent() {
-    return this.blocks.length > 0
-  }
-
-  get hasError() {
-    return this.error !== ''
-  }
-
-  get isEmpty() {
-    return this.hasLoaded && !this.hasContent
-  }
-
-  // public api
-  // =
-
-  async refresh() {
-    return this.loadMore(true)
-  }
-
-  loadMore = bundleAsync(async (replace: boolean = false) => {
-    if (!replace && !this.hasMore) {
-      return
-    }
-    this._xLoading(replace)
-    try {
-      const res = await this.rootStore.agent.app.bsky.graph.getBlocks({
-        limit: PAGE_SIZE,
-        cursor: replace ? undefined : this.loadMoreCursor,
-      })
-      if (replace) {
-        this._replaceAll(res)
-      } else {
-        this._appendAll(res)
-      }
-      this._xIdle()
-    } catch (e: any) {
-      this._xIdle(e)
-    }
-  })
-
-  // state transitions
-  // =
-
-  _xLoading(isRefreshing = false) {
-    this.isLoading = true
-    this.isRefreshing = isRefreshing
-    this.error = ''
-  }
-
-  _xIdle(err?: any) {
-    this.isLoading = false
-    this.isRefreshing = false
-    this.hasLoaded = true
-    this.error = cleanError(err)
-    if (err) {
-      logger.error('Failed to fetch user followers', {error: err})
-    }
-  }
-
-  // helper functions
-  // =
-
-  _replaceAll(res: GetBlocks.Response) {
-    this.blocks = []
-    this._appendAll(res)
-  }
-
-  _appendAll(res: GetBlocks.Response) {
-    this.loadMoreCursor = res.data.cursor
-    this.hasMore = !!this.loadMoreCursor
-    this.blocks = this.blocks.concat(res.data.blocks)
-  }
-}
diff --git a/src/state/models/lists/likes.ts b/src/state/models/lists/likes.ts
deleted file mode 100644
index df20f09db..000000000
--- a/src/state/models/lists/likes.ts
+++ /dev/null
@@ -1,135 +0,0 @@
-import {makeAutoObservable, runInAction} from 'mobx'
-import {AtUri} from '@atproto/api'
-import {AppBskyFeedGetLikes as GetLikes} from '@atproto/api'
-import {RootStoreModel} from '../root-store'
-import {cleanError} from 'lib/strings/errors'
-import {bundleAsync} from 'lib/async/bundle'
-import * as apilib from 'lib/api/index'
-import {logger} from '#/logger'
-
-const PAGE_SIZE = 30
-
-export type LikeItem = GetLikes.Like
-
-export class LikesModel {
-  // state
-  isLoading = false
-  isRefreshing = false
-  hasLoaded = false
-  error = ''
-  resolvedUri = ''
-  params: GetLikes.QueryParams
-  hasMore = true
-  loadMoreCursor?: string
-
-  // data
-  uri: string = ''
-  likes: LikeItem[] = []
-
-  constructor(public rootStore: RootStoreModel, params: GetLikes.QueryParams) {
-    makeAutoObservable(
-      this,
-      {
-        rootStore: false,
-        params: false,
-      },
-      {autoBind: true},
-    )
-    this.params = params
-  }
-
-  get hasContent() {
-    return this.uri !== ''
-  }
-
-  get hasError() {
-    return this.error !== ''
-  }
-
-  get isEmpty() {
-    return this.hasLoaded && !this.hasContent
-  }
-
-  // public api
-  // =
-
-  async refresh() {
-    return this.loadMore(true)
-  }
-
-  loadMore = bundleAsync(async (replace: boolean = false) => {
-    if (!replace && !this.hasMore) {
-      return
-    }
-    this._xLoading(replace)
-    try {
-      if (!this.resolvedUri) {
-        await this._resolveUri()
-      }
-      const params = Object.assign({}, this.params, {
-        uri: this.resolvedUri,
-        limit: PAGE_SIZE,
-        cursor: replace ? undefined : this.loadMoreCursor,
-      })
-      const res = await this.rootStore.agent.getLikes(params)
-      if (replace) {
-        this._replaceAll(res)
-      } else {
-        this._appendAll(res)
-      }
-      this._xIdle()
-    } catch (e: any) {
-      this._xIdle(e)
-    }
-  })
-
-  // state transitions
-  // =
-
-  _xLoading(isRefreshing = false) {
-    this.isLoading = true
-    this.isRefreshing = isRefreshing
-    this.error = ''
-  }
-
-  _xIdle(err?: any) {
-    this.isLoading = false
-    this.isRefreshing = false
-    this.hasLoaded = true
-    this.error = cleanError(err)
-    if (err) {
-      logger.error('Failed to fetch likes', {error: err})
-    }
-  }
-
-  // helper functions
-  // =
-
-  async _resolveUri() {
-    const urip = new AtUri(this.params.uri)
-    if (!urip.host.startsWith('did:')) {
-      try {
-        urip.host = await apilib.resolveName(this.rootStore, urip.host)
-      } catch (e: any) {
-        this.error = e.toString()
-      }
-    }
-    runInAction(() => {
-      this.resolvedUri = urip.toString()
-    })
-  }
-
-  _replaceAll(res: GetLikes.Response) {
-    this.likes = []
-    this._appendAll(res)
-  }
-
-  _appendAll(res: GetLikes.Response) {
-    this.loadMoreCursor = res.data.cursor
-    this.hasMore = !!this.loadMoreCursor
-    this.rootStore.me.follows.hydrateMany(
-      res.data.likes.map(like => like.actor),
-    )
-    this.likes = this.likes.concat(res.data.likes)
-  }
-}
diff --git a/src/state/models/lists/lists-list.ts b/src/state/models/lists/lists-list.ts
deleted file mode 100644
index eb6291637..000000000
--- a/src/state/models/lists/lists-list.ts
+++ /dev/null
@@ -1,244 +0,0 @@
-import {makeAutoObservable} from 'mobx'
-import {AppBskyGraphDefs as GraphDefs} from '@atproto/api'
-import {RootStoreModel} from '../root-store'
-import {cleanError} from 'lib/strings/errors'
-import {bundleAsync} from 'lib/async/bundle'
-import {accumulate} from 'lib/async/accumulate'
-import {logger} from '#/logger'
-
-const PAGE_SIZE = 30
-
-export class ListsListModel {
-  // state
-  isLoading = false
-  isRefreshing = false
-  hasLoaded = false
-  error = ''
-  loadMoreError = ''
-  hasMore = true
-  loadMoreCursor?: string
-
-  // data
-  lists: GraphDefs.ListView[] = []
-
-  constructor(
-    public rootStore: RootStoreModel,
-    public source: 'mine' | 'my-curatelists' | 'my-modlists' | string,
-  ) {
-    makeAutoObservable(
-      this,
-      {
-        rootStore: false,
-      },
-      {autoBind: true},
-    )
-  }
-
-  get hasContent() {
-    return this.lists.length > 0
-  }
-
-  get hasError() {
-    return this.error !== ''
-  }
-
-  get isEmpty() {
-    return this.hasLoaded && !this.hasContent
-  }
-
-  get curatelists() {
-    return this.lists.filter(
-      list => list.purpose === 'app.bsky.graph.defs#curatelist',
-    )
-  }
-
-  get isCuratelistsEmpty() {
-    return this.hasLoaded && this.curatelists.length === 0
-  }
-
-  get modlists() {
-    return this.lists.filter(
-      list => list.purpose === 'app.bsky.graph.defs#modlist',
-    )
-  }
-
-  get isModlistsEmpty() {
-    return this.hasLoaded && this.modlists.length === 0
-  }
-
-  /**
-   * Removes posts from the feed upon deletion.
-   */
-  onListDeleted(uri: string) {
-    this.lists = this.lists.filter(l => l.uri !== uri)
-  }
-
-  // public api
-  // =
-
-  /**
-   * Register any event listeners. Returns a cleanup function.
-   */
-  registerListeners() {
-    const sub = this.rootStore.onListDeleted(this.onListDeleted.bind(this))
-    return () => sub.remove()
-  }
-
-  async refresh() {
-    return this.loadMore(true)
-  }
-
-  loadMore = bundleAsync(async (replace: boolean = false) => {
-    if (!replace && !this.hasMore) {
-      return
-    }
-    this._xLoading(replace)
-    try {
-      let cursor: string | undefined
-      let lists: GraphDefs.ListView[] = []
-      if (
-        this.source === 'mine' ||
-        this.source === 'my-curatelists' ||
-        this.source === 'my-modlists'
-      ) {
-        const promises = [
-          accumulate(cursor =>
-            this.rootStore.agent.app.bsky.graph
-              .getLists({
-                actor: this.rootStore.me.did,
-                cursor,
-                limit: 50,
-              })
-              .then(res => ({cursor: res.data.cursor, items: res.data.lists})),
-          ),
-        ]
-        if (this.source === 'my-modlists') {
-          promises.push(
-            accumulate(cursor =>
-              this.rootStore.agent.app.bsky.graph
-                .getListMutes({
-                  cursor,
-                  limit: 50,
-                })
-                .then(res => ({
-                  cursor: res.data.cursor,
-                  items: res.data.lists,
-                })),
-            ),
-          )
-          promises.push(
-            accumulate(cursor =>
-              this.rootStore.agent.app.bsky.graph
-                .getListBlocks({
-                  cursor,
-                  limit: 50,
-                })
-                .then(res => ({
-                  cursor: res.data.cursor,
-                  items: res.data.lists,
-                })),
-            ),
-          )
-        }
-        const resultset = await Promise.all(promises)
-        for (const res of resultset) {
-          for (let list of res) {
-            if (
-              this.source === 'my-curatelists' &&
-              list.purpose !== 'app.bsky.graph.defs#curatelist'
-            ) {
-              continue
-            }
-            if (
-              this.source === 'my-modlists' &&
-              list.purpose !== 'app.bsky.graph.defs#modlist'
-            ) {
-              continue
-            }
-            if (!lists.find(l => l.uri === list.uri)) {
-              lists.push(list)
-            }
-          }
-        }
-      } else {
-        const res = await this.rootStore.agent.app.bsky.graph.getLists({
-          actor: this.source,
-          limit: PAGE_SIZE,
-          cursor: replace ? undefined : this.loadMoreCursor,
-        })
-        lists = res.data.lists
-        cursor = res.data.cursor
-      }
-      if (replace) {
-        this._replaceAll({lists, cursor})
-      } else {
-        this._appendAll({lists, cursor})
-      }
-      this._xIdle()
-    } catch (e: any) {
-      this._xIdle(replace ? e : undefined, !replace ? e : undefined)
-    }
-  })
-
-  /**
-   * Attempt to load more again after a failure
-   */
-  async retryLoadMore() {
-    this.loadMoreError = ''
-    this.hasMore = true
-    return this.loadMore()
-  }
-
-  // state transitions
-  // =
-
-  _xLoading(isRefreshing = false) {
-    this.isLoading = true
-    this.isRefreshing = isRefreshing
-    this.error = ''
-  }
-
-  _xIdle(err?: any, loadMoreErr?: any) {
-    this.isLoading = false
-    this.isRefreshing = false
-    this.hasLoaded = true
-    this.error = cleanError(err)
-    this.loadMoreError = cleanError(loadMoreErr)
-    if (err) {
-      logger.error('Failed to fetch user lists', {error: err})
-    }
-    if (loadMoreErr) {
-      logger.error('Failed to fetch user lists', {
-        error: loadMoreErr,
-      })
-    }
-  }
-
-  // helper functions
-  // =
-
-  _replaceAll({
-    lists,
-    cursor,
-  }: {
-    lists: GraphDefs.ListView[]
-    cursor: string | undefined
-  }) {
-    this.lists = []
-    this._appendAll({lists, cursor})
-  }
-
-  _appendAll({
-    lists,
-    cursor,
-  }: {
-    lists: GraphDefs.ListView[]
-    cursor: string | undefined
-  }) {
-    this.loadMoreCursor = cursor
-    this.hasMore = !!this.loadMoreCursor
-    this.lists = this.lists.concat(
-      lists.map(list => ({...list, _reactKey: list.uri})),
-    )
-  }
-}
diff --git a/src/state/models/lists/muted-accounts.ts b/src/state/models/lists/muted-accounts.ts
deleted file mode 100644
index 19ade0d9c..000000000
--- a/src/state/models/lists/muted-accounts.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-import {makeAutoObservable} from 'mobx'
-import {
-  AppBskyGraphGetMutes as GetMutes,
-  AppBskyActorDefs as ActorDefs,
-} from '@atproto/api'
-import {RootStoreModel} from '../root-store'
-import {cleanError} from 'lib/strings/errors'
-import {bundleAsync} from 'lib/async/bundle'
-import {logger} from '#/logger'
-
-const PAGE_SIZE = 30
-
-export class MutedAccountsModel {
-  // state
-  isLoading = false
-  isRefreshing = false
-  hasLoaded = false
-  error = ''
-  hasMore = true
-  loadMoreCursor?: string
-
-  // data
-  mutes: ActorDefs.ProfileView[] = []
-
-  constructor(public rootStore: RootStoreModel) {
-    makeAutoObservable(
-      this,
-      {
-        rootStore: false,
-      },
-      {autoBind: true},
-    )
-  }
-
-  get hasContent() {
-    return this.mutes.length > 0
-  }
-
-  get hasError() {
-    return this.error !== ''
-  }
-
-  get isEmpty() {
-    return this.hasLoaded && !this.hasContent
-  }
-
-  // public api
-  // =
-
-  async refresh() {
-    return this.loadMore(true)
-  }
-
-  loadMore = bundleAsync(async (replace: boolean = false) => {
-    if (!replace && !this.hasMore) {
-      return
-    }
-    this._xLoading(replace)
-    try {
-      const res = await this.rootStore.agent.app.bsky.graph.getMutes({
-        limit: PAGE_SIZE,
-        cursor: replace ? undefined : this.loadMoreCursor,
-      })
-      if (replace) {
-        this._replaceAll(res)
-      } else {
-        this._appendAll(res)
-      }
-      this._xIdle()
-    } catch (e: any) {
-      this._xIdle(e)
-    }
-  })
-
-  // state transitions
-  // =
-
-  _xLoading(isRefreshing = false) {
-    this.isLoading = true
-    this.isRefreshing = isRefreshing
-    this.error = ''
-  }
-
-  _xIdle(err?: any) {
-    this.isLoading = false
-    this.isRefreshing = false
-    this.hasLoaded = true
-    this.error = cleanError(err)
-    if (err) {
-      logger.error('Failed to fetch user followers', {error: err})
-    }
-  }
-
-  // helper functions
-  // =
-
-  _replaceAll(res: GetMutes.Response) {
-    this.mutes = []
-    this._appendAll(res)
-  }
-
-  _appendAll(res: GetMutes.Response) {
-    this.loadMoreCursor = res.data.cursor
-    this.hasMore = !!this.loadMoreCursor
-    this.mutes = this.mutes.concat(res.data.mutes)
-  }
-}
diff --git a/src/state/models/lists/reposted-by.ts b/src/state/models/lists/reposted-by.ts
deleted file mode 100644
index c5058558a..000000000
--- a/src/state/models/lists/reposted-by.ts
+++ /dev/null
@@ -1,136 +0,0 @@
-import {makeAutoObservable, runInAction} from 'mobx'
-import {AtUri} from '@atproto/api'
-import {
-  AppBskyFeedGetRepostedBy as GetRepostedBy,
-  AppBskyActorDefs,
-} from '@atproto/api'
-import {RootStoreModel} from '../root-store'
-import {bundleAsync} from 'lib/async/bundle'
-import {cleanError} from 'lib/strings/errors'
-import * as apilib from 'lib/api/index'
-import {logger} from '#/logger'
-
-const PAGE_SIZE = 30
-
-export type RepostedByItem = AppBskyActorDefs.ProfileViewBasic
-
-export class RepostedByModel {
-  // state
-  isLoading = false
-  isRefreshing = false
-  hasLoaded = false
-  error = ''
-  resolvedUri = ''
-  params: GetRepostedBy.QueryParams
-  hasMore = true
-  loadMoreCursor?: string
-
-  // data
-  uri: string = ''
-  repostedBy: RepostedByItem[] = []
-
-  constructor(
-    public rootStore: RootStoreModel,
-    params: GetRepostedBy.QueryParams,
-  ) {
-    makeAutoObservable(
-      this,
-      {
-        rootStore: false,
-        params: false,
-      },
-      {autoBind: true},
-    )
-    this.params = params
-  }
-
-  get hasContent() {
-    return this.uri !== ''
-  }
-
-  get hasError() {
-    return this.error !== ''
-  }
-
-  get isEmpty() {
-    return this.hasLoaded && !this.hasContent
-  }
-
-  // public api
-  // =
-
-  async refresh() {
-    return this.loadMore(true)
-  }
-
-  loadMore = bundleAsync(async (replace: boolean = false) => {
-    this._xLoading(replace)
-    try {
-      if (!this.resolvedUri) {
-        await this._resolveUri()
-      }
-      const params = Object.assign({}, this.params, {
-        uri: this.resolvedUri,
-        limit: PAGE_SIZE,
-        cursor: replace ? undefined : this.loadMoreCursor,
-      })
-      const res = await this.rootStore.agent.getRepostedBy(params)
-      if (replace) {
-        this._replaceAll(res)
-      } else {
-        this._appendAll(res)
-      }
-      this._xIdle()
-    } catch (e: any) {
-      this._xIdle(e)
-    }
-  })
-
-  // state transitions
-  // =
-
-  _xLoading(isRefreshing = false) {
-    this.isLoading = true
-    this.isRefreshing = isRefreshing
-    this.error = ''
-  }
-
-  _xIdle(err?: any) {
-    this.isLoading = false
-    this.isRefreshing = false
-    this.hasLoaded = true
-    this.error = cleanError(err)
-    if (err) {
-      logger.error('Failed to fetch reposted by view', {error: err})
-    }
-  }
-
-  // helper functions
-  // =
-
-  async _resolveUri() {
-    const urip = new AtUri(this.params.uri)
-    if (!urip.host.startsWith('did:')) {
-      try {
-        urip.host = await apilib.resolveName(this.rootStore, urip.host)
-      } catch (e: any) {
-        this.error = e.toString()
-      }
-    }
-    runInAction(() => {
-      this.resolvedUri = urip.toString()
-    })
-  }
-
-  _replaceAll(res: GetRepostedBy.Response) {
-    this.repostedBy = []
-    this._appendAll(res)
-  }
-
-  _appendAll(res: GetRepostedBy.Response) {
-    this.loadMoreCursor = res.data.cursor
-    this.hasMore = !!this.loadMoreCursor
-    this.repostedBy = this.repostedBy.concat(res.data.repostedBy)
-    this.rootStore.me.follows.hydrateMany(res.data.repostedBy)
-  }
-}
diff --git a/src/state/models/lists/user-followers.ts b/src/state/models/lists/user-followers.ts
deleted file mode 100644
index 159308b9b..000000000
--- a/src/state/models/lists/user-followers.ts
+++ /dev/null
@@ -1,121 +0,0 @@
-import {makeAutoObservable} from 'mobx'
-import {
-  AppBskyGraphGetFollowers as GetFollowers,
-  AppBskyActorDefs as ActorDefs,
-} from '@atproto/api'
-import {RootStoreModel} from '../root-store'
-import {cleanError} from 'lib/strings/errors'
-import {bundleAsync} from 'lib/async/bundle'
-import {logger} from '#/logger'
-
-const PAGE_SIZE = 30
-
-export type FollowerItem = ActorDefs.ProfileViewBasic
-
-export class UserFollowersModel {
-  // state
-  isLoading = false
-  isRefreshing = false
-  hasLoaded = false
-  error = ''
-  params: GetFollowers.QueryParams
-  hasMore = true
-  loadMoreCursor?: string
-
-  // data
-  subject: ActorDefs.ProfileViewBasic = {
-    did: '',
-    handle: '',
-  }
-  followers: FollowerItem[] = []
-
-  constructor(
-    public rootStore: RootStoreModel,
-    params: GetFollowers.QueryParams,
-  ) {
-    makeAutoObservable(
-      this,
-      {
-        rootStore: false,
-        params: false,
-      },
-      {autoBind: true},
-    )
-    this.params = params
-  }
-
-  get hasContent() {
-    return this.subject.did !== ''
-  }
-
-  get hasError() {
-    return this.error !== ''
-  }
-
-  get isEmpty() {
-    return this.hasLoaded && !this.hasContent
-  }
-
-  // public api
-  // =
-
-  async refresh() {
-    return this.loadMore(true)
-  }
-
-  loadMore = bundleAsync(async (replace: boolean = false) => {
-    if (!replace && !this.hasMore) {
-      return
-    }
-    this._xLoading(replace)
-    try {
-      const params = Object.assign({}, this.params, {
-        limit: PAGE_SIZE,
-        cursor: replace ? undefined : this.loadMoreCursor,
-      })
-      const res = await this.rootStore.agent.getFollowers(params)
-      if (replace) {
-        this._replaceAll(res)
-      } else {
-        this._appendAll(res)
-      }
-      this._xIdle()
-    } catch (e: any) {
-      this._xIdle(e)
-    }
-  })
-
-  // state transitions
-  // =
-
-  _xLoading(isRefreshing = false) {
-    this.isLoading = true
-    this.isRefreshing = isRefreshing
-    this.error = ''
-  }
-
-  _xIdle(err?: any) {
-    this.isLoading = false
-    this.isRefreshing = false
-    this.hasLoaded = true
-    this.error = cleanError(err)
-    if (err) {
-      logger.error('Failed to fetch user followers', {error: err})
-    }
-  }
-
-  // helper functions
-  // =
-
-  _replaceAll(res: GetFollowers.Response) {
-    this.followers = []
-    this._appendAll(res)
-  }
-
-  _appendAll(res: GetFollowers.Response) {
-    this.loadMoreCursor = res.data.cursor
-    this.hasMore = !!this.loadMoreCursor
-    this.followers = this.followers.concat(res.data.followers)
-    this.rootStore.me.follows.hydrateMany(res.data.followers)
-  }
-}
diff --git a/src/state/models/lists/user-follows.ts b/src/state/models/lists/user-follows.ts
deleted file mode 100644
index 3abbbaf95..000000000
--- a/src/state/models/lists/user-follows.ts
+++ /dev/null
@@ -1,121 +0,0 @@
-import {makeAutoObservable} from 'mobx'
-import {
-  AppBskyGraphGetFollows as GetFollows,
-  AppBskyActorDefs as ActorDefs,
-} from '@atproto/api'
-import {RootStoreModel} from '../root-store'
-import {cleanError} from 'lib/strings/errors'
-import {bundleAsync} from 'lib/async/bundle'
-import {logger} from '#/logger'
-
-const PAGE_SIZE = 30
-
-export type FollowItem = ActorDefs.ProfileViewBasic
-
-export class UserFollowsModel {
-  // state
-  isLoading = false
-  isRefreshing = false
-  hasLoaded = false
-  error = ''
-  params: GetFollows.QueryParams
-  hasMore = true
-  loadMoreCursor?: string
-
-  // data
-  subject: ActorDefs.ProfileViewBasic = {
-    did: '',
-    handle: '',
-  }
-  follows: FollowItem[] = []
-
-  constructor(
-    public rootStore: RootStoreModel,
-    params: GetFollows.QueryParams,
-  ) {
-    makeAutoObservable(
-      this,
-      {
-        rootStore: false,
-        params: false,
-      },
-      {autoBind: true},
-    )
-    this.params = params
-  }
-
-  get hasContent() {
-    return this.subject.did !== ''
-  }
-
-  get hasError() {
-    return this.error !== ''
-  }
-
-  get isEmpty() {
-    return this.hasLoaded && !this.hasContent
-  }
-
-  // public api
-  // =
-
-  async refresh() {
-    return this.loadMore(true)
-  }
-
-  loadMore = bundleAsync(async (replace: boolean = false) => {
-    if (!replace && !this.hasMore) {
-      return
-    }
-    this._xLoading(replace)
-    try {
-      const params = Object.assign({}, this.params, {
-        limit: PAGE_SIZE,
-        cursor: replace ? undefined : this.loadMoreCursor,
-      })
-      const res = await this.rootStore.agent.getFollows(params)
-      if (replace) {
-        this._replaceAll(res)
-      } else {
-        this._appendAll(res)
-      }
-      this._xIdle()
-    } catch (e: any) {
-      this._xIdle(e)
-    }
-  })
-
-  // state transitions
-  // =
-
-  _xLoading(isRefreshing = false) {
-    this.isLoading = true
-    this.isRefreshing = isRefreshing
-    this.error = ''
-  }
-
-  _xIdle(err?: any) {
-    this.isLoading = false
-    this.isRefreshing = false
-    this.hasLoaded = true
-    this.error = cleanError(err)
-    if (err) {
-      logger.error('Failed to fetch user follows', err)
-    }
-  }
-
-  // helper functions
-  // =
-
-  _replaceAll(res: GetFollows.Response) {
-    this.follows = []
-    this._appendAll(res)
-  }
-
-  _appendAll(res: GetFollows.Response) {
-    this.loadMoreCursor = res.data.cursor
-    this.hasMore = !!this.loadMoreCursor
-    this.follows = this.follows.concat(res.data.follows)
-    this.rootStore.me.follows.hydrateMany(res.data.follows)
-  }
-}