about summary refs log tree commit diff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/api/feed/author.ts10
-rw-r--r--src/lib/constants.ts19
-rw-r--r--src/lib/generate-starterpack.ts23
-rw-r--r--src/lib/hooks/useCleanError.ts2
-rw-r--r--src/lib/hooks/useDraggableScrollView.ts6
-rw-r--r--src/lib/icons.tsx4
-rw-r--r--src/lib/media/picker.e2e.tsx17
-rw-r--r--src/lib/media/picker.shared.ts24
-rw-r--r--src/lib/media/picker.tsx6
-rw-r--r--src/lib/media/picker.web.tsx2
-rw-r--r--src/lib/routes/types.ts6
-rw-r--r--src/lib/statsig/gates.ts5
-rw-r--r--src/lib/strings/errors.ts2
13 files changed, 93 insertions, 33 deletions
diff --git a/src/lib/api/feed/author.ts b/src/lib/api/feed/author.ts
index 36cce71f0..cc19f0f7a 100644
--- a/src/lib/api/feed/author.ts
+++ b/src/lib/api/feed/author.ts
@@ -1,10 +1,10 @@
 import {
   AppBskyFeedDefs,
-  AppBskyFeedGetAuthorFeed as GetAuthorFeed,
-  BskyAgent,
+  type AppBskyFeedGetAuthorFeed as GetAuthorFeed,
+  type BskyAgent,
 } from '@atproto/api'
 
