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/models/media/gallery.ts25
-rw-r--r--src/state/queries/post-feed.ts61
-rw-r--r--src/state/shell/composer.tsx2
3 files changed, 67 insertions, 21 deletions
diff --git a/src/state/models/media/gallery.ts b/src/state/models/media/gallery.ts
index 04023bf82..9c8c13010 100644
--- a/src/state/models/media/gallery.ts
+++ b/src/state/models/media/gallery.ts
@@ -4,11 +4,21 @@ import {Image as RNImage} from 'react-native-image-crop-picker'
 import {openPicker} from 'lib/media/picker'
 import {getImageDim} from 'lib/media/manip'
 
+interface InitialImageUri {
+  uri: string
+  width: number
+  height: number
+}
+
 export class GalleryModel {
   images: ImageModel[] = []
 
-  constructor() {
+  constructor(uris?: {uri: string; width: number; height: number}[]) {
     makeAutoObservable(this)
+
+    if (uris) {
+      this.addFromUris(uris)
+    }
   }
 
   get isEmpty() {
@@ -23,7 +33,7 @@ export class GalleryModel {
     return this.images.some(image => image.altText.trim() === '')
   }
 
-  async add(image_: Omit<RNImage, 'size'>) {
+  *add(image_: Omit<RNImage, 'size'>) {
     if (this.size >= 4) {
       return
     }
@@ -86,4 +96,15 @@ export class GalleryModel {
       }),
     )
   }
+
+  async addFromUris(uris: InitialImageUri[]) {
+    for (const uriObj of uris) {
+      this.add({
+        mime: 'image/jpeg',
+        height: uriObj.height,
+        width: uriObj.width,
+        path: uriObj.uri,
+      })
+    }
+  }
 }
diff --git a/src/state/queries/post-feed.ts b/src/state/queries/post-feed.ts
index 40399395a..c295ffcb0 100644
--- a/src/state/queries/post-feed.ts
+++ b/src/state/queries/post-feed.ts
@@ -1,6 +1,11 @@
 import React, {useCallback, useEffect, useRef} from 'react'
 import {AppState} from 'react-native'
-import {AppBskyFeedDefs, AppBskyFeedPost, PostModeration} from '@atproto/api'
+import {
+  AppBskyFeedDefs,
+  AppBskyFeedPost,
+  AtUri,
+  PostModeration,
+} from '@atproto/api'
 import {
   useInfiniteQuery,
   InfiniteData,
@@ -29,6 +34,7 @@ import {KnownError} from '#/view/com/posts/FeedErrorMessage'
 import {embedViewRecordToPostView, getEmbeddedPost} from './util'
 import {useModerationOpts} from './preferences'
 import {queryClient} from 'lib/react-query'
+import {BSKY_FEED_OWNER_DIDS} from 'lib/constants'
 
 type ActorDid = string
 type AuthorFilter =
@@ -137,24 +143,41 @@ export function usePostFeedQuery(
             cursor: undefined,
           }
 
-      const res = await api.fetch({cursor, limit: PAGE_SIZE})
-      precacheFeedPostProfiles(queryClient, res.feed)
-
-      /*
-       * If this is a public view, we need to check if posts fail moderation.
-       * If all fail, we throw an error. If only some fail, we continue and let
-       * moderations happen later, which results in some posts being shown and
-       * some not.
-       */
-      if (!getAgent().session) {
-        assertSomePostsPassModeration(res.feed)
-      }
+      try {
+        const res = await api.fetch({cursor, limit: PAGE_SIZE})
+        precacheFeedPostProfiles(queryClient, res.feed)
+
+        /*
+         * If this is a public view, we need to check if posts fail moderation.
+         * If all fail, we throw an error. If only some fail, we continue and let
+         * moderations happen later, which results in some posts being shown and
+         * some not.
+         */
+        if (!getAgent().session) {
+          assertSomePostsPassModeration(res.feed)
+        }
+
+        return {
+          api,
+          cursor: res.cursor,
+          feed: res.feed,
+          fetchedAt: Date.now(),
+        }
+      } catch (e) {
+        const feedDescParts = feedDesc.split('|')
+        const feedOwnerDid = new AtUri(feedDescParts[1]).hostname
+
+        if (
+          feedDescParts[0] === 'feedgen' &&
+          BSKY_FEED_OWNER_DIDS.includes(feedOwnerDid)
+        ) {
+          logger.error(`Bluesky feed may be offline: ${feedOwnerDid}`, {
+            feedDesc,
+            jsError: e,
+          })
+        }
 
-      return {
-        api,
-        cursor: res.cursor,
-        feed: res.feed,
-        fetchedAt: Date.now(),
+        throw e
       }
     },
     initialPageParam: undefined,
@@ -253,7 +276,7 @@ export function usePostFeedQuery(
                             .success
                         ) {
                           return {
-                            _reactKey: `${slice._reactKey}-${i}`,
+                            _reactKey: `${slice._reactKey}-${i}-${item.post.uri}`,
                             uri: item.post.uri,
                             post: item.post,
                             record: item.post.record,
diff --git a/src/state/shell/composer.tsx b/src/state/shell/composer.tsx
index 696a3c5ba..c9dbfbeac 100644
--- a/src/state/shell/composer.tsx
+++ b/src/state/shell/composer.tsx
@@ -38,6 +38,8 @@ export interface ComposerOpts {
   quote?: ComposerOptsQuote
   mention?: string // handle of user to mention
   openPicker?: (pos: DOMRect | undefined) => void
+  text?: string
+  imageUris?: {uri: string; width: number; height: number}[]
 }
 
 type StateContext = ComposerOpts | undefined