about summary refs log tree commit diff
path: root/src/state
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2024-01-04 17:37:36 -0800
committerGitHub <noreply@github.com>2024-01-04 17:37:36 -0800
commit0dae24e78ffe0b2d69349a03f669949e4d5afc21 (patch)
treef2895aebae675d0c5d6c9213626a37a98a53f724 /src/state
parentdb62f272412df2c34e1a57200291b53fa1cd07aa (diff)
downloadvoidsky-0dae24e78ffe0b2d69349a03f669949e4d5afc21.tar.zst
Additional embed sources and external-media consent controls (#2424)
* add apple music embed

* add vimeo embed

* add logic for tenor and giphy embeds

* keep it simple, use playerUri for images too

* add gif embed player

* lint, fix tests

* remove links that can't produce a thumb

* Revert "remove links that can't produce a thumb"

This reverts commit 985b92b4e622db936bb0c79fdf324099b9c8fcd8.

* Revert "Revert "remove links that can't produce a thumb""

This reverts commit 4895ded8b5120c4fc52b43ae85c9a01ea0b1a733.

* Revert "Revert "Revert "remove links that can't produce a thumb"""

This reverts commit 36d04b517ba5139e1639f2eda28d7f9aaa2dbfb6.

* properly obtain giphy metadata regardless of used url

* test fixes

* adjust gif player

* add all twitch embed types

* support m.youtube links

* few logic adjustments

* adjust spotify player height

* prefetch gif before showing

* use memory-disk cache policy on gifs

* use `disk` cachePolicy on ios - can't start/stop animation

* support pause/play on web

* onLoad fix

* remove extra pressable, add accessibility, fix scale issues

* improve size of embed

* add settings

* fix(?) settings

* add source to embed player params

* update tests

* better naming and settings options

* consent modal

* fix test id

* why is webstorm adding .tsx

* web modal

* simplify types

* adjust snap points

* remove unnecessary yt embed library. just use the webview always

* remove now useless WebGifStill 😭

* more type cleanup

* more type cleanup

* combine parse and prefs check in one memo

* improve dimensions of youtube shorts

* oops didn't commit the test 🫥

* add shorts as separate embed type

* fix up schema

* shorts modal

* hide gif details

* support localized spotify embeds

* more cleanup

* improve look and accessibility of gif embeds

* Update routing for the external embeds settings page

* Update and simplify the external embed preferences screen

* Update copy in embedconsent modal and add 'allow all' button

---------

Co-authored-by: Hailey <me@haileyok.com>
Diffstat (limited to 'src/state')
-rw-r--r--src/state/modals/index.tsx8
-rw-r--r--src/state/persisted/legacy.ts1
-rw-r--r--src/state/persisted/schema.ts16
-rw-r--r--src/state/preferences/external-embeds-prefs.tsx54
-rw-r--r--src/state/preferences/index.tsx9
5 files changed, 87 insertions, 1 deletions
diff --git a/src/state/modals/index.tsx b/src/state/modals/index.tsx
index 81a220d1b..8c32c472a 100644
--- a/src/state/modals/index.tsx
+++ b/src/state/modals/index.tsx
@@ -6,6 +6,7 @@ import {Image as RNImage} from 'react-native-image-crop-picker'
 import {ImageModel} from '#/state/models/media/image'
 import {GalleryModel} from '#/state/models/media/gallery'
 import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
+import {EmbedPlayerSource} from '#/lib/strings/embed-player.ts'
 import {ThreadgateSetting} from '../queries/threadgate'
 
 export interface ConfirmModal {
@@ -180,6 +181,12 @@ export interface LinkWarningModal {
   href: string
 }
 
+export interface EmbedConsentModal {
+  name: 'embed-consent'
+  source: EmbedPlayerSource
+  onAccept: () => void
+}
+
 export type Modal =
   // Account
   | AddAppPasswordModal
@@ -223,6 +230,7 @@ export type Modal =
   // Generic
   | ConfirmModal
   | LinkWarningModal
+  | EmbedConsentModal
 
 const ModalContext = React.createContext<{
   isModalActive: boolean
diff --git a/src/state/persisted/legacy.ts b/src/state/persisted/legacy.ts
index cdb542f5a..334ef1d92 100644
--- a/src/state/persisted/legacy.ts
+++ b/src/state/persisted/legacy.ts
@@ -109,6 +109,7 @@ export function transform(legacy: Partial<LegacySchema>): Schema {
       step: legacy.onboarding?.step || defaults.onboarding.step,
     },
     hiddenPosts: defaults.hiddenPosts,
+    externalEmbeds: defaults.externalEmbeds,
   }
 }
 
diff --git a/src/state/persisted/schema.ts b/src/state/persisted/schema.ts
index 27b1f26bd..6a26cedae 100644
--- a/src/state/persisted/schema.ts
+++ b/src/state/persisted/schema.ts
@@ -1,6 +1,8 @@
 import {z} from 'zod'
 import {deviceLocales} from '#/platform/detection'
 
+const externalEmbedOptions = ['show', 'hide'] as const
+
 // only data needed for rendering account page
 const accountSchema = z.object({
   service: z.string(),
@@ -30,6 +32,19 @@ export const schema = z.object({
     appLanguage: z.string(),
   }),
   requireAltTextEnabled: z.boolean(), // should move to server
+  externalEmbeds: z
+    .object({
+      giphy: z.enum(externalEmbedOptions).optional(),
+      tenor: z.enum(externalEmbedOptions).optional(),
+      youtube: z.enum(externalEmbedOptions).optional(),
+      youtubeShorts: z.enum(externalEmbedOptions).optional(),
+      twitch: z.enum(externalEmbedOptions).optional(),
+      vimeo: z.enum(externalEmbedOptions).optional(),
+      spotify: z.enum(externalEmbedOptions).optional(),
+      appleMusic: z.enum(externalEmbedOptions).optional(),
+      soundcloud: z.enum(externalEmbedOptions).optional(),
+    })
+    .optional(),
   mutedThreads: z.array(z.string()), // should move to server
   invites: z.object({
     copiedInvites: z.array(z.string()),
@@ -60,6 +75,7 @@ export const defaults: Schema = {
     appLanguage: deviceLocales[0] || 'en',
   },
   requireAltTextEnabled: false,
+  externalEmbeds: {},
   mutedThreads: [],
   invites: {
     copiedInvites: [],
diff --git a/src/state/preferences/external-embeds-prefs.tsx b/src/state/preferences/external-embeds-prefs.tsx
new file mode 100644
index 000000000..0f6385fe8
--- /dev/null
+++ b/src/state/preferences/external-embeds-prefs.tsx
@@ -0,0 +1,54 @@
+import React from 'react'
+import * as persisted from '#/state/persisted'
+import {EmbedPlayerSource} from 'lib/strings/embed-player'
+
+type StateContext = persisted.Schema['externalEmbeds']
+type SetContext = (source: EmbedPlayerSource, value: 'show' | 'hide') => void
+
+const stateContext = React.createContext<StateContext>(
+  persisted.defaults.externalEmbeds,
+)
+const setContext = React.createContext<SetContext>({} as SetContext)
+
+export function Provider({children}: React.PropsWithChildren<{}>) {
+  const [state, setState] = React.useState(persisted.get('externalEmbeds'))
+
+  const setStateWrapped = React.useCallback(
+    (source: EmbedPlayerSource, value: 'show' | 'hide') => {
+      setState(prev => {
+        persisted.write('externalEmbeds', {
+          ...prev,
+          [source]: value,
+        })
+
+        return {
+          ...prev,
+          [source]: value,
+        }
+      })
+    },
+    [setState],
+  )
+
+  React.useEffect(() => {
+    return persisted.onUpdate(() => {
+      setState(persisted.get('externalEmbeds'))
+    })
+  }, [setStateWrapped])
+
+  return (
+    <stateContext.Provider value={state}>
+      <setContext.Provider value={setStateWrapped}>
+        {children}
+      </setContext.Provider>
+    </stateContext.Provider>
+  )
+}
+
+export function useExternalEmbedsPrefs() {
+  return React.useContext(stateContext)
+}
+
+export function useSetExternalEmbedPref() {
+  return React.useContext(setContext)
+}
diff --git a/src/state/preferences/index.tsx b/src/state/preferences/index.tsx
index 5ec659031..cc2d9244c 100644
--- a/src/state/preferences/index.tsx
+++ b/src/state/preferences/index.tsx
@@ -2,19 +2,26 @@ import React from 'react'
 import {Provider as LanguagesProvider} from './languages'
 import {Provider as AltTextRequiredProvider} from '../preferences/alt-text-required'
 import {Provider as HiddenPostsProvider} from '../preferences/hidden-posts'
+import {Provider as ExternalEmbedsProvider} from './external-embeds-prefs'
 
 export {useLanguagePrefs, useLanguagePrefsApi} from './languages'
 export {
   useRequireAltTextEnabled,
   useSetRequireAltTextEnabled,
 } from './alt-text-required'
+export {
+  useExternalEmbedsPrefs,
+  useSetExternalEmbedPref,
+} from './external-embeds-prefs'
 export * from './hidden-posts'
 
 export function Provider({children}: React.PropsWithChildren<{}>) {
   return (
     <LanguagesProvider>
       <AltTextRequiredProvider>
-        <HiddenPostsProvider>{children}</HiddenPostsProvider>
+        <ExternalEmbedsProvider>
+          <HiddenPostsProvider>{children}</HiddenPostsProvider>
+        </ExternalEmbedsProvider>
       </AltTextRequiredProvider>
     </LanguagesProvider>
   )