diff options
Diffstat (limited to 'src/view/com/modals')
-rw-r--r-- | src/view/com/modals/AltImage.tsx | 147 | ||||
-rw-r--r-- | src/view/com/modals/EmbedConsent.tsx | 153 | ||||
-rw-r--r-- | src/view/com/modals/Modal.tsx | 4 | ||||
-rw-r--r-- | src/view/com/modals/Modal.web.tsx | 3 |
4 files changed, 225 insertions, 82 deletions
diff --git a/src/view/com/modals/AltImage.tsx b/src/view/com/modals/AltImage.tsx index a2e918317..5156511d6 100644 --- a/src/view/com/modals/AltImage.tsx +++ b/src/view/com/modals/AltImage.tsx @@ -1,14 +1,12 @@ import React, {useMemo, useCallback, useState} from 'react' import { ImageStyle, - KeyboardAvoidingView, - ScrollView, StyleSheet, - TextInput, TouchableOpacity, View, useWindowDimensions, } from 'react-native' +import {ScrollView, TextInput} from './util' import {Image} from 'expo-image' import {usePalette} from 'lib/hooks/usePalette' import {gradients, s} from 'lib/styles' @@ -17,13 +15,13 @@ import {MAX_ALT_TEXT} from 'lib/constants' import {useTheme} from 'lib/ThemeContext' import {Text} from '../util/text/Text' import LinearGradient from 'react-native-linear-gradient' -import {isAndroid, isWeb} from 'platform/detection' +import {isWeb} from 'platform/detection' import {ImageModel} from 'state/models/media/image' import {useLingui} from '@lingui/react' import {Trans, msg} from '@lingui/macro' import {useModalControls} from '#/state/modals' -export const snapPoints = ['fullscreen'] +export const snapPoints = ['100%'] interface Props { image: ImageModel @@ -54,102 +52,86 @@ export function Component({image}: Props) { } }, [image, windim]) + const onUpdate = useCallback( + (v: string) => { + v = enforceLen(v, MAX_ALT_TEXT) + setAltText(v) + image.setAltText(v) + }, + [setAltText, image], + ) + const onPressSave = useCallback(() => { image.setAltText(altText) closeModal() }, [closeModal, image, altText]) - const onPressCancel = () => { - closeModal() - } - return ( - <KeyboardAvoidingView - behavior={isAndroid ? 'height' : 'padding'} - style={[pal.view, styles.container]}> - <ScrollView - testID="altTextImageModal" - style={styles.scrollContainer} - keyboardShouldPersistTaps="always" - nativeID="imageAltText"> - <View style={styles.scrollInner}> - <View style={[pal.viewLight, styles.imageContainer]}> - <Image - testID="selectedPhotoImage" - style={imageStyles} - source={{ - uri: image.cropped?.path ?? image.path, - }} - contentFit="contain" - accessible={true} - accessibilityIgnoresInvertColors - /> - </View> - <TextInput - testID="altTextImageInput" - style={[styles.textArea, pal.border, pal.text]} - keyboardAppearance={theme.colorScheme} - multiline - placeholder="Add alt text" - placeholderTextColor={pal.colors.textLight} - value={altText} - onChangeText={text => setAltText(enforceLen(text, MAX_ALT_TEXT))} - accessibilityLabel={_(msg`Image alt text`)} - accessibilityHint="" - accessibilityLabelledBy="imageAltText" - autoFocus + <ScrollView + testID="altTextImageModal" + style={[pal.view, styles.scrollContainer]} + keyboardShouldPersistTaps="always" + nativeID="imageAltText"> + <View style={styles.scrollInner}> + <View style={[pal.viewLight, styles.imageContainer]}> + <Image + testID="selectedPhotoImage" + style={imageStyles} + source={{ + uri: image.cropped?.path ?? image.path, + }} + contentFit="contain" + accessible={true} + accessibilityIgnoresInvertColors /> - <View style={styles.buttonControls}> - <TouchableOpacity - testID="altTextImageSaveBtn" - onPress={onPressSave} - accessibilityLabel={_(msg`Save alt text`)} - accessibilityHint={`Saves alt text, which reads: ${altText}`} - accessibilityRole="button"> - <LinearGradient - colors={[gradients.blueLight.start, gradients.blueLight.end]} - start={{x: 0, y: 0}} - end={{x: 1, y: 1}} - style={[styles.button]}> - <Text type="button-lg" style={[s.white, s.bold]}> - <Trans>Save</Trans> - </Text> - </LinearGradient> - </TouchableOpacity> - <TouchableOpacity - testID="altTextImageCancelBtn" - onPress={onPressCancel} - accessibilityRole="button" - accessibilityLabel={_(msg`Cancel add image alt text`)} - accessibilityHint="" - onAccessibilityEscape={onPressCancel}> - <View style={[styles.button]}> - <Text type="button-lg" style={[pal.textLight]}> - <Trans>Cancel</Trans> - </Text> - </View> - </TouchableOpacity> - </View> </View> - </ScrollView> - </KeyboardAvoidingView> + <TextInput + testID="altTextImageInput" + style={[styles.textArea, pal.border, pal.text]} + keyboardAppearance={theme.colorScheme} + multiline + placeholder={_(msg`Add alt text`)} + placeholderTextColor={pal.colors.textLight} + value={altText} + onChangeText={onUpdate} + accessibilityLabel={_(msg`Image alt text`)} + accessibilityHint="" + accessibilityLabelledBy="imageAltText" + autoFocus + /> + <View style={styles.buttonControls}> + <TouchableOpacity + testID="altTextImageSaveBtn" + onPress={onPressSave} + accessibilityLabel={_(msg`Save alt text`)} + accessibilityHint="" + accessibilityRole="button"> + <LinearGradient + colors={[gradients.blueLight.start, gradients.blueLight.end]} + start={{x: 0, y: 0}} + end={{x: 1, y: 1}} + style={[styles.button]}> + <Text type="button-lg" style={[s.white, s.bold]}> + <Trans>Done</Trans> + </Text> + </LinearGradient> + </TouchableOpacity> + </View> + </View> + </ScrollView> ) } const styles = StyleSheet.create({ - container: { - flex: 1, - height: '100%', - width: '100%', - paddingVertical: isWeb ? 0 : 18, - }, scrollContainer: { flex: 1, height: '100%', paddingHorizontal: isWeb ? 0 : 12, + paddingVertical: isWeb ? 0 : 24, }, scrollInner: { gap: 12, + paddingTop: isWeb ? 0 : 12, }, imageContainer: { borderRadius: 8, @@ -173,5 +155,6 @@ const styles = StyleSheet.create({ }, buttonControls: { gap: 8, + paddingBottom: isWeb ? 0 : 50, }, }) diff --git a/src/view/com/modals/EmbedConsent.tsx b/src/view/com/modals/EmbedConsent.tsx new file mode 100644 index 000000000..04104c52e --- /dev/null +++ b/src/view/com/modals/EmbedConsent.tsx @@ -0,0 +1,153 @@ +import React from 'react' +import {StyleSheet, TouchableOpacity, View} from 'react-native' +import LinearGradient from 'react-native-linear-gradient' +import {s, colors, gradients} from 'lib/styles' +import {Text} from '../util/text/Text' +import {ScrollView} from './util' +import {usePalette} from 'lib/hooks/usePalette' +import { + EmbedPlayerSource, + embedPlayerSources, + externalEmbedLabels, +} from '#/lib/strings/embed-player' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {useModalControls} from '#/state/modals' +import {useSetExternalEmbedPref} from '#/state/preferences/external-embeds-prefs' +import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' + +export const snapPoints = [450] + +export function Component({ + onAccept, + source, +}: { + onAccept: () => void + source: EmbedPlayerSource +}) { + const pal = usePalette('default') + const {closeModal} = useModalControls() + const {_} = useLingui() + const setExternalEmbedPref = useSetExternalEmbedPref() + const {isMobile} = useWebMediaQueries() + + const onShowAllPress = React.useCallback(() => { + for (const key of embedPlayerSources) { + setExternalEmbedPref(key, 'show') + } + onAccept() + closeModal() + }, [closeModal, onAccept, setExternalEmbedPref]) + + const onShowPress = React.useCallback(() => { + setExternalEmbedPref(source, 'show') + onAccept() + closeModal() + }, [closeModal, onAccept, setExternalEmbedPref, source]) + + const onHidePress = React.useCallback(() => { + setExternalEmbedPref(source, 'hide') + closeModal() + }, [closeModal, setExternalEmbedPref, source]) + + return ( + <ScrollView + testID="embedConsentModal" + style={[ + s.flex1, + pal.view, + isMobile + ? {paddingHorizontal: 20, paddingTop: 10} + : {paddingHorizontal: 30}, + ]}> + <Text style={[pal.text, styles.title]}> + <Trans>External Media</Trans> + </Text> + + <Text style={pal.text}> + <Trans> + This content is hosted by {externalEmbedLabels[source]}. Do you want + to enable external media? + </Trans> + </Text> + <View style={[s.mt10]} /> + <Text style={pal.textLight}> + <Trans> + External media may allow websites to collect information about you and + your device. No information is sent or requested until you press the + "play" button. + </Trans> + </Text> + <View style={[s.mt20]} /> + <TouchableOpacity + testID="enableAllBtn" + onPress={onShowAllPress} + accessibilityRole="button" + accessibilityLabel={_( + msg`Show embeds from ${externalEmbedLabels[source]}`, + )} + accessibilityHint="" + onAccessibilityEscape={closeModal}> + <LinearGradient + colors={[gradients.blueLight.start, gradients.blueLight.end]} + start={{x: 0, y: 0}} + end={{x: 1, y: 1}} + style={[styles.btn]}> + <Text style={[s.white, s.bold, s.f18]}> + <Trans>Enable External Media</Trans> + </Text> + </LinearGradient> + </TouchableOpacity> + <View style={[s.mt10]} /> + <TouchableOpacity + testID="enableSourceBtn" + onPress={onShowPress} + accessibilityRole="button" + accessibilityLabel={_( + msg`Never load embeds from ${externalEmbedLabels[source]}`, + )} + accessibilityHint="" + onAccessibilityEscape={closeModal}> + <View style={[styles.btn, pal.btn]}> + <Text style={[pal.text, s.bold, s.f18]}> + <Trans>Enable {externalEmbedLabels[source]} only</Trans> + </Text> + </View> + </TouchableOpacity> + <View style={[s.mt10]} /> + <TouchableOpacity + testID="disableSourceBtn" + onPress={onHidePress} + accessibilityRole="button" + accessibilityLabel={_( + msg`Never load embeds from ${externalEmbedLabels[source]}`, + )} + accessibilityHint="" + onAccessibilityEscape={closeModal}> + <View style={[styles.btn, pal.btn]}> + <Text style={[pal.text, s.bold, s.f18]}> + <Trans>No thanks</Trans> + </Text> + </View> + </TouchableOpacity> + </ScrollView> + ) +} + +const styles = StyleSheet.create({ + title: { + textAlign: 'center', + fontWeight: 'bold', + fontSize: 24, + marginBottom: 12, + }, + btn: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + width: '100%', + borderRadius: 32, + padding: 14, + backgroundColor: colors.gray1, + }, +}) diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx index 2aac20dac..f9d211d07 100644 --- a/src/view/com/modals/Modal.tsx +++ b/src/view/com/modals/Modal.tsx @@ -38,6 +38,7 @@ import * as VerifyEmailModal from './VerifyEmail' import * as ChangeEmailModal from './ChangeEmail' import * as SwitchAccountModal from './SwitchAccount' import * as LinkWarningModal from './LinkWarning' +import * as EmbedConsentModal from './EmbedConsent' const DEFAULT_SNAPPOINTS = ['90%'] const HANDLE_HEIGHT = 24 @@ -176,6 +177,9 @@ export function ModalsContainer() { } else if (activeModal?.name === 'link-warning') { snapPoints = LinkWarningModal.snapPoints element = <LinkWarningModal.Component {...activeModal} /> + } else if (activeModal?.name === 'embed-consent') { + snapPoints = EmbedConsentModal.snapPoints + element = <EmbedConsentModal.Component {...activeModal} /> } else { return null } diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx index 12138f54d..c43a8a6ce 100644 --- a/src/view/com/modals/Modal.web.tsx +++ b/src/view/com/modals/Modal.web.tsx @@ -34,6 +34,7 @@ import * as BirthDateSettingsModal from './BirthDateSettings' import * as VerifyEmailModal from './VerifyEmail' import * as ChangeEmailModal from './ChangeEmail' import * as LinkWarningModal from './LinkWarning' +import * as EmbedConsentModal from './EmbedConsent' export function ModalsContainer() { const {isModalActive, activeModals} = useModals() @@ -129,6 +130,8 @@ function Modal({modal}: {modal: ModalIface}) { element = <ChangeEmailModal.Component /> } else if (modal.name === 'link-warning') { element = <LinkWarningModal.Component {...modal} /> + } else if (modal.name === 'embed-consent') { + element = <EmbedConsentModal.Component {...modal} /> } else { return null } |