Reorg, add icon

This commit is contained in:
Eric Bailey
2024-02-07 16:59:27 -06:00
parent 5998b34471
commit af5b545e25
5 changed files with 374 additions and 78 deletions
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M10.25 4a.75.75 0 0 0-.75.75V11a1 1 0 1 1-2 0V6.75a.75.75 0 0 0-1.5 0V14a6 6 0 0 0 12 0V9a2 2 0 0 0-2 2v1.5a1 1 0 0 1-.684.949l-.628.21A2.469 2.469 0 0 0 13 16a1 1 0 1 1-2 0 4.469 4.469 0 0 1 3-4.22V11c0-.703.181-1.364.5-1.938V5.75a.75.75 0 0 0-1.5 0V9a1 1 0 1 1-2 0V4.75a.75.75 0 0 0-.75-.75Zm2.316-.733A2.75 2.75 0 0 1 16.5 5.75v1.54c.463-.187.97-.29 1.5-.29h1a1 1 0 0 1 1 1v6a8 8 0 1 1-16 0V6.75a2.75 2.75 0 0 1 3.571-2.625 2.751 2.751 0 0 1 4.995-.858Z" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 599 B

+5
View File
@@ -0,0 +1,5 @@
import {createSinglePathSVG} from './TEMPLATE'
export const RaisingHande4Finger_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M10.25 4a.75.75 0 0 0-.75.75V11a1 1 0 1 1-2 0V6.75a.75.75 0 0 0-1.5 0V14a6 6 0 0 0 12 0V9a2 2 0 0 0-2 2v1.5a1 1 0 0 1-.684.949l-.628.21A2.469 2.469 0 0 0 13 16a1 1 0 1 1-2 0 4.469 4.469 0 0 1 3-4.22V11c0-.703.181-1.364.5-1.938V5.75a.75.75 0 0 0-1.5 0V9a1 1 0 1 1-2 0V4.75a.75.75 0 0 0-.75-.75Zm2.316-.733A2.75 2.75 0 0 1 16.5 5.75v1.54c.463-.187.97-.29 1.5-.29h1a1 1 0 0 1 1 1v6a8 8 0 1 1-16 0V6.75a2.75 2.75 0 0 1 3.571-2.625 2.751 2.751 0 0 1 4.995-.858Z',
})
@@ -0,0 +1,72 @@
import React from 'react'
import {View} from 'react-native'
import {Trans, msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native'
import {useTheme, atoms as a} from '#/alf'
import {Text} from '#/components/Typography'
import {Button, ButtonText} from '#/components/Button'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
import {NavigationProp} from '#/lib/routes/types'
export function ErrorState({error}: {error: string}) {
const t = useTheme()
const {_} = useLingui()
const navigation = useNavigation<NavigationProp>()
const onPressBack = React.useCallback(() => {
if (navigation.canGoBack()) {
navigation.goBack()
} else {
navigation.navigate('Home')
}
}, [navigation])
return (
<>
<CircleInfo width={48} style={[t.atoms.text_contrast_400]} />
<Text style={[a.text_xl, a.font_bold, a.pb_md, a.pt_xl]}>
<Trans>Hmmmm, we couldn't load that moderation service.</Trans>
</Text>
<Text
style={[
a.text_md,
a.leading_normal,
a.pb_md,
t.atoms.text_contrast_700,
]}>
<Trans>
This moderation service is unavailable. See below for more details. If
this issue persists, contact us.
</Trans>
</Text>
<View
style={[
a.relative,
a.py_md,
a.px_lg,
a.rounded_md,
a.mb_2xl,
t.atoms.bg_contrast_25,
]}>
<Text style={[a.text_md, a.leading_normal]}>{error}</Text>
</View>
<View style={{flexDirection: 'row'}}>
<Button
size="small"
color="secondary"
variant="solid"
label={_(msg`Go Back`)}
accessibilityHint="Return to previous page"
onPress={onPressBack}>
<ButtonText>
<Trans>Go Back</Trans>
</ButtonText>
</Button>
</View>
</>
)
}
@@ -0,0 +1,232 @@
import React from 'react'
import {Pressable, StyleSheet, View} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {useNavigation} from '@react-navigation/native'
import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {Text as OldText} from '#/view/com/util/text/Text'
import {TextLink} from '#/view/com/util/Link'
import {CenteredView} from '#/view/com/util/Views'
import {sanitizeHandle} from 'lib/strings/handles'
import {makeProfileLink} from 'lib/routes/links'
import {NavigationProp} from 'lib/routes/types'
import {BACK_HITSLOP} from 'lib/constants'
import {isNative} from 'platform/detection'
import {useLingui} from '@lingui/react'
import {Trans, msg} from '@lingui/macro'
import {useSetDrawerOpen} from '#/state/shell'
import {emitSoftReset} from '#/state/events'
import {AppBskyModerationDefs} from '@atproto/api'
import {HandIcon} from '#/lib/icons'
import {shareUrl} from 'lib/sharing'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {Button as OldButton} from '#/view/com/util/forms/Button'
import {NativeDropdown, DropdownItem} from 'view/com/util/forms/NativeDropdown'
import {useSession} from '#/state/session'
import {useModalControls} from '#/state/modals'
import { useTheme, atoms as a } from '#/alf'
import {Text} from '#/components/Typography'
import {Button, ButtonText} from '#/components/Button'
import {RaisingHande4Finger_Stroke2_Corner0_Rounded as RaisingHand} from '#/components/icons/RaisingHand'
export function Header({
info,
}: {
info: AppBskyModerationDefs.ModServiceViewDetailed
}) {
const setDrawerOpen = useSetDrawerOpen()
const navigation = useNavigation<NavigationProp>()
const {_} = useLingui()
const {isMobile} = useWebMediaQueries()
const pal = usePalette('default')
const canGoBack = navigation.canGoBack()
const onPressBack = React.useCallback(() => {
if (navigation.canGoBack()) {
navigation.goBack()
} else {
navigation.navigate('Home')
}
}, [navigation])
const onPressMenu = React.useCallback(() => {
setDrawerOpen(true)
}, [setDrawerOpen])
return (
<CenteredView style={pal.view}>
{isMobile && (
<View
style={[
{
flexDirection: 'row',
alignItems: 'center',
borderBottomWidth: 1,
paddingTop: isNative ? 0 : 8,
paddingBottom: 8,
paddingHorizontal: isMobile ? 12 : 14,
},
pal.border,
]}>
<Pressable
testID="headerDrawerBtn"
onPress={canGoBack ? onPressBack : onPressMenu}
hitSlop={BACK_HITSLOP}
style={canGoBack ? styles.backBtn : styles.backBtnWide}
accessibilityRole="button"
accessibilityLabel={canGoBack ? 'Back' : 'Menu'}
accessibilityHint="">
{canGoBack ? (
<FontAwesomeIcon
size={18}
icon="angle-left"
style={[styles.backIcon, pal.text]}
/>
) : (
<FontAwesomeIcon
size={18}
icon="bars"
style={[styles.backIcon, pal.textLight]}
/>
)}
</Pressable>
<View style={{flex: 1}} />
<CommonControls info={info} />
</View>
)}
<View
style={{
flexDirection: 'row',
alignItems: 'flex-start',
gap: 10,
paddingTop: 14,
paddingBottom: 14,
paddingHorizontal: isMobile ? 12 : 14,
}}>
<View style={{alignSelf: 'center'}}>
<HandIcon style={pal.text} size={32} strokeWidth={5.5} />
</View>
<View style={{flex: 1}}>
<TextLink
testID="headerTitle"
type="title-xl"
href={makeProfileLink(info.creator, 'modservice')}
style={[pal.text, {fontWeight: 'bold'}]}
text={
info.creator.displayName
? sanitizeDisplayName(info.creator.displayName)
: sanitizeHandle(info.creator.handle, '@')
}
onPress={emitSoftReset}
numberOfLines={4}
/>
<OldText type="xl" style={[pal.textLight]} numberOfLines={1}>
<Trans>Moderation service</Trans>
</OldText>
</View>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
}}>
<OldButton type="primary" label={_(msg`Subscribe`)} />
{!isMobile && <CommonControls info={info} />}
</View>
</View>
</CenteredView>
)
}
function CommonControls({
info,
}: {
info: AppBskyModerationDefs.ModServiceViewDetailed
}) {
const pal = usePalette('default')
const {hasSession} = useSession()
const {openModal} = useModalControls()
const {_} = useLingui()
const onPressShare = React.useCallback(() => {
const url = makeProfileLink(info.creator, 'modservice')
shareUrl(url)
// track('CustomFeed:Share') TODO
}, [info /*, track*/])
const onPressReport = React.useCallback(() => {
if (!info) return
openModal({
name: 'report',
uri: info.uri,
cid: info.cid,
})
}, [openModal, info])
const dropdownItems: DropdownItem[] = React.useMemo(() => {
return [
hasSession && {
testID: 'modHeaderDropdownReportBtn',
label: _(msg`Report mod service`),
onPress: onPressReport,
icon: {
ios: {
name: 'exclamationmark.triangle',
},
android: 'ic_menu_report_image',
web: 'circle-exclamation',
},
},
{
testID: 'modHeaderDropdownShareBtn',
label: _(msg`Share mod service`),
onPress: onPressShare,
icon: {
ios: {
name: 'square.and.arrow.up',
},
android: 'ic_menu_share',
web: 'share',
},
},
].filter(Boolean) as DropdownItem[]
}, [hasSession, onPressReport, onPressShare, _])
return (
<>
<NativeDropdown
testID="headerDropdownBtn"
items={dropdownItems}
accessibilityLabel={_(msg`More options`)}
accessibilityHint="">
<View style={[pal.viewLight, styles.btn, {marginLeft: 6}]}>
<FontAwesomeIcon icon="ellipsis" size={20} color={pal.colors.text} />
</View>
</NativeDropdown>
</>
)
}
const styles = StyleSheet.create({
backBtn: {
width: 20,
height: 30,
},
backBtnWide: {
width: 20,
height: 30,
paddingHorizontal: 6,
},
backIcon: {
marginTop: 6,
},
btn: {
flexDirection: 'row',
alignItems: 'center',
gap: 6,
paddingVertical: 7,
paddingHorizontal: 14,
borderRadius: 50,
},
})
+64 -78
View File
@@ -36,104 +36,90 @@ import {
} from '#/state/queries/preferences'
import {useSession} from '#/state/session'
import {useLikeMutation, useUnlikeMutation} from '#/state/queries/like'
import {ModServiceHeader} from '#/view/com/moderation/ModServiceHeader'
import {sanitizeHandle} from '#/lib/strings/handles'
import { useTheme, atoms as a } from '#/alf'
import {useTheme, atoms as a} from '#/alf'
import {Text} from '#/components/Typography'
import {Loader} from '#/components/Loader'
import {Button, ButtonText} from '#/components/Button'
import { CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
import {ErrorState} from '#/screens/ProfileModerationService/ErrorState'
import {Header} from '#/screens/ProfileModerationService/Header'
// TODO
const MIN_HEIGHT = Dimensions.get('window').height * 1.5
// TODO loader default color
type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileModservice'>
export function ProfileModserviceScreen(props: Props) {
export function ProfileModserviceScreen(
props: NativeStackScreenProps<CommonNavigatorParams, 'ProfileModservice'>,
) {
const t = useTheme()
const {name: handleOrDid} = props.route.params
const pal = usePalette('default')
const {_} = useLingui()
const navigation = useNavigation<NavigationProp>()
const {name: handleOrDid} = props.route.params
const {isLoading, error, data: resolvedDid} = useResolveDidQuery(handleOrDid)
const {error, data: resolvedDid} = useResolveDidQuery(handleOrDid)
const onPressBack = React.useCallback(() => {
if (navigation.canGoBack()) {
navigation.goBack()
} else {
navigation.navigate('Home')
}
}, [navigation])
if (error) {
return (
<CenteredView>
<View style={[a.pt_2xl, a.px_xl, a.border_l, a.border_r, t.atoms.border, {
minHeight: MIN_HEIGHT,
}]}>
<CircleInfo
width={48}
style={[t.atoms.text_contrast_400]}
/>
<Text style={[a.text_xl, a.font_bold, a.pb_md, a.pt_xl]}>
<Trans>Hmmmm, we couldn't load that moderation service.</Trans>
</Text>
<Text style={[a.text_md, a.leading_normal, a.pb_md, t.atoms.text_contrast_700]}>
<Trans>This moderation service is unavailable. See below for more details. If this issue persists, contact us.</Trans>
</Text>
<View style={[a.relative, a.py_md, a.px_lg, a.rounded_md, a.mb_2xl, t.atoms.bg_contrast_25]}>
<Text style={[a.text_md, a.leading_normal]}>
Error: {error.toString()}
</Text>
</View>
<View style={{flexDirection: 'row'}}>
<Button
size='small'
color='secondary'
variant='solid'
label={_(msg`Go Back`)}
accessibilityHint="Return to previous page"
onPress={onPressBack}>
<ButtonText>
<Trans>Go Back</Trans>
</ButtonText>
</Button>
</View>
</View>
</CenteredView>
)
}
return resolvedDid ? (
<ProfileModservicecreenIntermediate modDid={resolvedDid} />
) : (
return (
<CenteredView>
<View style={s.p20}>
<ActivityIndicator size="large" />
<View
style={[
a.pt_2xl,
a.px_xl,
a.border_l,
a.border_r,
t.atoms.border,
{
minHeight: MIN_HEIGHT,
},
]}>
{isLoading ? (
<View style={[a.w_full, a.align_center]}>
<Loader size="xl" fill={t.atoms.text.color} />
</View>
) : resolvedDid ? (
<ProfileModservicecreenIntermediate modDid={resolvedDid} />
) : (
<ErrorState
error={
error?.toString() ||
_(msg`Something went wrong, please try again.`)
}
/>
)}
</View>
</CenteredView>
)
}
function ProfileModservicecreenIntermediate({modDid}: {modDid: string}) {
const {data: preferences} = usePreferencesQuery()
const {data: info} = useModServiceInfoQuery({did: modDid})
const t = useTheme()
const {_} = useLingui()
const {
isLoading: isPreferencesLoading,
error: preferencesError,
data: preferences,
} = usePreferencesQuery()
const {
isLoading: isModServiceLoading,
error: modServiceError,
data: info,
} = useModServiceInfoQuery({did: modDid})
if (!preferences || !info) {
return (
<CenteredView>
<View style={s.p20}>
<ActivityIndicator size="large" />
</View>
</CenteredView>
)
}
const isLoading = isPreferencesLoading || isModServiceLoading
const error = preferencesError || modServiceError
return (
return isLoading ? (
<View style={[a.w_full, a.align_center]}>
<Loader size="xl" fill={t.atoms.text.color} />
</View>
) : preferences && info ? (
<ProfileModserviceScreenInner preferences={preferences} modInfo={info} />
) : (
<ErrorState
error={
error?.toString() || _(msg`Something went wrong, please try again.`)
}
/>
)
}
@@ -211,7 +197,7 @@ export function ProfileModserviceScreenInner({
contentContainerStyle={{
minHeight: Dimensions.get('window').height * 1.5,
}}>
<ModServiceHeader info={modInfo} />
<Header info={modInfo} />
<View
style={[
{