Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add dark mode option to in-app embed snippet dialog (#7377)

* add color theme selection

* change chevron direction

* change chevron direction again

* rm unused ref

authored by

Samuel Newman and committed by
GitHub
f5f02aa4 2d854091

+125 -59
+125 -59
src/components/dialogs/Embed.tsx
··· 1 - import React, {memo, useRef, useState} from 'react' 2 - import {TextInput, View} from 'react-native' 1 + import {memo, useEffect, useMemo, useState} from 'react' 2 + import {View} from 'react-native' 3 3 import {AppBskyActorDefs, AppBskyFeedPost, AtUri} from '@atproto/api' 4 4 import {msg, Trans} from '@lingui/macro' 5 5 import {useLingui} from '@lingui/react' ··· 8 8 import {niceDate} from '#/lib/strings/time' 9 9 import {toShareUrl} from '#/lib/strings/url-helpers' 10 10 import {atoms as a, useTheme} from '#/alf' 11 + import {Button, ButtonIcon, ButtonText} from '#/components/Button' 11 12 import * as Dialog from '#/components/Dialog' 12 13 import * as TextField from '#/components/forms/TextField' 13 - import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' 14 - import {CodeBrackets_Stroke2_Corner0_Rounded as CodeBrackets} from '#/components/icons/CodeBrackets' 14 + import * as ToggleButton from '#/components/forms/ToggleButton' 15 + import {Check_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check' 16 + import { 17 + ChevronBottom_Stroke2_Corner0_Rounded as ChevronBottomIcon, 18 + ChevronRight_Stroke2_Corner0_Rounded as ChevronRightIcon, 19 + } from '#/components/icons/Chevron' 20 + import {CodeBrackets_Stroke2_Corner0_Rounded as CodeBracketsIcon} from '#/components/icons/CodeBrackets' 15 21 import {Text} from '#/components/Typography' 16 - import {Button, ButtonIcon, ButtonText} from '../Button' 22 + 23 + export type ColorModeValues = 'system' | 'light' | 'dark' 17 24 18 25 type EmbedDialogProps = { 19 26 control: Dialog.DialogControlProps ··· 44 51 }: Omit<EmbedDialogProps, 'control'>) { 45 52 const t = useTheme() 46 53 const {_, i18n} = useLingui() 47 - const ref = useRef<TextInput>(null) 48 54 const [copied, setCopied] = useState(false) 55 + const [showCustomisation, setShowCustomisation] = useState(false) 56 + const [colorMode, setColorMode] = useState<ColorModeValues>('system') 49 57 50 58 // reset copied state after 2 seconds 51 - React.useEffect(() => { 59 + useEffect(() => { 52 60 if (copied) { 53 61 const timeout = setTimeout(() => { 54 62 setCopied(false) ··· 57 65 } 58 66 }, [copied]) 59 67 60 - const snippet = React.useMemo(() => { 68 + const snippet = useMemo(() => { 61 69 function toEmbedUrl(href: string) { 62 70 return toShareUrl(href) + '?ref_src=embed' 63 71 } ··· 75 83 // x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x 76 84 return `<blockquote class="bluesky-embed" data-bluesky-uri="${escapeHtml( 77 85 postUri, 78 - )}" data-bluesky-cid="${escapeHtml(postCid)}"><p lang="${escapeHtml( 79 - lang, 80 - )}">${escapeHtml(record.text)}${ 86 + )}" data-bluesky-cid="${escapeHtml( 87 + postCid, 88 + )}" data-bluesky-embed-color-mode="${escapeHtml( 89 + colorMode, 90 + )}"><p lang="${escapeHtml(lang)}">${escapeHtml(record.text)}${ 81 91 record.embed 82 92 ? `<br><br><a href="${escapeHtml(href)}">[image or embed]</a>` 83 93 : '' ··· 88 98 )}</a>) <a href="${escapeHtml(href)}">${escapeHtml( 89 99 niceDate(i18n, timestamp), 90 100 )}</a></blockquote><script async src="${EMBED_SCRIPT}" charset="utf-8"></script>` 91 - }, [i18n, postUri, postCid, record, timestamp, postAuthor]) 101 + }, [i18n, postUri, postCid, record, timestamp, postAuthor, colorMode]) 92 102 93 103 return ( 94 - <Dialog.Inner label="Embed post" style={[a.gap_md, {maxWidth: 500}]}> 95 - <View style={[a.gap_sm, a.pb_lg]}> 96 - <Text style={[a.text_2xl, a.font_bold]}> 97 - <Trans>Embed post</Trans> 98 - </Text> 99 - <Text 100 - style={[a.text_md, t.atoms.text_contrast_medium, a.leading_normal]}> 101 - <Trans> 102 - Embed this post in your website. Simply copy the following snippet 103 - and paste it into the HTML code of your website. 104 - </Trans> 105 - </Text> 106 - </View> 107 - 108 - <View style={[a.flex_row, a.gap_sm]}> 109 - <View style={[a.flex_1]}> 110 - <TextField.Root> 111 - <TextField.Icon icon={CodeBrackets} /> 112 - <TextField.Input 113 - label={_(msg`Embed HTML code`)} 114 - editable={false} 115 - selection={{start: 0, end: snippet.length}} 116 - value={snippet} 117 - style={{}} 118 - /> 119 - </TextField.Root> 104 + <Dialog.Inner label="Embed post" style={[{maxWidth: 500}]}> 105 + <View style={[a.gap_lg]}> 106 + <View style={[a.gap_sm]}> 107 + <Text style={[a.text_2xl, a.font_heavy]}> 108 + <Trans>Embed post</Trans> 109 + </Text> 110 + <Text 111 + style={[a.text_md, t.atoms.text_contrast_medium, a.leading_normal]}> 112 + <Trans> 113 + Embed this post in your website. Simply copy the following snippet 114 + and paste it into the HTML code of your website. 115 + </Trans> 116 + </Text> 120 117 </View> 121 - <Button 122 - label={_(msg`Copy code`)} 123 - color="primary" 124 - variant="solid" 125 - size="large" 126 - onPress={() => { 127 - ref.current?.focus() 128 - ref.current?.setSelection(0, snippet.length) 129 - navigator.clipboard.writeText(snippet) 130 - setCopied(true) 131 - }}> 132 - {copied ? ( 133 - <> 134 - <ButtonIcon icon={Check} /> 135 - <ButtonText> 136 - <Trans>Copied!</Trans> 137 - </ButtonText> 138 - </> 139 - ) : ( 118 + <View 119 + style={[ 120 + a.border, 121 + t.atoms.border_contrast_low, 122 + a.rounded_sm, 123 + a.overflow_hidden, 124 + ]}> 125 + <Button 126 + label={ 127 + showCustomisation 128 + ? _(msg`Hide customization options`) 129 + : _(msg`Show customization options`) 130 + } 131 + color="secondary" 132 + variant="ghost" 133 + size="small" 134 + shape="default" 135 + onPress={() => setShowCustomisation(c => !c)} 136 + style={[ 137 + a.justify_start, 138 + showCustomisation && t.atoms.bg_contrast_25, 139 + ]}> 140 + <ButtonIcon 141 + icon={showCustomisation ? ChevronBottomIcon : ChevronRightIcon} 142 + /> 140 143 <ButtonText> 141 - <Trans>Copy code</Trans> 144 + <Trans>Customization options</Trans> 142 145 </ButtonText> 146 + </Button> 147 + 148 + {showCustomisation && ( 149 + <View style={[a.gap_sm, a.p_md]}> 150 + <Text style={[t.atoms.text_contrast_medium, a.font_bold]}> 151 + <Trans>Color theme</Trans> 152 + </Text> 153 + <ToggleButton.Group 154 + label={_(msg`Color mode`)} 155 + values={[colorMode]} 156 + onChange={([value]) => setColorMode(value as ColorModeValues)}> 157 + <ToggleButton.Button name="system" label={_(msg`System`)}> 158 + <ToggleButton.ButtonText> 159 + <Trans>System</Trans> 160 + </ToggleButton.ButtonText> 161 + </ToggleButton.Button> 162 + <ToggleButton.Button name="light" label={_(msg`Light`)}> 163 + <ToggleButton.ButtonText> 164 + <Trans>Light</Trans> 165 + </ToggleButton.ButtonText> 166 + </ToggleButton.Button> 167 + <ToggleButton.Button name="dark" label={_(msg`Dark`)}> 168 + <ToggleButton.ButtonText> 169 + <Trans>Dark</Trans> 170 + </ToggleButton.ButtonText> 171 + </ToggleButton.Button> 172 + </ToggleButton.Group> 173 + </View> 143 174 )} 144 - </Button> 175 + </View> 176 + <View style={[a.flex_row, a.gap_sm]}> 177 + <View style={[a.flex_1]}> 178 + <TextField.Root> 179 + <TextField.Icon icon={CodeBracketsIcon} /> 180 + <TextField.Input 181 + label={_(msg`Embed HTML code`)} 182 + editable={false} 183 + selection={{start: 0, end: snippet.length}} 184 + value={snippet} 185 + /> 186 + </TextField.Root> 187 + </View> 188 + <Button 189 + label={_(msg`Copy code`)} 190 + color="primary" 191 + variant="solid" 192 + size="large" 193 + onPress={() => { 194 + navigator.clipboard.writeText(snippet) 195 + setCopied(true) 196 + }}> 197 + {copied ? ( 198 + <> 199 + <ButtonIcon icon={CheckIcon} /> 200 + <ButtonText> 201 + <Trans>Copied!</Trans> 202 + </ButtonText> 203 + </> 204 + ) : ( 205 + <ButtonText> 206 + <Trans>Copy code</Trans> 207 + </ButtonText> 208 + )} 209 + </Button> 210 + </View> 145 211 </View> 146 212 <Dialog.Close /> 147 213 </Dialog.Inner>