about summary refs log tree commit diff
path: root/src/state
diff options
context:
space:
mode:
Diffstat (limited to 'src/state')
-rw-r--r--src/state/index.ts5
-rw-r--r--src/state/models/content/feed-source.ts11
-rw-r--r--src/state/models/content/list.ts7
-rw-r--r--src/state/models/content/post-thread.ts3
-rw-r--r--src/state/models/content/profile.ts3
-rw-r--r--src/state/models/discovery/feeds.ts3
-rw-r--r--src/state/models/discovery/suggested-actors.ts3
-rw-r--r--src/state/models/feeds/notifications.ts24
-rw-r--r--src/state/models/feeds/post.ts13
-rw-r--r--src/state/models/feeds/posts.ts9
-rw-r--r--src/state/models/invited-users.ts3
-rw-r--r--src/state/models/lists/actor-feeds.ts3
-rw-r--r--src/state/models/lists/blocked-accounts.ts3
-rw-r--r--src/state/models/lists/likes.ts3
-rw-r--r--src/state/models/lists/lists-list.ts5
-rw-r--r--src/state/models/lists/muted-accounts.ts3
-rw-r--r--src/state/models/lists/reposted-by.ts3
-rw-r--r--src/state/models/lists/user-followers.ts3
-rw-r--r--src/state/models/lists/user-follows.ts3
-rw-r--r--src/state/models/log.ts115
-rw-r--r--src/state/models/me.ts19
-rw-r--r--src/state/models/media/image.ts3
-rw-r--r--src/state/models/root-store.ts15
-rw-r--r--src/state/models/session.ts3
-rw-r--r--src/state/models/ui/create-account.ts5
-rw-r--r--src/state/models/ui/preferences.ts3
-rw-r--r--src/state/models/ui/profile.ts13
-rw-r--r--src/state/models/ui/saved-feeds.ts3
28 files changed, 95 insertions, 194 deletions
diff --git a/src/state/index.ts b/src/state/index.ts
index 2c81c0ddf..55dcae6d6 100644
--- a/src/state/index.ts
+++ b/src/state/index.ts
@@ -4,6 +4,7 @@ import {BskyAgent} from '@atproto/api'
 import {RootStoreModel} from './models/root-store'
 import * as apiPolyfill from 'lib/api/api-polyfill'
 import * as storage from 'lib/storage'
+import {logger} from '#/logger'
 
 export const LOCAL_DEV_SERVICE =
   Platform.OS === 'android' ? 'http://10.0.2.2:2583' : 'http://localhost:2583'
