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>
This commit is contained in:
Paul Frazee 2024-01-04 17:37:36 -08:00 committed by GitHub
parent db62f27241
commit 0dae24e78f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 1240 additions and 131 deletions

View file

@ -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

View file

@ -109,6 +109,7 @@ export function transform(legacy: Partial<LegacySchema>): Schema {
step: legacy.onboarding?.step || defaults.onboarding.step,
},
hiddenPosts: defaults.hiddenPosts,
externalEmbeds: defaults.externalEmbeds,
}
}

View file

@ -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: [],

View file

@ -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)
}

View file

@ -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>
)