-import {FeedAPI, FeedAPIResponse} from './types'
+import {type FeedAPI, type FeedAPIResponse} from './types'
 
 export class AuthorFeedAPI implements FeedAPI {
   agent: BskyAgent
@@ -23,9 +23,7 @@ export class AuthorFeedAPI implements FeedAPI {
 
   get params() {
     const params = {...this._params}
-    params.includePins =
-      params.filter === 'posts_with_replies' ||
-      params.filter === 'posts_and_author_threads'
+    params.includePins = params.filter === 'posts_and_author_threads'
     return params
   }
 
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index 130722b9c..b6b06ee7f 100644
--- a/src/lib/constants.ts
+++ b/src/lib/constants.ts
@@ -1,6 +1,9 @@
 import {type Insets, Platform} from 'react-native'
 import {type AppBskyActorDefs} from '@atproto/api'
 
+import {type ProxyHeaderValue} from '#/state/session/agent'
+import {BLUESKY_PROXY_DID, CHAT_PROXY_DID} from '#/env'
+
 export const LOCAL_DEV_SERVICE =
   Platform.OS === 'android' ? 'http://10.0.2.2:2583' : 'http://localhost:2583'
 export const STAGING_SERVICE = 'https://staging.bsky.dev'
@@ -90,8 +93,6 @@ export const STAGING_FEEDS = [
   `feedgen|${STAGING_DEFAULT_FEED('thevids')}`,
 ]
 
-export const FEEDBACK_FEEDS = [...PROD_FEEDS, ...STAGING_FEEDS]
-
 export const POST_IMG_MAX = {
   width: 2000,
   height: 2000,
@@ -213,6 +214,20 @@ export const PUBLIC_STAGING_APPVIEW_DID = 'did:web:api.staging.bsky.dev'
 
 export const DEV_ENV_APPVIEW = `http://localhost:2584` // always the same
 
+// temp hack for e2e - esb
+export let BLUESKY_PROXY_HEADER: ProxyHeaderValue = `${BLUESKY_PROXY_DID}#bsky_appview`
+export function setBlueskyProxyHeader(header: ProxyHeaderValue) {
+  BLUESKY_PROXY_HEADER = header
+}
+
+export const BLUESKY_SERVICE_HEADERS = {
+  'atproto-proxy': BLUESKY_PROXY_HEADER,
+}
+
+export const DM_SERVICE_HEADERS = {
+  'atproto-proxy': `${CHAT_PROXY_DID}#bsky_chat`,
+}
+
 export const webLinks = {
   tos: `https://bsky.social/about/support/tos`,
   privacy: `https://bsky.social/about/support/privacy-policy`,
diff --git a/src/lib/generate-starterpack.ts b/src/lib/generate-starterpack.ts
index 11e334329..76bef3fbe 100644
--- a/src/lib/generate-starterpack.ts
+++ b/src/lib/generate-starterpack.ts
@@ -1,10 +1,10 @@
 import {
-  $Typed,
-  AppBskyActorDefs,
-  AppBskyGraphGetStarterPack,
-  BskyAgent,
-  ComAtprotoRepoApplyWrites,
-  Facet,
+  type $Typed,
+  type AppBskyActorDefs,
+  type AppBskyGraphGetStarterPack,
+  type BskyAgent,
+  type ComAtprotoRepoApplyWrites,
+  type Facet,
 } from '@atproto/api'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
@@ -15,7 +15,7 @@ import {sanitizeDisplayName} from '#/lib/strings/display-names'
 import {sanitizeHandle} from '#/lib/strings/handles'
 import {enforceLen} from '#/lib/strings/helpers'
 import {useAgent} from '#/state/session'
-import * as bsky from '#/types/bsky'
+import type * as bsky from '#/types/bsky'
 
 export const createStarterPackList = async ({
   name,
@@ -46,14 +46,7 @@ export const createStarterPackList = async ({
   if (!list) throw new Error('List creation failed')
   await agent.com.atproto.repo.applyWrites({
     repo: agent.session!.did,
-    writes: [
-      createListItem({did: agent.session!.did, listUri: list.uri}),
-    ].concat(
-      profiles
-        // Ensure we don't have ourselves in this list twice
-        .filter(p => p.did !== agent.session!.did)
-        .map(p => createListItem({did: p.did, listUri: list.uri})),
-    ),
+    writes: profiles.map(p => createListItem({did: p.did, listUri: list.uri})),
   })
 
   return list
diff --git a/src/lib/hooks/useCleanError.ts b/src/lib/hooks/useCleanError.ts
index dc9284e90..3b7b575bc 100644
--- a/src/lib/hooks/useCleanError.ts
+++ b/src/lib/hooks/useCleanError.ts
@@ -42,7 +42,7 @@ export function useCleanError() {
         }
       }
 
-      if (raw.includes('Bad token scope')) {
+      if (raw.includes('Bad token scope') || raw.includes('Bad token method')) {
         return {
           raw,
           clean: _(
diff --git a/src/lib/hooks/useDraggableScrollView.ts b/src/lib/hooks/useDraggableScrollView.ts
index 05fda9a9f..d4d35ccda 100644
--- a/src/lib/hooks/useDraggableScrollView.ts
+++ b/src/lib/hooks/useDraggableScrollView.ts
@@ -20,9 +20,6 @@ export function useDraggableScroll<Scrollable extends ScrollView = ScrollView>({
       return
     }
     const slider = ref.current as unknown as HTMLDivElement
-    if (!slider) {
-      return
-    }
     let isDragging = false
     let isMouseDown = false
     let startX = 0
@@ -61,6 +58,9 @@ export function useDraggableScroll<Scrollable extends ScrollView = ScrollView>({
       e.preventDefault()
       const walk = x - startX
       slider.scrollLeft = scrollLeft - walk
+
+      if (slider.contains(document.activeElement))
+        (document.activeElement as HTMLElement)?.blur?.()
     }
 
     slider.addEventListener('mousedown', mouseDown)
diff --git a/src/lib/icons.tsx b/src/lib/icons.tsx
index 6e0be9d0a..4bea1ebef 100644
--- a/src/lib/icons.tsx
+++ b/src/lib/icons.tsx
@@ -7,17 +7,19 @@ export function MagnifyingGlassIcon({
   style,
   size,
   strokeWidth = 2,
+  color = 'currentColor',
 }: {
   style?: StyleProp<ViewStyle>
   size?: string | number
   strokeWidth?: number
+  color?: string
 }) {
   return (
     <Svg
       fill="none"
       viewBox="0 0 24 24"
       strokeWidth={strokeWidth}
-      stroke="currentColor"
+      stroke={color}
       width={size || 24}
       height={size || 24}
       style={style}>
diff --git a/src/lib/media/picker.e2e.tsx b/src/lib/media/picker.e2e.tsx
index a2a9357ec..92e1495e5 100644
--- a/src/lib/media/picker.e2e.tsx
+++ b/src/lib/media/picker.e2e.tsx
@@ -7,6 +7,7 @@ import ExpoImageCropTool, {type OpenCropperOptions} from 'expo-image-crop-tool'
 
 import {compressIfNeeded} from './manip'
 import {type PickerImage} from './picker.shared'
+import {ImagePickerResult} from 'expo-image-picker'
 
 async function getFile() {
   const imagesDir = documentDirectory!
@@ -38,6 +39,22 @@ export async function openPicker(): Promise<PickerImage[]> {
   return [await getFile()]
 }
 
+export async function openUnifiedPicker(): Promise<ImagePickerResult> {
+  const file = await getFile()
+
+  return {
+    assets: [
+      {
+        type: 'image',
+        uri: file.path,
+        mimeType: file.mime,
+        ...file,
+      },
+    ],
+    canceled: false,
+  }
+}
+
 export async function openCamera(): Promise<PickerImage> {
   return await getFile()
 }
diff --git a/src/lib/media/picker.shared.ts b/src/lib/media/picker.shared.ts
index 8ec1154c8..fc89cad33 100644
--- a/src/lib/media/picker.shared.ts
+++ b/src/lib/media/picker.shared.ts
@@ -1,11 +1,14 @@
 import {
   type ImagePickerOptions,
   launchImageLibraryAsync,
+  UIImagePickerPreferredAssetRepresentationMode,
 } from 'expo-image-picker'
 import {t} from '@lingui/macro'
 
+import {isIOS, isWeb} from '#/platform/detection'
 import {type ImageMeta} from '#/state/gallery'
 import * as Toast from '#/view/com/util/Toast'
+import {VIDEO_MAX_DURATION_MS} from '../constants'
 import {getDataUriSize} from './util'
 
 export type PickerImage = ImageMeta & {
@@ -20,6 +23,8 @@ export async function openPicker(opts?: ImagePickerOptions) {
     selectionLimit: 1,
     ...opts,
     legacy: true,
+    preferredAssetRepresentationMode:
+      UIImagePickerPreferredAssetRepresentationMode.Automatic,
   })
 
   return (response.assets ?? [])
@@ -36,3 +41,22 @@ export async function openPicker(opts?: ImagePickerOptions) {
       size: getDataUriSize(image.uri),
     }))
 }
+
+export async function openUnifiedPicker({
+  selectionCountRemaining,
+}: {
+  selectionCountRemaining: number
+}) {
+  return await launchImageLibraryAsync({
+    exif: false,
+    mediaTypes: ['images', 'videos'],
+    quality: 1,
+    allowsMultipleSelection: true,
+    legacy: true,
+    base64: isWeb,
+    selectionLimit: isIOS ? selectionCountRemaining : undefined,
+    preferredAssetRepresentationMode:
+      UIImagePickerPreferredAssetRepresentationMode.Automatic,
+    videoMaxDuration: VIDEO_MAX_DURATION_MS / 1000,
+  })
+}
diff --git a/src/lib/media/picker.tsx b/src/lib/media/picker.tsx
index 6095730d5..2526da3c8 100644
--- a/src/lib/media/picker.tsx
+++ b/src/lib/media/picker.tsx
@@ -1,7 +1,11 @@
 import ExpoImageCropTool, {type OpenCropperOptions} from 'expo-image-crop-tool'
 import {type ImagePickerOptions, launchCameraAsync} from 'expo-image-picker'
 
-export {openPicker, type PickerImage as RNImage} from './picker.shared'
+export {
+  openPicker,
+  openUnifiedPicker,
+  type PickerImage as RNImage,
+} from './picker.shared'
 
 export async function openCamera(customOpts: ImagePickerOptions) {
   const opts: ImagePickerOptions = {
diff --git a/src/lib/media/picker.web.tsx b/src/lib/media/picker.web.tsx
index c1e4e4ab7..233600583 100644
--- a/src/lib/media/picker.web.tsx
+++ b/src/lib/media/picker.web.tsx
@@ -3,7 +3,7 @@ import {type OpenCropperOptions} from 'expo-image-crop-tool'
 import {type PickerImage} from './picker.shared'
 import {type CameraOpts} from './types'
 
-export {openPicker} from './picker.shared'
+export {openPicker, openUnifiedPicker} from './picker.shared'
 
 export async function openCamera(_opts: CameraOpts): Promise<PickerImage> {
   throw new Error('openCamera is not supported on web')
diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts
index b1db5caa6..1725fdfb4 100644
--- a/src/lib/routes/types.ts
+++ b/src/lib/routes/types.ts
@@ -79,7 +79,11 @@ export type CommonNavigatorParams = {
   Start: {name: string; rkey: string}
   StarterPack: {name: string; rkey: string; new?: boolean}
   StarterPackShort: {code: string}
-  StarterPackWizard: undefined
+  StarterPackWizard: {
+    fromDialog?: boolean
+    targetDid?: string
+    onSuccess?: () => void
+  }
   StarterPackEdit: {rkey?: string}
   VideoFeed: VideoFeedSourceContext
 }
diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts
index ef6dc1d4d..391314162 100644
--- a/src/lib/statsig/gates.ts
+++ b/src/lib/statsig/gates.ts
@@ -1,14 +1,17 @@
 export type Gate =
   // Keep this alphabetic please.
   | 'alt_share_icon'
+  | 'cta_above_post_heading'
+  | 'cta_above_post_replies'
   | 'debug_show_feedcontext'
   | 'debug_subscriptions'
   | 'disable_onboarding_policy_update_notice'
   | 'explore_show_suggested_feeds'
   | 'old_postonboarding'
   | 'onboarding_add_video_feed'
+  | 'onboarding_suggested_accounts'
+  | 'onboarding_value_prop'
   | 'post_follow_profile_suggested_accounts'
-  | 'post_threads_v2_unspecced'
   | 'remove_show_latest_button'
   | 'test_gate_1'
   | 'test_gate_2'
diff --git a/src/lib/strings/errors.ts b/src/lib/strings/errors.ts
index 1a010fea6..6c8177e44 100644
--- a/src/lib/strings/errors.ts
+++ b/src/lib/strings/errors.ts
@@ -17,7 +17,7 @@ export function cleanError(str: any): string {
   ) {
     return t`The server appears to be experiencing issues. Please try again in a few moments.`
   }
-  if (str.includes('Bad token scope')) {
+  if (str.includes('Bad token scope') || str.includes('Bad token method')) {
     return t`This feature is not available while using an App Password. Please sign in with your main password.`
   }
   if (str.startsWith('Error: ')) {