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/index.ts105
-rw-r--r--src/lib/link-meta/bsky.ts8
-rw-r--r--src/lib/moderation.ts13
-rw-r--r--src/lib/moderation/useModerationCauseDescription.ts27
4 files changed, 96 insertions, 57 deletions
diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts
index 658ed78de..94c8869a1 100644
--- a/src/lib/api/index.ts
+++ b/src/lib/api/index.ts
@@ -3,7 +3,7 @@ import {
   AppBskyEmbedImages,
   AppBskyEmbedRecord,
   AppBskyEmbedRecordWithMedia,
-  AppBskyFeedThreadgate,
+  AppBskyFeedPostgate,
   BskyAgent,
   ComAtprotoLabelDefs,
   RichText,
@@ -11,7 +11,13 @@ import {
 import {AtUri} from '@atproto/api'
 
 import {logger} from '#/logger'
-import {ThreadgateSetting} from '#/state/queries/threadgate'
+import {writePostgateRecord} from '#/state/queries/postgate'
+import {
+  createThreadgateRecord,
+  ThreadgateAllowUISetting,
+  threadgateAllowUISettingToAllowRecordValue,
+  writeThreadgateRecord,
+} from '#/state/queries/threadgate'
 import {isNetworkError} from 'lib/strings/errors'
 import {shortenLinks, stripInvalidMentions} from 'lib/strings/rich-text-manip'
 import {isNative} from 'platform/detection'
@@ -44,7 +50,8 @@ interface PostOpts {
   extLink?: ExternalEmbedDraft
   images?: ImageModel[]
   labels?: string[]
-  threadgate?: ThreadgateSetting[]
+  threadgate: ThreadgateAllowUISetting[]
+  postgate: AppBskyFeedPostgate.Record
   onStateChange?: (state: string) => void
   langs?: string[]
 }
@@ -232,7 +239,9 @@ export async function post(agent: BskyAgent, opts: PostOpts) {
       labels,
     })
   } catch (e: any) {
-    console.error(`Failed to create post: ${e.toString()}`)
+    logger.error(`Failed to create post`, {
+      safeMessage: e.message,
+    })
     if (isNetworkError(e)) {
       throw new Error(
         'Post failed to upload. Please check your Internet connection and try again.',
@@ -242,56 +251,52 @@ export async function post(agent: BskyAgent, opts: PostOpts) {
     }
   }
 
-  try {
-    // TODO: this needs to be batch-created with the post!
-    if (opts.threadgate?.length) {
-      await createThreadgate(agent, res.uri, opts.threadgate)
+  if (opts.threadgate.some(tg => tg.type !== 'everybody')) {
+    try {
+      // TODO: this needs to be batch-created with the post!
+      await writeThreadgateRecord({
+        agent,
+        postUri: res.uri,
+        threadgate: createThreadgateRecord({
+          post: res.uri,
+          allow: threadgateAllowUISettingToAllowRecordValue(opts.threadgate),
+        }),
+      })
+    } catch (e: any) {
+      logger.error(`Failed to create threadgate`, {
+        context: 'composer',
+        safeMessage: e.message,
+      })
+      throw new Error(
+        'Failed to save post interaction settings. Your post was created but users may be able to interact with it.',
+      )
     }
-  } catch (e: any) {
-    console.error(`Failed to create threadgate: ${e.toString()}`)
-    throw new Error(
-      'Post reply-controls failed to be set. Your post was created but anyone can reply to it.',
-    )
   }
 
-  return res
-}
-
-export async function createThreadgate(
-  agent: BskyAgent,
-  postUri: string,
-  threadgate: ThreadgateSetting[],
-) {
-  let allow: (
-    | AppBskyFeedThreadgate.MentionRule
-    | AppBskyFeedThreadgate.FollowingRule
-    | AppBskyFeedThreadgate.ListRule
-  )[] = []
-  if (!threadgate.find(v => v.type === 'nobody')) {
-    for (const rule of threadgate) {
-      if (rule.type === 'mention') {
-        allow.push({$type: 'app.bsky.feed.threadgate#mentionRule'})
-      } else if (rule.type === 'following') {
-        allow.push({$type: 'app.bsky.feed.threadgate#followingRule'})
-      } else if (rule.type === 'list') {
-        allow.push({
-          $type: 'app.bsky.feed.threadgate#listRule',
-          list: rule.list,
-        })
-      }
+  if (
+    opts.postgate.embeddingRules?.length ||
+    opts.postgate.detachedEmbeddingUris?.length
+  ) {
+    try {
+      // TODO: this needs to be batch-created with the post!
+      await writePostgateRecord({
+        agent,
+        postUri: res.uri,
+        postgate: {
+          ...opts.postgate,
+          post: res.uri,
+        },
+      })
+    } catch (e: any) {
+      logger.error(`Failed to create postgate`, {
+        context: 'composer',
+        safeMessage: e.message,
+      })
+      throw new Error(
+        'Failed to save post interaction settings. Your post was created but users may be able to interact with it.',
+      )
     }
   }
 
-  const postUrip = new AtUri(postUri)
-  await agent.api.com.atproto.repo.putRecord({
-    repo: agent.accountDid,
-    collection: 'app.bsky.feed.threadgate',
-    rkey: postUrip.rkey,
-    record: {
-      $type: 'app.bsky.feed.threadgate',
-      post: postUri,
-      allow,
-      createdAt: new Date().toISOString(),
-    },
-  })
+  return res
 }
diff --git a/src/lib/link-meta/bsky.ts b/src/lib/link-meta/bsky.ts
index e3b4ea0c9..3d49f5237 100644
--- a/src/lib/link-meta/bsky.ts
+++ b/src/lib/link-meta/bsky.ts
@@ -107,6 +107,11 @@ export async function extractBskyMeta(
   return meta
 }
 
+export class EmbeddingDisabledError extends Error {
+  constructor() {
+    super('Embedding is disabled for this record')
+  }
+}
 export async function getPostAsQuote(
   getPost: ReturnType<typeof useGetPost>,
   url: string,
@@ -115,6 +120,9 @@ export async function getPostAsQuote(
   const [_0, user, _1, rkey] = url.split('/').filter(Boolean)
   const uri = makeRecordUri(user, 'app.bsky.feed.post', rkey)
   const post = await getPost({uri: uri})
+  if (post.viewer?.embeddingDisabled) {
+    throw new EmbeddingDisabledError()
+  }
   return {
     uri: post.uri,
     cid: post.cid,
diff --git a/src/lib/moderation.ts b/src/lib/moderation.ts
index 4105c2c2d..3c96deecb 100644
--- a/src/lib/moderation.ts
+++ b/src/lib/moderation.ts
@@ -1,17 +1,20 @@
 import {
-  ModerationCause,
-  ModerationUI,
-  InterpretedLabelValueDefinition,
-  LABELS,
   AppBskyLabelerDefs,
   BskyAgent,
+  InterpretedLabelValueDefinition,
+  LABELS,
+  ModerationCause,
   ModerationOpts,
+  ModerationUI,
 } from '@atproto/api'
 
 import {sanitizeDisplayName} from '#/lib/strings/display-names'
 import {sanitizeHandle} from '#/lib/strings/handles'
+import {AppModerationCause} from '#/components/Pills'
 
-export function getModerationCauseKey(cause: ModerationCause): string {
+export function getModerationCauseKey(
+  cause: ModerationCause | AppModerationCause,
+): string {
   const source =
     cause.source.type === 'labeler'
       ? cause.source.did
diff --git a/src/lib/moderation/useModerationCauseDescription.ts b/src/lib/moderation/useModerationCauseDescription.ts
index 01ffbe5cf..9dce0b565 100644
--- a/src/lib/moderation/useModerationCauseDescription.ts
+++ b/src/lib/moderation/useModerationCauseDescription.ts
@@ -8,11 +8,13 @@ import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
 import {useLabelDefinitions} from '#/state/preferences'
+import {useSession} from '#/state/session'
 import {CircleBanSign_Stroke2_Corner0_Rounded as CircleBanSign} from '#/components/icons/CircleBanSign'
 import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
 import {Props as SVGIconProps} from '#/components/icons/common'
 import {EyeSlash_Stroke2_Corner0_Rounded as EyeSlash} from '#/components/icons/EyeSlash'
 import {Warning_Stroke2_Corner0_Rounded as Warning} from '#/components/icons/Warning'
+import {AppModerationCause} from '#/components/Pills'
 import {useGlobalLabelStrings} from './useGlobalLabelStrings'
 import {getDefinition, getLabelStrings} from './useLabelInfo'
 
@@ -27,8 +29,9 @@ export interface ModerationCauseDescription {
 }
 
 export function useModerationCauseDescription(
-  cause: ModerationCause | undefined,
+  cause: ModerationCause | AppModerationCause | undefined,
 ): ModerationCauseDescription {
+  const {currentAccount} = useSession()
   const {_, i18n} = useLingui()
   const {labelDefs, labelers} = useLabelDefinitions()
   const globalLabelStrings = useGlobalLabelStrings()
@@ -111,6 +114,18 @@ export function useModerationCauseDescription(
         description: _(msg`You have hidden this post`),
       }
     }
+    if (cause.type === 'reply-hidden') {
+      const isMe = currentAccount?.did === cause.source.did
+      return {
+        icon: EyeSlash,
+        name: isMe
+          ? _(msg`Reply Hidden by You`)
+          : _(msg`Reply Hidden by Thread Author`),
+        description: isMe
+          ? _(msg`You hid this reply.`)
+          : _(msg`The author of this thread has hidden this reply.`),
+      }
+    }
     if (cause.type === 'label') {
       const def = cause.labelDef || getDefinition(labelDefs, cause.label)
       const strings = getLabelStrings(i18n.locale, globalLabelStrings, def)
@@ -150,5 +165,13 @@ export function useModerationCauseDescription(
       name: '',
       description: ``,
     }
-  }, [labelDefs, labelers, globalLabelStrings, cause, _, i18n.locale])
+  }, [
+    labelDefs,
+    labelers,
+    globalLabelStrings,
+    cause,
+    _,
+    i18n.locale,
+    currentAccount?.did,
+  ])
 }