add completed state
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
|
import {useCallback} from 'react'
|
||||||
|
import {type ListRenderItemInfo, View} from 'react-native'
|
||||||
import * as Contacts from 'expo-contacts'
|
import * as Contacts from 'expo-contacts'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {type ModerationOpts} from '@atproto/api'
|
||||||
|
import {msg, Plural, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {useQuery} from '@tanstack/react-query'
|
import {useQuery} from '@tanstack/react-query'
|
||||||
|
|
||||||
@@ -8,20 +11,31 @@ import {
|
|||||||
type NativeStackScreenProps,
|
type NativeStackScreenProps,
|
||||||
} from '#/lib/routes/types'
|
} from '#/lib/routes/types'
|
||||||
import {isNative} from '#/platform/detection'
|
import {isNative} from '#/platform/detection'
|
||||||
|
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
||||||
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
|
import {useProfilesQuery} from '#/state/queries/profile'
|
||||||
import {ErrorScreen} from '#/view/com/util/error/ErrorScreen'
|
import {ErrorScreen} from '#/view/com/util/error/ErrorScreen'
|
||||||
import {atoms as a, useGutters, useTheme} from '#/alf'
|
import {List} from '#/view/com/util/List'
|
||||||
|
import {atoms as a, tokens, useGutters, useTheme} from '#/alf'
|
||||||
import {Admonition} from '#/components/Admonition'
|
import {Admonition} from '#/components/Admonition'
|
||||||
import {ButtonText} from '#/components/Button'
|
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||||
import {ContactsHeroImage} from '#/components/contacts/components/HeroImage'
|
import {ContactsHeroImage} from '#/components/contacts/components/HeroImage'
|
||||||
|
import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as ResyncIcon} from '#/components/icons/ArrowRotateCounterClockwise'
|
||||||
|
import {Contacts_Stroke2_Corner2_Rounded as FindContactsIcon} from '#/components/icons/Contacts'
|
||||||
|
import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
|
||||||
|
import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash'
|
||||||
import * as Layout from '#/components/Layout'
|
import * as Layout from '#/components/Layout'
|
||||||
import {InlineLinkText, Link} from '#/components/Link'
|
import {InlineLinkText, Link} from '#/components/Link'
|
||||||
|
import * as ProfileCard from '#/components/ProfileCard'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
|
import type * as bsky from '#/types/bsky'
|
||||||
|
import * as SettingsList from './components/SettingsList'
|
||||||
|
|
||||||
type Props = NativeStackScreenProps<AllNavigatorParams, 'FindContactsSettings'>
|
type Props = NativeStackScreenProps<AllNavigatorParams, 'FindContactsSettings'>
|
||||||
export function FindContactsSettingsScreen({}: Props) {
|
export function FindContactsSettingsScreen({}: Props) {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
|
|
||||||
const hasInitiated = false
|
const hasInitiated = true
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.Screen>
|
<Layout.Screen>
|
||||||
@@ -103,5 +117,177 @@ function Intro() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Status() {
|
function Status() {
|
||||||
return null
|
const {data: matches} = useProfilesQuery({
|
||||||
|
handles: ['hailey.at', 'pfrazee.com', 'esb.lol'],
|
||||||
|
})
|
||||||
|
const moderationOpts = useModerationOpts()
|
||||||
|
|
||||||
|
const numMatches = matches?.profiles.length ?? 0
|
||||||
|
|
||||||
|
const renderItem = useCallback(
|
||||||
|
({item, index}: ListRenderItemInfo<bsky.profile.AnyProfileView>) => {
|
||||||
|
if (!moderationOpts) return null
|
||||||
|
return (
|
||||||
|
<MatchItem
|
||||||
|
profile={item}
|
||||||
|
isFirst={index === 0}
|
||||||
|
isLast={index === numMatches - 1}
|
||||||
|
moderationOpts={moderationOpts}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[numMatches, moderationOpts],
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<List
|
||||||
|
data={matches?.profiles ?? []}
|
||||||
|
renderItem={renderItem}
|
||||||
|
ListHeaderComponent={<StatusHeader numMatches={numMatches} />}
|
||||||
|
ListFooterComponent={<StatusFooter />}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function MatchItem({
|
||||||
|
profile,
|
||||||
|
isFirst,
|
||||||
|
isLast,
|
||||||
|
moderationOpts,
|
||||||
|
}: {
|
||||||
|
profile: bsky.profile.AnyProfileView
|
||||||
|
isFirst: boolean
|
||||||
|
isLast: boolean
|
||||||
|
moderationOpts: ModerationOpts
|
||||||
|
}) {
|
||||||
|
const t = useTheme()
|
||||||
|
const {_} = useLingui()
|
||||||
|
const shadow = useProfileShadow(profile)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[a.px_xl]}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.p_md,
|
||||||
|
a.border_t,
|
||||||
|
a.border_x,
|
||||||
|
t.atoms.border_contrast_low,
|
||||||
|
isFirst && [
|
||||||
|
a.curve_continuous,
|
||||||
|
{borderTopLeftRadius: tokens.borderRadius.lg},
|
||||||
|
{borderTopRightRadius: tokens.borderRadius.lg},
|
||||||
|
],
|
||||||
|
isLast && [
|
||||||
|
a.border_b,
|
||||||
|
a.curve_continuous,
|
||||||
|
{borderBottomLeftRadius: tokens.borderRadius.lg},
|
||||||
|
{borderBottomRightRadius: tokens.borderRadius.lg},
|
||||||
|
],
|
||||||
|
]}>
|
||||||
|
<ProfileCard.Header>
|
||||||
|
<ProfileCard.Avatar
|
||||||
|
profile={profile}
|
||||||
|
moderationOpts={moderationOpts}
|
||||||
|
/>
|
||||||
|
<ProfileCard.NameAndHandle
|
||||||
|
profile={profile}
|
||||||
|
moderationOpts={moderationOpts}
|
||||||
|
/>
|
||||||
|
<ProfileCard.FollowButton
|
||||||
|
profile={profile}
|
||||||
|
moderationOpts={moderationOpts}
|
||||||
|
logContext="FindContacts"
|
||||||
|
/>
|
||||||
|
{!shadow.viewer?.following && (
|
||||||
|
<Button
|
||||||
|
color="secondary"
|
||||||
|
variant="ghost"
|
||||||
|
label={_(msg`Remove suggestion`)}
|
||||||
|
// onPress={() => onRemoveSuggestion(profile.did)}
|
||||||
|
hoverStyle={[a.bg_transparent, {opacity: 0.5}]}
|
||||||
|
hitSlop={8}>
|
||||||
|
<ButtonIcon icon={XIcon} />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</ProfileCard.Header>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatusHeader({numMatches}: {numMatches: number}) {
|
||||||
|
const t = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsList.Item
|
||||||
|
style={[a.pt_xl, a.pb_md, numMatches === 0 && a.align_start]}>
|
||||||
|
<SettingsList.ItemIcon icon={FindContactsIcon} />
|
||||||
|
{numMatches > 1 ? (
|
||||||
|
<SettingsList.ItemText>
|
||||||
|
<Plural
|
||||||
|
value={numMatches}
|
||||||
|
one="# contact found"
|
||||||
|
other="# contacts found"
|
||||||
|
/>
|
||||||
|
</SettingsList.ItemText>
|
||||||
|
) : (
|
||||||
|
<View style={[a.flex_1, a.gap_2xs]}>
|
||||||
|
<SettingsList.ItemText>
|
||||||
|
<Trans>Waiting for your friends to join</Trans>
|
||||||
|
</SettingsList.ItemText>
|
||||||
|
<Text
|
||||||
|
style={[a.text_sm, t.atoms.text_contrast_medium, a.leading_snug]}>
|
||||||
|
<Trans>We will notify you when your contacts sign up.</Trans>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</SettingsList.Item>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatusFooter() {
|
||||||
|
const {_, i18n} = useLingui()
|
||||||
|
const t = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[a.px_xl, a.py_xl, a.gap_4xl]}>
|
||||||
|
<View style={[a.gap_sm, a.align_start]}>
|
||||||
|
<Link
|
||||||
|
label={_(msg`Resync contacts`)}
|
||||||
|
to={{screen: 'FindContactsFlow'}}
|
||||||
|
size="small"
|
||||||
|
color="primary_subtle">
|
||||||
|
<ButtonIcon icon={ResyncIcon} />
|
||||||
|
<ButtonText>
|
||||||
|
<Trans>Resync contacts</Trans>
|
||||||
|
</ButtonText>
|
||||||
|
</Link>
|
||||||
|
<Text style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
|
||||||
|
<Trans>
|
||||||
|
Contacts last uploaded on{' '}
|
||||||
|
{i18n.date(new Date(), {
|
||||||
|
dateStyle: 'long',
|
||||||
|
})}
|
||||||
|
</Trans>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={[a.gap_sm, a.align_start]}>
|
||||||
|
<Button
|
||||||
|
label={_(msg`Remove all contacts`)}
|
||||||
|
size="small"
|
||||||
|
color="negative_subtle">
|
||||||
|
<ButtonIcon icon={TrashIcon} />
|
||||||
|
<ButtonText>
|
||||||
|
<Trans>Remove all contacts</Trans>
|
||||||
|
</ButtonText>
|
||||||
|
</Button>
|
||||||
|
<Text style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
|
||||||
|
<Trans>
|
||||||
|
Deleting your contact matching data will remove you from future
|
||||||
|
friend suggestions and immediately delete all non-matching data.
|
||||||
|
</Trans>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user