about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/hooks/useIntentHandler.ts35
-rw-r--r--src/state/models/media/gallery.ts25
-rw-r--r--src/state/shell/composer.tsx2
-rw-r--r--src/view/com/composer/Composer.tsx11
-rw-r--r--src/view/shell/Composer.tsx2
-rw-r--r--src/view/shell/Composer.web.tsx3
6 files changed, 67 insertions, 11 deletions
diff --git a/src/lib/hooks/useIntentHandler.ts b/src/lib/hooks/useIntentHandler.ts
index 249e6898e..de9a96da9 100644
--- a/src/lib/hooks/useIntentHandler.ts
+++ b/src/lib/hooks/useIntentHandler.ts
@@ -6,6 +6,8 @@ import {useSession} from 'state/session'
 
 type IntentType = 'compose'
 
+const VALID_IMAGE_REGEX = /^[\w.:\-_/]+\|\d+(\.\d+)?\|\d+(\.\d+)?$/
+
 export function useIntentHandler() {
   const incomingUrl = Linking.useURL()
   const composeIntent = useComposeIntent()
@@ -29,7 +31,7 @@ export function useIntentHandler() {
         case 'compose': {
           composeIntent({
             text: params.get('text'),
-            imageUris: params.get('imageUris'),
+            imageUrisStr: params.get('imageUris'),
           })
         }
       }
@@ -45,18 +47,39 @@ function useComposeIntent() {
 
   return React.useCallback(
     ({
-      // eslint-disable-next-line @typescript-eslint/no-unused-vars
       text,
-      // eslint-disable-next-line @typescript-eslint/no-unused-vars
-      imageUris,
+      imageUrisStr,
     }: {
       text: string | null
-      imageUris: string | null // unused for right now, will be used later with intents
+      imageUrisStr: string | null // unused for right now, will be used later with intents
     }) => {
       if (!hasSession) return
 
+      const imageUris = imageUrisStr
+        ?.split(',')
+        .filter(part => {
+          // For some security, we're going to filter out any image uri that is external. We don't want someone to
+          // be able to provide some link like "bluesky://intent/compose?imageUris=https://IHaveYourIpNow.com/image.jpeg
+          // and we load that image
+          if (part.includes('https://') || part.includes('http://')) {
+            return false
+          }
+          // We also should just filter out cases that don't have all the info we need
+          if (!VALID_IMAGE_REGEX.test(part)) {
+            return false
+          }
+          return true
+        })
+        .map(part => {
+          const [uri, width, height] = part.split('|')
+          return {uri, width: Number(width), height: Number(height)}
+        })
+
       setTimeout(() => {
-        openComposer({}) // will pass in values to the composer here in the share extension
+        openComposer({
+          text: text ?? undefined,
+          imageUris: isNative ? imageUris : undefined,
+        })
       }, 500)
     },
     [openComposer, hasSession],
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/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
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index 1ed6b98a5..2855d4232 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -71,6 +71,8 @@ export const ComposePost = observer(function ComposePost({
   quote: initQuote,
   mention: initMention,
   openPicker,
+  text: initText,
+  imageUris: initImageUris,
 }: Props) {
   const {currentAccount} = useSession()
   const {data: currentProfile} = useProfileQuery({did: currentAccount!.did})
@@ -91,7 +93,9 @@ export const ComposePost = observer(function ComposePost({
   const [error, setError] = useState('')
   const [richtext, setRichText] = useState(
     new RichText({
-      text: initMention
+      text: initText
+        ? initText
+        : initMention
         ? insertMentionAt(
             `@${initMention}`,
             initMention.length + 1,
@@ -110,7 +114,10 @@ export const ComposePost = observer(function ComposePost({
   const [labels, setLabels] = useState<string[]>([])
   const [threadgate, setThreadgate] = useState<ThreadgateSetting[]>([])
   const [suggestedLinks, setSuggestedLinks] = useState<Set<string>>(new Set())
-  const gallery = useMemo(() => new GalleryModel(), [])
+  const gallery = useMemo(
+    () => new GalleryModel(initImageUris),
+    [initImageUris],
+  )
   const onClose = useCallback(() => {
     closeComposer()
   }, [closeComposer])
diff --git a/src/view/shell/Composer.tsx b/src/view/shell/Composer.tsx
index d37ff4fb7..1937fcb6e 100644
--- a/src/view/shell/Composer.tsx
+++ b/src/view/shell/Composer.tsx
@@ -55,6 +55,8 @@ export const Composer = observer(function ComposerImpl({
         onPost={state.onPost}
         quote={state.quote}
         mention={state.mention}
+        text={state.text}
+        imageUris={state.imageUris}
       />
     </Animated.View>
   )
diff --git a/src/view/shell/Composer.web.tsx b/src/view/shell/Composer.web.tsx
index 99e659d62..00233f66a 100644
--- a/src/view/shell/Composer.web.tsx
+++ b/src/view/shell/Composer.web.tsx
@@ -9,7 +9,7 @@ import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock'
 import {
   EmojiPicker,
   EmojiPickerState,
-} from 'view/com/composer/text-input/web/EmojiPicker.web.tsx'
+} from 'view/com/composer/text-input/web/EmojiPicker.web'
 
 const BOTTOM_BAR_HEIGHT = 61
 
@@ -69,6 +69,7 @@ export function Composer({}: {winHeight: number}) {
           onPost={state.onPost}
           mention={state.mention}
           openPicker={onOpenPicker}
+          text={state.text}
         />
       </Animated.View>
       <EmojiPicker state={pickerState} close={onClosePicker} />