@@ -22,10 +23,10 @@ export async function setupState(serviceUri = DEFAULT_SERVICE) {
   rootStore = new RootStoreModel(new BskyAgent({service: serviceUri}))
   try {
     data = (await storage.load(ROOT_STATE_STORAGE_KEY)) || {}
-    rootStore.log.debug('Initial hydrate', {hasSession: !!data.session})
+    logger.debug('Initial hydrate', {hasSession: !!data.session})
     rootStore.hydrate(data)
   } catch (e: any) {
-    rootStore.log.error('Failed to load state from storage', {error: e})
+    logger.error('Failed to load state from storage', {error: e})
   }
   rootStore.attemptSessionResumption()
 
diff --git a/src/state/models/content/feed-source.ts b/src/state/models/content/feed-source.ts
index d1b8fc9dc..79747d6fb 100644
--- a/src/state/models/content/feed-source.ts
+++ b/src/state/models/content/feed-source.ts
@@ -6,6 +6,7 @@ import {sanitizeHandle} from 'lib/strings/handles'
 import {bundleAsync} from 'lib/async/bundle'
 import {cleanError} from 'lib/strings/errors'
 import {track} from 'lib/analytics/analytics'
+import {logger} from '#/logger'
 
 export class FeedSourceModel {
   // state
@@ -134,7 +135,7 @@ export class FeedSourceModel {
     try {
       await this.rootStore.preferences.addSavedFeed(this.uri)
     } catch (error) {
-      this.rootStore.log.error('Failed to save feed', {error})
+      logger.error('Failed to save feed', {error})
     } finally {
       track('CustomFeed:Save')
     }
@@ -147,7 +148,7 @@ export class FeedSourceModel {
     try {
       await this.rootStore.preferences.removeSavedFeed(this.uri)
     } catch (error) {
-      this.rootStore.log.error('Failed to unsave feed', {error})
+      logger.error('Failed to unsave feed', {error})
     } finally {
       track('CustomFeed:Unsave')
     }
@@ -157,7 +158,7 @@ export class FeedSourceModel {
     try {
       await this.rootStore.preferences.addPinnedFeed(this.uri)
     } catch (error) {
-      this.rootStore.log.error('Failed to pin feed', {error})
+      logger.error('Failed to pin feed', {error})
     } finally {
       track('CustomFeed:Pin', {
         name: this.displayName,
@@ -194,7 +195,7 @@ export class FeedSourceModel {
     } catch (e: any) {
       this.likeUri = undefined
       this.likeCount = (this.likeCount || 1) - 1
-      this.rootStore.log.error('Failed to like feed', {error: e})
+      logger.error('Failed to like feed', {error: e})
     } finally {
       track('CustomFeed:Like')
     }
@@ -215,7 +216,7 @@ export class FeedSourceModel {
     } catch (e: any) {
       this.likeUri = uri
       this.likeCount = (this.likeCount || 0) + 1
-      this.rootStore.log.error('Failed to unlike feed', {error: e})
+      logger.error('Failed to unlike feed', {error: e})
     } finally {
       track('CustomFeed:Unlike')
     }
diff --git a/src/state/models/content/list.ts b/src/state/models/content/list.ts
index 985d8d82d..115426e5c 100644
--- a/src/state/models/content/list.ts
+++ b/src/state/models/content/list.ts
@@ -16,6 +16,7 @@ import {cleanError} from 'lib/strings/errors'
 import {bundleAsync} from 'lib/async/bundle'
 import {track} from 'lib/analytics/analytics'
 import {until} from 'lib/async/until'
+import {logger} from '#/logger'
 
 const PAGE_SIZE = 30
 
@@ -339,7 +340,7 @@ export class ListModel {
     try {
       await this.rootStore.preferences.addPinnedFeed(this.uri)
     } catch (error) {
-      this.rootStore.log.error('Failed to pin feed', {error})
+      logger.error('Failed to pin feed', {error})
     } finally {
       track('CustomFeed:Pin', {
         name: this.data?.name || '',
@@ -455,10 +456,10 @@ export class ListModel {
     this.error = cleanError(err)
     this.loadMoreError = cleanError(loadMoreErr)
     if (err) {
-      this.rootStore.log.error('Failed to fetch user items', {error: err})
+      logger.error('Failed to fetch user items', {error: err})
     }
     if (loadMoreErr) {
-      this.rootStore.log.error('Failed to fetch user items', {
+      logger.error('Failed to fetch user items', {
         error: loadMoreErr,
       })
     }
diff --git a/src/state/models/content/post-thread.ts b/src/state/models/content/post-thread.ts
index cf6377da7..fd194056a 100644
--- a/src/state/models/content/post-thread.ts
+++ b/src/state/models/content/post-thread.ts
@@ -11,6 +11,7 @@ import * as apilib from 'lib/api/index'
 import {cleanError} from 'lib/strings/errors'
 import {ThreadViewPreference} from '../ui/preferences'
 import {PostThreadItemModel} from './post-thread-item'
+import {logger} from '#/logger'
 
 export class PostThreadModel {
   // state
@@ -163,7 +164,7 @@ export class PostThreadModel {
     this.hasLoaded = true
     this.error = cleanError(err)
     if (err) {
-      this.rootStore.log.error('Failed to fetch post thread', {error: err})
+      logger.error('Failed to fetch post thread', {error: err})
     }
     this.notFound = err instanceof GetPostThread.NotFoundError
   }
diff --git a/src/state/models/content/profile.ts b/src/state/models/content/profile.ts
index 0050970e6..14362ceec 100644
--- a/src/state/models/content/profile.ts
+++ b/src/state/models/content/profile.ts
@@ -15,6 +15,7 @@ import {cleanError} from 'lib/strings/errors'
 import {FollowState} from '../cache/my-follows'
 import {Image as RNImage} from 'react-native-image-crop-picker'
 import {track} from 'lib/analytics/analytics'
+import {logger} from '#/logger'
 
 export class ProfileViewerModel {
   muted?: boolean
@@ -235,7 +236,7 @@ export class ProfileModel {
     this.hasLoaded = true
     this.error = cleanError(err)
     if (err) {
-      this.rootStore.log.error('Failed to fetch profile', {error: err})
+      logger.error('Failed to fetch profile', {error: err})
     }
   }
 
diff --git a/src/state/models/discovery/feeds.ts b/src/state/models/discovery/feeds.ts
index 3902f3ac1..a7c94e40d 100644
--- a/src/state/models/discovery/feeds.ts
+++ b/src/state/models/discovery/feeds.ts
@@ -4,6 +4,7 @@ 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 DEFAULT_LIMIT = 50
 
@@ -120,7 +121,7 @@ export class FeedsDiscoveryModel {
     this.hasLoaded = true
     this.error = cleanError(err)
     if (err) {
-      this.rootStore.log.error('Failed to fetch popular feeds', {error: err})
+      logger.error('Failed to fetch popular feeds', {error: err})
     }
   }
 
diff --git a/src/state/models/discovery/suggested-actors.ts b/src/state/models/discovery/suggested-actors.ts
index 8776fcd85..450786c2f 100644
--- a/src/state/models/discovery/suggested-actors.ts
+++ b/src/state/models/discovery/suggested-actors.ts
@@ -3,6 +3,7 @@ import {AppBskyActorDefs, moderateProfile} 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
 
@@ -144,7 +145,7 @@ export class SuggestedActorsModel {
     this.hasLoaded = true
     this.error = cleanError(err)
     if (err) {
-      this.rootStore.log.error('Failed to fetch suggested actors', {error: err})
+      logger.error('Failed to fetch suggested actors', {error: err})
     }
   }
 }
diff --git a/src/state/models/feeds/notifications.ts b/src/state/models/feeds/notifications.ts
index a834b543a..607e3038b 100644
--- a/src/state/models/feeds/notifications.ts
+++ b/src/state/models/feeds/notifications.ts
@@ -17,6 +17,7 @@ import {bundleAsync} from 'lib/async/bundle'
 import {RootStoreModel} from '../root-store'
 import {PostThreadModel} from '../content/post-thread'
 import {cleanError} from 'lib/strings/errors'
+import {logger} from '#/logger'
 
 const GROUPABLE_REASONS = ['like', 'repost', 'follow']
 const PAGE_SIZE = 30
@@ -210,7 +211,7 @@ export class NotificationsFeedItemModel {
         if (valid.success) {
           return v
         } else {
-          this.rootStore.log.warn('Received an invalid record', {
+          logger.warn('Received an invalid record', {
             record: v,
             error: valid.error,
           })
@@ -218,7 +219,7 @@ export class NotificationsFeedItemModel {
         }
       }
     }
-    this.rootStore.log.warn(
+    logger.warn(
       'app.bsky.notifications.list served an unsupported record type',
       {record: v},
     )
@@ -319,7 +320,7 @@ export class NotificationsFeedModel {
    * Nuke all data
    */
   clear() {
-    this.rootStore.log.debug('NotificationsModel:clear')
+    logger.debug('NotificationsModel:clear')
     this.isLoading = false
     this.isRefreshing = false
     this.hasLoaded = false
@@ -336,7 +337,7 @@ export class NotificationsFeedModel {
    * Load for first render
    */
   setup = bundleAsync(async (isRefreshing: boolean = false) => {
-    this.rootStore.log.debug('NotificationsModel:refresh', {isRefreshing})
+    logger.debug('NotificationsModel:refresh', {isRefreshing})
     await this.lock.acquireAsync()
     try {
       this._xLoading(isRefreshing)
@@ -368,7 +369,7 @@ export class NotificationsFeedModel {
    * Sync the next set of notifications to show
    */
   syncQueue = bundleAsync(async () => {
-    this.rootStore.log.debug('NotificationsModel:syncQueue')
+    logger.debug('NotificationsModel:syncQueue')
     if (this.unreadCount >= MAX_VISIBLE_NOTIFS) {
       return // no need to check
     }
@@ -401,7 +402,7 @@ export class NotificationsFeedModel {
       this._setQueued(this._filterNotifications(queueModels))
       this._countUnread()
     } catch (e) {
-      this.rootStore.log.error('NotificationsModel:syncQueue failed', {
+      logger.error('NotificationsModel:syncQueue failed', {
         error: e,
       })
     } finally {
@@ -463,10 +464,7 @@ export class NotificationsFeedModel {
       }
     }
     await Promise.all(promises).catch(e => {
-      this.rootStore.log.error(
-        'Uncaught failure during notifications update()',
-        e,
-      )
+      logger.error('Uncaught failure during notifications update()', e)
     })
   }
 
@@ -483,7 +481,7 @@ export class NotificationsFeedModel {
         this.lastSync ? this.lastSync.toISOString() : undefined,
       )
     } catch (e: any) {
-      this.rootStore.log.warn('Failed to update notifications read state', {
+      logger.warn('Failed to update notifications read state', {
         error: e,
       })
     }
@@ -505,10 +503,10 @@ export class NotificationsFeedModel {
     this.error = cleanError(error)
     this.loadMoreError = cleanError(loadMoreError)
     if (error) {
-      this.rootStore.log.error('Failed to fetch notifications', {error})
+      logger.error('Failed to fetch notifications', {error})
     }
     if (loadMoreError) {
-      this.rootStore.log.error('Failed to load more notifications', {
+      logger.error('Failed to load more notifications', {
         error: loadMoreError,
       })
     }
diff --git a/src/state/models/feeds/post.ts b/src/state/models/feeds/post.ts
index be3417104..d064edc21 100644
--- a/src/state/models/feeds/post.ts
+++ b/src/state/models/feeds/post.ts
@@ -10,6 +10,7 @@ import {RootStoreModel} from '../root-store'
 import {updateDataOptimistically} from 'lib/async/revertible'
 import {track} from 'lib/analytics/analytics'
 import {hackAddDeletedEmbed} from 'lib/api/hack-add-deleted-embed'
+import {logger} from '#/logger'
 
 type FeedViewPost = AppBskyFeedDefs.FeedViewPost
 type ReasonRepost = AppBskyFeedDefs.ReasonRepost
@@ -42,14 +43,14 @@ export class PostsFeedItemModel {
       } else {
         this.postRecord = undefined
         this.richText = undefined
-        rootStore.log.warn('Received an invalid app.bsky.feed.post record', {
+        logger.warn('Received an invalid app.bsky.feed.post record', {
           error: valid.error,
         })
       }
     } else {
       this.postRecord = undefined
       this.richText = undefined
-      rootStore.log.warn(
+      logger.warn(
         'app.bsky.feed.getTimeline or app.bsky.feed.getAuthorFeed served an unexpected record type',
         {record: this.post.record},
       )
@@ -132,7 +133,7 @@ export class PostsFeedItemModel {
         track('Post:Like')
       }
     } catch (error) {
-      this.rootStore.log.error('Failed to toggle like', {error})
+      logger.error('Failed to toggle like', {error})
     }
   }
 
@@ -167,7 +168,7 @@ export class PostsFeedItemModel {
         track('Post:Repost')
       }
     } catch (error) {
-      this.rootStore.log.error('Failed to toggle repost', {error})
+      logger.error('Failed to toggle repost', {error})
     }
   }
 
@@ -181,7 +182,7 @@ export class PostsFeedItemModel {
         track('Post:ThreadMute')
       }
     } catch (error) {
-      this.rootStore.log.error('Failed to toggle thread mute', {error})
+      logger.error('Failed to toggle thread mute', {error})
     }
   }
 
@@ -190,7 +191,7 @@ export class PostsFeedItemModel {
       await this.rootStore.agent.deletePost(this.post.uri)
       this.rootStore.emitPostDeleted(this.post.uri)
     } catch (error) {
-      this.rootStore.log.error('Failed to delete post', {error})
+      logger.error('Failed to delete post', {error})
     } finally {
       track('Post:Delete')
     }
diff --git a/src/state/models/feeds/posts.ts b/src/state/models/feeds/posts.ts
index 5c10ae4c7..0a06c581c 100644
--- a/src/state/models/feeds/posts.ts
+++ b/src/state/models/feeds/posts.ts
@@ -22,6 +22,7 @@ import {LikesFeedAPI} from 'lib/api/feed/likes'
 import {CustomFeedAPI} from 'lib/api/feed/custom'
 import {ListFeedAPI} from 'lib/api/feed/list'
 import {MergeFeedAPI} from 'lib/api/feed/merge'
+import {logger} from '#/logger'
 
 const PAGE_SIZE = 30
 
@@ -161,7 +162,7 @@ export class PostsFeedModel {
    * Nuke all data
    */
   clear() {
-    this.rootStore.log.debug('FeedModel:clear')
+    logger.debug('FeedModel:clear')
     this.isLoading = false
     this.isRefreshing = false
     this.hasNewLatest = false
@@ -177,7 +178,7 @@ export class PostsFeedModel {
    * Load for first render
    */
   setup = bundleAsync(async (isRefreshing: boolean = false) => {
-    this.rootStore.log.debug('FeedModel:setup', {isRefreshing})
+    logger.debug('FeedModel:setup', {isRefreshing})
     if (isRefreshing) {
       this.isRefreshing = true // set optimistically for UI
     }
@@ -324,10 +325,10 @@ export class PostsFeedModel {
     this.knownError = detectKnownError(this.feedType, error)
     this.loadMoreError = cleanError(loadMoreError)
     if (error) {
-      this.rootStore.log.error('Posts feed request failed', {error})
+      logger.error('Posts feed request failed', {error})
     }
     if (loadMoreError) {
-      this.rootStore.log.error('Posts feed load-more request failed', {
+      logger.error('Posts feed load-more request failed', {
         error: loadMoreError,
       })
     }
diff --git a/src/state/models/invited-users.ts b/src/state/models/invited-users.ts
index 995c4bfb5..9ba65e19e 100644
--- a/src/state/models/invited-users.ts
+++ b/src/state/models/invited-users.ts
@@ -2,6 +2,7 @@ import {makeAutoObservable, runInAction} from 'mobx'
 import {ComAtprotoServerDefs, AppBskyActorDefs} from '@atproto/api'
 import {RootStoreModel} from './root-store'
 import {isObj, hasProp, isStrArray} from 'lib/type-guards'
+import {logger} from '#/logger'
 
 export class InvitedUsers {
   copiedInvites: string[] = []
@@ -63,7 +64,7 @@ export class InvitedUsers {
         })
         this.rootStore.me.follows.hydrateMany(this.profiles)
       } catch (e) {
-        this.rootStore.log.error('Failed to fetch profiles for invited users', {
+        logger.error('Failed to fetch profiles for invited users', {
           error: e,
         })
       }
diff --git a/src/state/models/lists/actor-feeds.ts b/src/state/models/lists/actor-feeds.ts
index 65da765f1..29c01e536 100644
--- a/src/state/models/lists/actor-feeds.ts
+++ b/src/state/models/lists/actor-feeds.ts
@@ -4,6 +4,7 @@ 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
 
@@ -98,7 +99,7 @@ export class ActorFeedsModel {
     this.hasLoaded = true
     this.error = cleanError(err)
     if (err) {
-      this.rootStore.log.error('Failed to fetch user followers', {error: err})
+      logger.error('Failed to fetch user followers', {error: err})
     }
   }
 
diff --git a/src/state/models/lists/blocked-accounts.ts b/src/state/models/lists/blocked-accounts.ts
index b4495b543..5c3dbe7ce 100644
--- a/src/state/models/lists/blocked-accounts.ts
+++ b/src/state/models/lists/blocked-accounts.ts
@@ -6,6 +6,7 @@ import {
 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
 
@@ -86,7 +87,7 @@ export class BlockedAccountsModel {
     this.hasLoaded = true
     this.error = cleanError(err)
     if (err) {
-      this.rootStore.log.error('Failed to fetch user followers', {error: err})
+      logger.error('Failed to fetch user followers', {error: err})
     }
   }
 
diff --git a/src/state/models/lists/likes.ts b/src/state/models/lists/likes.ts
index 61e480e19..df20f09db 100644
--- a/src/state/models/lists/likes.ts
+++ b/src/state/models/lists/likes.ts
@@ -5,6 +5,7 @@ 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
 
@@ -97,7 +98,7 @@ export class LikesModel {
     this.hasLoaded = true
     this.error = cleanError(err)
     if (err) {
-      this.rootStore.log.error('Failed to fetch likes', {error: err})
+      logger.error('Failed to fetch likes', {error: err})
     }
   }
 
diff --git a/src/state/models/lists/lists-list.ts b/src/state/models/lists/lists-list.ts
index 7415d06d7..eb6291637 100644
--- a/src/state/models/lists/lists-list.ts
+++ b/src/state/models/lists/lists-list.ts
@@ -4,6 +4,7 @@ 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
 
@@ -204,10 +205,10 @@ export class ListsListModel {
     this.error = cleanError(err)
     this.loadMoreError = cleanError(loadMoreErr)
     if (err) {
-      this.rootStore.log.error('Failed to fetch user lists', {error: err})
+      logger.error('Failed to fetch user lists', {error: err})
     }
     if (loadMoreErr) {
-      this.rootStore.log.error('Failed to fetch user lists', {
+      logger.error('Failed to fetch user lists', {
         error: loadMoreErr,
       })
     }
diff --git a/src/state/models/lists/muted-accounts.ts b/src/state/models/lists/muted-accounts.ts
index bc9e53e5c..19ade0d9c 100644
--- a/src/state/models/lists/muted-accounts.ts
+++ b/src/state/models/lists/muted-accounts.ts
@@ -6,6 +6,7 @@ import {
 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
 
@@ -86,7 +87,7 @@ export class MutedAccountsModel {
     this.hasLoaded = true
     this.error = cleanError(err)
     if (err) {
-      this.rootStore.log.error('Failed to fetch user followers', {error: err})
+      logger.error('Failed to fetch user followers', {error: err})
     }
   }
 
diff --git a/src/state/models/lists/reposted-by.ts b/src/state/models/lists/reposted-by.ts
index fe639fd0e..c5058558a 100644
--- a/src/state/models/lists/reposted-by.ts
+++ b/src/state/models/lists/reposted-by.ts
@@ -8,6 +8,7 @@ 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
 
@@ -100,7 +101,7 @@ export class RepostedByModel {
     this.hasLoaded = true
     this.error = cleanError(err)
     if (err) {
-      this.rootStore.log.error('Failed to fetch reposted by view', {error: err})
+      logger.error('Failed to fetch reposted by view', {error: err})
     }
   }
 
diff --git a/src/state/models/lists/user-followers.ts b/src/state/models/lists/user-followers.ts
index d76ecce1a..159308b9b 100644
--- a/src/state/models/lists/user-followers.ts
+++ b/src/state/models/lists/user-followers.ts
@@ -6,6 +6,7 @@ import {
 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
 
@@ -99,7 +100,7 @@ export class UserFollowersModel {
     this.hasLoaded = true
     this.error = cleanError(err)
     if (err) {
-      this.rootStore.log.error('Failed to fetch user followers', {error: err})
+      logger.error('Failed to fetch user followers', {error: err})
     }
   }
 
diff --git a/src/state/models/lists/user-follows.ts b/src/state/models/lists/user-follows.ts
index c9630eba8..3abbbaf95 100644
--- a/src/state/models/lists/user-follows.ts
+++ b/src/state/models/lists/user-follows.ts
@@ -6,6 +6,7 @@ import {
 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
 
@@ -99,7 +100,7 @@ export class UserFollowsModel {
     this.hasLoaded = true
     this.error = cleanError(err)
     if (err) {
-      this.rootStore.log.error('Failed to fetch user follows', err)
+      logger.error('Failed to fetch user follows', err)
     }
   }
 
diff --git a/src/state/models/log.ts b/src/state/models/log.ts
deleted file mode 100644
index 7c9c37c0d..000000000
--- a/src/state/models/log.ts
+++ /dev/null
@@ -1,115 +0,0 @@
-import {makeAutoObservable} from 'mobx'
-// import {XRPCError, XRPCInvalidResponseError} from '@atproto/xrpc' TODO
-
-const MAX_ENTRIES = 300
-
-interface LogEntry {
-  id: string
-  type?: string
-  summary?: string
-  details?: string
-  ts?: number
-}
-
-let _lastTs: string
-let _lastId: string
-function genId(): string {
-  let candidate = String(Date.now())
-  if (_lastTs === candidate) {
-    const id = _lastId + 'x'
-    _lastId = id
-    return id
-  }
-  _lastTs = candidate
-  _lastId = candidate
-  return candidate
-}
-
-export class LogModel {
-  entries: LogEntry[] = []
-  timers = new Map<string, number>()
-
-  constructor() {
-    makeAutoObservable(this)
-  }
-
-  add(entry: LogEntry) {
-    this.entries.push(entry)
-    while (this.entries.length > MAX_ENTRIES) {
-      this.entries = this.entries.slice(50)
-    }
-  }
-
-  debug(summary: string, details?: any) {
-    details = detailsToStr(details)
-    console.debug(summary, details || '')
-    this.add({
-      id: genId(),
-      type: 'debug',
-      summary,
-      details,
-      ts: Date.now(),
-    })
-  }
-
-  warn(summary: string, details?: any) {
-    details = detailsToStr(details)
-    console.debug(summary, details || '')
-    this.add({
-      id: genId(),
-      type: 'warn',
-      summary,
-      details,
-      ts: Date.now(),
-    })
-  }
-
-  error(summary: string, details?: any) {
-    details = detailsToStr(details)
-    console.debug(summary, details || '')
-    this.add({
-      id: genId(),
-      type: 'error',
-      summary,
-      details,
-      ts: Date.now(),
-    })
-  }
-
-  time = (label = 'default') => {
-    this.timers.set(label, performance.now())
-  }
-
-  timeEnd = (label = 'default', warn = false) => {
-    const endTime = performance.now()
-    if (this.timers.has(label)) {
-      const elapsedTime = endTime - this.timers.get(label)!
-      console.log(`${label}: ${elapsedTime.toFixed(3)}ms`)
-      this.timers.delete(label)
-    } else {
-      warn && console.warn(`Timer with label '${label}' does not exist.`)
-    }
-  }
-}
-
-function detailsToStr(details?: any) {
-  if (details && typeof details !== 'string') {
-    if (
-      // details instanceof XRPCInvalidResponseError || TODO
-      details.constructor.name === 'XRPCInvalidResponseError'
-    ) {
-      return `The server gave an ill-formatted response.\nMethod: ${
-        details.lexiconNsid
-      }.\nError: ${details.validationError.toString()}`
-    } else if (
-      // details instanceof XRPCError || TODO
-      details.constructor.name === 'XRPCError'
-    ) {
-      return `An XRPC error occurred.\nStatus: ${details.status}\nError: ${details.error}\nMessage: ${details.message}`
-    } else if (details instanceof Error) {
-      return details.toString()
-    }
-    return JSON.stringify(details, null, 2)
-  }
-  return details
-}
diff --git a/src/state/models/me.ts b/src/state/models/me.ts
index 14b2ef843..d12cb68c4 100644
--- a/src/state/models/me.ts
+++ b/src/state/models/me.ts
@@ -9,6 +9,7 @@ import {NotificationsFeedModel} from './feeds/notifications'
 import {MyFeedsUIModel} from './ui/my-feeds'
 import {MyFollowsCache} from './cache/my-follows'
 import {isObj, hasProp} from 'lib/type-guards'
+import {logger} from '#/logger'
 
 const PROFILE_UPDATE_INTERVAL = 10 * 60 * 1e3 // 10min
 const NOTIFS_UPDATE_INTERVAL = 30 * 1e3 // 30sec
@@ -104,21 +105,21 @@ export class MeModel {
 
   async load() {
     const sess = this.rootStore.session
-    this.rootStore.log.debug('MeModel:load', {hasSession: sess.hasSession})
+    logger.debug('MeModel:load', {hasSession: sess.hasSession})
     if (sess.hasSession) {
       this.did = sess.currentSession?.did || ''
       await this.fetchProfile()
       this.mainFeed.clear()
       /* dont await */ this.mainFeed.setup().catch(e => {
-        this.rootStore.log.error('Failed to setup main feed model', {error: e})
+        logger.error('Failed to setup main feed model', {error: e})
       })
       /* dont await */ this.notifications.setup().catch(e => {
-        this.rootStore.log.error('Failed to setup notifications model', {
+        logger.error('Failed to setup notifications model', {
           error: e,
         })
       })
       /* dont await */ this.notifications.setup().catch(e => {
-        this.rootStore.log.error('Failed to setup notifications model', {
+        logger.error('Failed to setup notifications model', {
           error: e,
         })
       })
@@ -134,7 +135,7 @@ export class MeModel {
 
   async updateIfNeeded() {
     if (Date.now() - this.lastProfileStateUpdate > PROFILE_UPDATE_INTERVAL) {
-      this.rootStore.log.debug('Updating me profile information')
+      logger.debug('Updating me profile information')
       this.lastProfileStateUpdate = Date.now()
       await this.fetchProfile()
       await this.fetchInviteCodes()
@@ -188,7 +189,7 @@ export class MeModel {
           })
         })
       } catch (e) {
-        this.rootStore.log.error('Failed to fetch user invite codes', {
+        logger.error('Failed to fetch user invite codes', {
           error: e,
         })
       }
@@ -205,7 +206,7 @@ export class MeModel {
           this.appPasswords = res.data.passwords
         })
       } catch (e) {
-        this.rootStore.log.error('Failed to fetch user app passwords', {
+        logger.error('Failed to fetch user app passwords', {
           error: e,
         })
       }
@@ -228,7 +229,7 @@ export class MeModel {
         })
         return res.data
       } catch (e) {
-        this.rootStore.log.error('Failed to create app password', {error: e})
+        logger.error('Failed to create app password', {error: e})
       }
     }
   }
@@ -243,7 +244,7 @@ export class MeModel {
           this.appPasswords = this.appPasswords.filter(p => p.name !== name)
         })
       } catch (e) {
-        this.rootStore.log.error('Failed to delete app password', {error: e})
+        logger.error('Failed to delete app password', {error: e})
       }
     }
   }
diff --git a/src/state/models/media/image.ts b/src/state/models/media/image.ts
index 4ca0b47c6..b3796060c 100644
--- a/src/state/models/media/image.ts
+++ b/src/state/models/media/image.ts
@@ -9,6 +9,7 @@ import {ActionCrop, FlipType, SaveFormat} from 'expo-image-manipulator'
 import {Position} from 'react-avatar-editor'
 import {Dimensions} from 'lib/media/types'
 import {isIOS} from 'platform/detection'
+import {logger} from '#/logger'
 
 export interface ImageManipulationAttributes {
   aspectRatio?: '4:3' | '1:1' | '3:4' | 'None'
@@ -188,7 +189,7 @@ export class ImageModel implements Omit<RNImage, 'size'> {
         this.cropped = cropped
       })
     } catch (err) {
-      this.rootStore.log.error('Failed to crop photo', {error: err})
+      logger.error('Failed to crop photo', {error: err})
     }
   }
 
diff --git a/src/state/models/root-store.ts b/src/state/models/root-store.ts
index 621c87c11..cf7307ca3 100644
--- a/src/state/models/root-store.ts
+++ b/src/state/models/root-store.ts
@@ -41,7 +41,6 @@ export type AppInfo = z.infer<typeof appInfo>
 export class RootStoreModel {
   agent: BskyAgent
   appInfo?: AppInfo
-  log = logger
   session = new SessionModel(this)
   shell = new ShellUiModel(this)
   preferences = new PreferencesModel(this)
@@ -122,15 +121,15 @@ export class RootStoreModel {
    * Called during init to resume any stored session.
    */
   async attemptSessionResumption() {
-    this.log.debug('RootStoreModel:attemptSessionResumption')
+    logger.debug('RootStoreModel:attemptSessionResumption')
     try {
       await this.session.attemptSessionResumption()
-      this.log.debug('Session initialized', {
+      logger.debug('Session initialized', {
         hasSession: this.session.hasSession,
       })
       this.updateSessionState()
     } catch (e: any) {
-      this.log.warn('Failed to initialize session', {error: e})
+      logger.warn('Failed to initialize session', {error: e})
     }
   }
 
@@ -141,7 +140,7 @@ export class RootStoreModel {
     agent: BskyAgent,
     {hadSession}: {hadSession: boolean},
   ) {
-    this.log.debug('RootStoreModel:handleSessionChange')
+    logger.debug('RootStoreModel:handleSessionChange')
     this.agent = agent
     applyDebugHeader(this.agent)
     this.me.clear()
@@ -157,7 +156,7 @@ export class RootStoreModel {
    * Called by the session model. Handles session drops by informing the user.
    */
   async handleSessionDrop() {
-    this.log.debug('RootStoreModel:handleSessionDrop')
+    logger.debug('RootStoreModel:handleSessionDrop')
     resetToTab('HomeTab')
     this.me.clear()
     this.emitSessionDropped()
@@ -167,7 +166,7 @@ export class RootStoreModel {
    * Clears all session-oriented state.
    */
   clearAllSessionState() {
-    this.log.debug('RootStoreModel:clearAllSessionState')
+    logger.debug('RootStoreModel:clearAllSessionState')
     this.session.clear()
     resetToTab('HomeTab')
     this.me.clear()
@@ -184,7 +183,7 @@ export class RootStoreModel {
       await this.me.updateIfNeeded()
       await this.preferences.sync()
     } catch (e: any) {
-      this.log.error('Failed to fetch latest state', {error: e})
+      logger.error('Failed to fetch latest state', {error: e})
     }
   }
 
diff --git a/src/state/models/session.ts b/src/state/models/session.ts
index 7cd3c1222..5b95c7d32 100644
--- a/src/state/models/session.ts
+++ b/src/state/models/session.ts
@@ -12,6 +12,7 @@ import {z} from 'zod'
 import {RootStoreModel} from './root-store'
 import {IS_PROD} from 'lib/constants'
 import {track} from 'lib/analytics/analytics'
+import {logger} from '#/logger'
 
 export type ServiceDescription = DescribeServer.OutputSchema
 
@@ -56,7 +57,7 @@ export class SessionModel {
       ),
       isResumingSession: this.isResumingSession,
     }
-    this.rootStore.log.debug(message, details)
+    logger.debug(message, details, logger.DebugContext.session)
   }
 
   /**
diff --git a/src/state/models/ui/create-account.ts b/src/state/models/ui/create-account.ts
index 3bd39ba76..1711b530f 100644
--- a/src/state/models/ui/create-account.ts
+++ b/src/state/models/ui/create-account.ts
@@ -8,6 +8,7 @@ import {createFullHandle} from 'lib/strings/handles'
 import {cleanError} from 'lib/strings/errors'
 import {getAge} from 'lib/strings/time'
 import {track} from 'lib/analytics/analytics'
+import {logger} from '#/logger'
 
 const DEFAULT_DATE = new Date(Date.now() - 60e3 * 60 * 24 * 365 * 20) // default to 20 years ago
 
@@ -76,7 +77,7 @@ export class CreateAccountModel {
       this.setServiceDescription(desc)
       this.setUserDomain(desc.availableUserDomains[0])
     } catch (err: any) {
-      this.rootStore.log.warn(
+      logger.warn(
         `Failed to fetch service description for ${this.serviceUrl}`,
         {error: err},
       )
@@ -127,7 +128,7 @@ export class CreateAccountModel {
         errMsg =
           'Invite code not accepted. Check that you input it correctly and try again.'
       }
-      this.rootStore.log.error('Failed to create account', {error: e})
+      logger.error('Failed to create account', {error: e})
       this.setIsProcessing(false)
       this.setError(cleanError(errMsg))
       throw e
diff --git a/src/state/models/ui/preferences.ts b/src/state/models/ui/preferences.ts
index 7714d65df..6e43198a3 100644
--- a/src/state/models/ui/preferences.ts
+++ b/src/state/models/ui/preferences.ts
@@ -14,6 +14,7 @@ import {deviceLocales} from 'platform/detection'
 import {getAge} from 'lib/strings/time'
 import {FeedTuner} from 'lib/api/feed-manip'
 import {LANGUAGES} from '../../../locale/languages'
+import {logger} from '#/logger'
 
 // TEMP we need to permanently convert 'show' to 'ignore', for now we manually convert -prf
 export type LabelPreference = APILabelPreference | 'show'
@@ -246,7 +247,7 @@ export class PreferencesModel {
           })
           await this.rootStore.agent.setSavedFeeds(saved, pinned)
         } catch (error) {
-          this.rootStore.log.error('Failed to set default feeds', {error})
+          logger.error('Failed to set default feeds', {error})
         }
       }
     } finally {
diff --git a/src/state/models/ui/profile.ts b/src/state/models/ui/profile.ts
index 47a99a8fc..f96340c65 100644
--- a/src/state/models/ui/profile.ts
+++ b/src/state/models/ui/profile.ts
@@ -4,6 +4,7 @@ import {ProfileModel} from '../content/profile'
 import {PostsFeedModel} from '../feeds/posts'
 import {ActorFeedsModel} from '../lists/actor-feeds'
 import {ListsListModel} from '../lists/lists-list'
+import {logger} from '#/logger'
 
 export enum Sections {
   PostsNoReplies = 'Posts',
@@ -223,14 +224,10 @@ export class ProfileUiModel {
     await Promise.all([
       this.profile
         .setup()
-        .catch(err =>
-          this.rootStore.log.error('Failed to fetch profile', {error: err}),
-        ),
+        .catch(err => logger.error('Failed to fetch profile', {error: err})),
       this.feed
         .setup()
-        .catch(err =>
-          this.rootStore.log.error('Failed to fetch feed', {error: err}),
-        ),
+        .catch(err => logger.error('Failed to fetch feed', {error: err})),
     ])
     runInAction(() => {
       this.isAuthenticatedUser =
@@ -241,9 +238,7 @@ export class ProfileUiModel {
     this.lists.source = this.profile.did
     this.lists
       .loadMore()
-      .catch(err =>
-        this.rootStore.log.error('Failed to fetch lists', {error: err}),
-      )
+      .catch(err => logger.error('Failed to fetch lists', {error: err}))
   }
 
   async refresh() {
diff --git a/src/state/models/ui/saved-feeds.ts b/src/state/models/ui/saved-feeds.ts
index 72055abeb..fd84edc02 100644
--- a/src/state/models/ui/saved-feeds.ts
+++ b/src/state/models/ui/saved-feeds.ts
@@ -4,6 +4,7 @@ import {bundleAsync} from 'lib/async/bundle'
 import {cleanError} from 'lib/strings/errors'
 import {FeedSourceModel} from '../content/feed-source'
 import {track} from 'lib/analytics/analytics'
+import {logger} from '#/logger'
 
 export class SavedFeedsModel {
   // state
@@ -126,7 +127,7 @@ export class SavedFeedsModel {
     this.hasLoaded = true
     this.error = cleanError(err)
     if (err) {
-      this.rootStore.log.error('Failed to fetch user feeds', {err})
+      logger.error('Failed to fetch user feeds', {err})
     }
   }