[Chat] Tweak styles in invite link dialog (#10638)
This commit is contained in:
@@ -212,9 +212,12 @@ export function Item({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const highlightStyle = highlightRow
|
const highlightStyle = highlightRow
|
||||||
? selected
|
? [
|
||||||
? [a.rounded_full, a.p_md, {backgroundColor: t.palette.primary_50}]
|
a.rounded_full,
|
||||||
: [a.rounded_full, a.p_md]
|
a.p_md,
|
||||||
|
hovered && t.atoms.bg_contrast_25,
|
||||||
|
selected && {backgroundColor: t.palette.primary_50},
|
||||||
|
]
|
||||||
: null
|
: null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -588,6 +591,7 @@ export function RadioWithLabel({
|
|||||||
style={[
|
style={[
|
||||||
a.font_medium,
|
a.font_medium,
|
||||||
a.flex_1,
|
a.flex_1,
|
||||||
|
a.text_md,
|
||||||
a.leading_tight,
|
a.leading_tight,
|
||||||
selected ? t.atoms.text : t.atoms.text_contrast_high,
|
selected ? t.atoms.text : t.atoms.text_contrast_high,
|
||||||
]}>
|
]}>
|
||||||
|
|||||||
@@ -35,13 +35,14 @@ export function EditTextButton({
|
|||||||
a.flex_row,
|
a.flex_row,
|
||||||
a.align_center,
|
a.align_center,
|
||||||
a.justify_between,
|
a.justify_between,
|
||||||
a.px_md,
|
a.pl_lg,
|
||||||
|
a.pr_sm,
|
||||||
a.py_sm,
|
a.py_sm,
|
||||||
|
a.gap_sm,
|
||||||
]}>
|
]}>
|
||||||
{typeof children === 'function' ? children(context) : children}
|
{typeof children === 'function' ? children(context) : children}
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.ml_sm,
|
|
||||||
a.rounded_full,
|
a.rounded_full,
|
||||||
t.atoms.bg_contrast_50,
|
t.atoms.bg_contrast_50,
|
||||||
{paddingHorizontal: 10, paddingVertical: 8},
|
{paddingHorizontal: 10, paddingVertical: 8},
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import {useEffect, useState} from 'react'
|
import {useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {moderateProfile, type ModerationOpts} from '@atproto/api'
|
import {
|
||||||
|
type ChatBskyGroupDefs,
|
||||||
|
moderateProfile,
|
||||||
|
type ModerationOpts,
|
||||||
|
} from '@atproto/api'
|
||||||
|
import {plural} from '@lingui/core/macro'
|
||||||
import {Trans, useLingui} from '@lingui/react/macro'
|
import {Trans, useLingui} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||||
@@ -64,14 +69,9 @@ export function InviteLinkDialog({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const {joinLink} = convo.details
|
const {joinLink} = convo.details
|
||||||
const enabledStatus = joinLink?.enabledStatus
|
|
||||||
|
|
||||||
const defaultStep = joinLink ? Step.MANAGE : Step.INFO
|
const defaultStep = joinLink ? Step.MANAGE : Step.INFO
|
||||||
const defaultWhoCanJoin = joinLink
|
const defaultWhoCanJoin = joinLink ? joinLinkToKey(joinLink) : 'anyone'
|
||||||
? [
|
|
||||||
`${joinLink.joinRule}${joinLink.requireApproval ? ':requireApproval' : ''}`,
|
|
||||||
]
|
|
||||||
: ['anyone']
|
|
||||||
|
|
||||||
const [step, setStep] = useState(defaultStep)
|
const [step, setStep] = useState(defaultStep)
|
||||||
const [whoCanJoin, setWhoCanJoin] = useState(defaultWhoCanJoin)
|
const [whoCanJoin, setWhoCanJoin] = useState(defaultWhoCanJoin)
|
||||||
@@ -79,13 +79,13 @@ export function InviteLinkDialog({
|
|||||||
// Resync local state when the server-side join link rules change (mutation
|
// Resync local state when the server-side join link rules change (mutation
|
||||||
// success, refetch, or change from another client). Keyed on the rule string
|
// success, refetch, or change from another client). Keyed on the rule string
|
||||||
// so identity-only refetches don't bump a user mid-edit.
|
// so identity-only refetches don't bump a user mid-edit.
|
||||||
const joinLinkRuleKey = joinLink
|
const joinLinkRuleKey = joinLink ? joinLinkToKey(joinLink) : null
|
||||||
? `${joinLink.joinRule}${joinLink.requireApproval ? ':requireApproval' : ''}`
|
const [prevKey, setPrevKey] = useState(joinLinkRuleKey)
|
||||||
: null
|
if (joinLinkRuleKey !== prevKey) {
|
||||||
useEffect(() => {
|
|
||||||
setStep(joinLinkRuleKey ? Step.MANAGE : Step.INFO)
|
setStep(joinLinkRuleKey ? Step.MANAGE : Step.INFO)
|
||||||
setWhoCanJoin([joinLinkRuleKey ?? 'anyone'])
|
setWhoCanJoin(joinLinkRuleKey ?? 'anyone')
|
||||||
}, [joinLinkRuleKey])
|
setPrevKey(joinLinkRuleKey)
|
||||||
|
}
|
||||||
|
|
||||||
const {openComposer} = useOpenComposer()
|
const {openComposer} = useOpenComposer()
|
||||||
|
|
||||||
@@ -163,22 +163,32 @@ export function InviteLinkDialog({
|
|||||||
let content: React.ReactNode = null
|
let content: React.ReactNode = null
|
||||||
let header: string | null = null
|
let header: string | null = null
|
||||||
switch (step) {
|
switch (step) {
|
||||||
case Step.INFO:
|
case Step.INFO: {
|
||||||
header = l`Invite link`
|
header = l`Invite link`
|
||||||
content = (
|
content = (
|
||||||
<>
|
<>
|
||||||
<View>
|
<View style={[a.gap_lg]}>
|
||||||
<Text style={[a.text_md, t.atoms.text]}>
|
<Text style={[a.text_md, a.leading_snug]}>
|
||||||
<Trans>
|
<Trans>
|
||||||
An invite link lets people join this group chat without being
|
An invite link lets people join this group chat without being
|
||||||
added directly. You control who can use the link and whether
|
added directly. You control who can join the chat. You can
|
||||||
they need your approval. You can disable the link at any time.
|
disable the link at any time.
|
||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={[a.mt_lg, a.text_md, t.atoms.text]}>
|
<Text style={[a.text_md, a.leading_snug]}>
|
||||||
<Trans>
|
<Trans>
|
||||||
Your name, avatar, and the name of the group chat will be
|
Group chats can only have a maximum of{' '}
|
||||||
visible to everyone.
|
{plural(convo.details.memberLimit, {
|
||||||
|
one: '# person',
|
||||||
|
other: '# people',
|
||||||
|
})}
|
||||||
|
.
|
||||||
|
</Trans>
|
||||||
|
</Text>
|
||||||
|
<Text style={[a.text_md, a.leading_snug]}>
|
||||||
|
<Trans>
|
||||||
|
Your name, avatar, the name of the group chat, and the number of
|
||||||
|
members will be visible to everyone.
|
||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -199,15 +209,16 @@ export function InviteLinkDialog({
|
|||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
case Step.GENERATE:
|
}
|
||||||
header =
|
case Step.GENERATE: {
|
||||||
joinLink && enabledStatus === 'enabled'
|
const linkEnabled = joinLink?.enabledStatus === 'enabled'
|
||||||
? l`Update invite link`
|
const linkHasChanged = linkEnabled && joinLinkRuleKey !== whoCanJoin
|
||||||
: l`Generate invite link`
|
|
||||||
|
header = linkEnabled ? l`Update invite link` : l`Generate invite link`
|
||||||
content = (
|
content = (
|
||||||
<>
|
<>
|
||||||
<View>
|
<View>
|
||||||
<Text style={[a.text_md, t.atoms.text]}>
|
<Text style={[a.text_md]}>
|
||||||
<Trans>Choose who can join this group chat and how.</Trans>
|
<Trans>Choose who can join this group chat and how.</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -215,9 +226,9 @@ export function InviteLinkDialog({
|
|||||||
<Toggle.Group
|
<Toggle.Group
|
||||||
label={l`Who can join this group chat and how`}
|
label={l`Who can join this group chat and how`}
|
||||||
type="radio"
|
type="radio"
|
||||||
values={whoCanJoin}
|
values={[whoCanJoin]}
|
||||||
onChange={setWhoCanJoin}>
|
onChange={([value]) => setWhoCanJoin(value)}>
|
||||||
<View style={[a.gap_sm]}>
|
<View style={[a.gap_xs]}>
|
||||||
{whoCanJoinOptions.map(option => (
|
{whoCanJoinOptions.map(option => (
|
||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
key={option.name}
|
key={option.name}
|
||||||
@@ -239,18 +250,22 @@ export function InviteLinkDialog({
|
|||||||
<View style={[a.mt_4xl]}>
|
<View style={[a.mt_4xl]}>
|
||||||
<Button
|
<Button
|
||||||
label={
|
label={
|
||||||
joinLink && enabledStatus === 'enabled'
|
linkEnabled
|
||||||
? l`Update invite link`
|
? linkHasChanged
|
||||||
|
? l`Update invite link`
|
||||||
|
: l`Back`
|
||||||
: l`Generate invite link`
|
: l`Generate invite link`
|
||||||
}
|
}
|
||||||
color="primary"
|
color={linkEnabled && !linkHasChanged ? 'secondary' : 'primary'}
|
||||||
size="large"
|
size="large"
|
||||||
disabled={isSaving}
|
disabled={isSaving}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
const parts = whoCanJoin[0].split(':')
|
const {joinRule, requireApproval} = keyToJoinLink(whoCanJoin)
|
||||||
const joinRule = parts[0]
|
if (linkEnabled) {
|
||||||
const requireApproval = parts[1] === 'requireApproval'
|
if (!linkHasChanged) {
|
||||||
if (joinLink && enabledStatus === 'enabled') {
|
setStep(Step.MANAGE)
|
||||||
|
return
|
||||||
|
}
|
||||||
editJoinLink({
|
editJoinLink({
|
||||||
joinRule,
|
joinRule,
|
||||||
requireApproval,
|
requireApproval,
|
||||||
@@ -263,36 +278,40 @@ export function InviteLinkDialog({
|
|||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
{joinLink && enabledStatus === 'enabled'
|
{linkEnabled
|
||||||
? l`Update invite link`
|
? linkHasChanged
|
||||||
|
? l`Update invite link`
|
||||||
|
: l`Back`
|
||||||
: l`Generate invite link`}
|
: l`Generate invite link`}
|
||||||
</ButtonText>
|
</ButtonText>
|
||||||
<ButtonIcon icon={isSaving ? Loader : ArrowRightIcon} />
|
{linkHasChanged && (
|
||||||
|
<ButtonIcon icon={isSaving ? Loader : ArrowRightIcon} />
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
}
|
||||||
case Step.MANAGE: {
|
case Step.MANAGE: {
|
||||||
|
const linkEnabled = joinLink?.enabledStatus === 'enabled'
|
||||||
|
const linkDisabled = joinLink?.enabledStatus === 'disabled'
|
||||||
const joinLinkURI = joinLink?.code
|
const joinLinkURI = joinLink?.code
|
||||||
? `https://bsky.app/chat/${joinLink.code}`
|
? `https://bsky.app/chat/${joinLink.code}`
|
||||||
: 'https://bsky.app/chat'
|
: 'https://bsky.app/chat'
|
||||||
const createdAt = joinLink ? new Date(joinLink.createdAt) : null
|
const createdAt = joinLink ? new Date(joinLink.createdAt) : null
|
||||||
const currentOptionName = joinLink
|
const currentOption =
|
||||||
? `${joinLink.joinRule}${joinLink.requireApproval ? ':requireApproval' : ''}`
|
whoCanJoinOptions.find(
|
||||||
: whoCanJoinOptions[0].name
|
o => o.name === (joinLink ? joinLinkToKey(joinLink) : null),
|
||||||
const currentOption = whoCanJoinOptions.find(
|
) ?? whoCanJoinOptions[0]
|
||||||
o => o.name === currentOptionName,
|
|
||||||
)
|
|
||||||
const ownerValue = currentOption?.owner ?? whoCanJoinOptions[0].owner
|
const ownerValue = currentOption?.owner ?? whoCanJoinOptions[0].owner
|
||||||
const memberValue = currentOption?.member ?? whoCanJoinOptions[0].member
|
const memberValue = currentOption?.member ?? whoCanJoinOptions[0].member
|
||||||
header =
|
header = linkEnabled ? l`Invite link` : l`Invite link disabled`
|
||||||
enabledStatus === 'enabled' ? l`Invite link` : l`Invite link disabled`
|
|
||||||
content = (
|
content = (
|
||||||
<>
|
<>
|
||||||
<View style={[a.mt_lg]}>
|
<View style={[a.mt_lg]}>
|
||||||
<CopyTextButton
|
<CopyTextButton
|
||||||
disabled={enabledStatus === 'disabled' || !joinLink?.code}
|
disabled={linkDisabled || !joinLink?.code}
|
||||||
label={l`Invite link`}
|
label={l`Invite link`}
|
||||||
value={joinLinkURI}>
|
value={joinLinkURI}>
|
||||||
<Text
|
<Text
|
||||||
@@ -300,9 +319,7 @@ export function InviteLinkDialog({
|
|||||||
style={[
|
style={[
|
||||||
a.mr_xs,
|
a.mr_xs,
|
||||||
a.text_md,
|
a.text_md,
|
||||||
enabledStatus === 'disabled'
|
linkDisabled ? t.atoms.text_contrast_low : t.atoms.text,
|
||||||
? t.atoms.text_contrast_low
|
|
||||||
: t.atoms.text,
|
|
||||||
]}>
|
]}>
|
||||||
{joinLinkURI}
|
{joinLinkURI}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -319,25 +336,25 @@ export function InviteLinkDialog({
|
|||||||
</Text>
|
</Text>
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
{enabledStatus === 'enabled' ? (
|
{linkEnabled ? (
|
||||||
<View style={[a.mt_lg]}>
|
<View style={[a.mt_lg]}>
|
||||||
{isOwner ? (
|
{isOwner ? (
|
||||||
<EditTextButton
|
<EditTextButton
|
||||||
label={l`Edit link settings`}
|
label={l`Edit link settings`}
|
||||||
value={ownerValue}
|
value={ownerValue}
|
||||||
onPress={() => setStep(Step.GENERATE)}>
|
onPress={() => setStep(Step.GENERATE)}>
|
||||||
<View style={[a.flex_1, a.mr_xs]}>
|
<View style={[a.flex_1]}>
|
||||||
<Text numberOfLines={1} style={[a.text_md, t.atoms.text]}>
|
<Text numberOfLines={1} style={[a.text_sm]}>
|
||||||
{ownerValue}
|
{ownerValue}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</EditTextButton>
|
</EditTextButton>
|
||||||
) : (
|
) : (
|
||||||
<Text style={[a.text_sm, t.atoms.text]}>{memberValue}</Text>
|
<Text style={[a.text_sm]}>{memberValue}</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
{enabledStatus === 'enabled' ? (
|
{linkEnabled ? (
|
||||||
<View style={[a.flex_row, a.justify_between, a.gap_sm, a.mt_lg]}>
|
<View style={[a.flex_row, a.justify_between, a.gap_sm, a.mt_lg]}>
|
||||||
{isOwner ? (
|
{isOwner ? (
|
||||||
<StackedButton
|
<StackedButton
|
||||||
@@ -350,7 +367,7 @@ export function InviteLinkDialog({
|
|||||||
</StackedButton>
|
</StackedButton>
|
||||||
) : null}
|
) : null}
|
||||||
<StackedButton
|
<StackedButton
|
||||||
disabled={enabledStatus === 'disabled'}
|
disabled={linkDisabled}
|
||||||
label={l`Post link`}
|
label={l`Post link`}
|
||||||
icon={EditIcon}
|
icon={EditIcon}
|
||||||
color="primary_subtle"
|
color="primary_subtle"
|
||||||
@@ -366,7 +383,7 @@ export function InviteLinkDialog({
|
|||||||
<Trans>Post link</Trans>
|
<Trans>Post link</Trans>
|
||||||
</StackedButton>
|
</StackedButton>
|
||||||
<StackedButton
|
<StackedButton
|
||||||
disabled={enabledStatus === 'disabled'}
|
disabled={linkDisabled}
|
||||||
label={l`Share`}
|
label={l`Share`}
|
||||||
icon={ArrowShareRightIcon}
|
icon={ArrowShareRightIcon}
|
||||||
color="primary_subtle"
|
color="primary_subtle"
|
||||||
@@ -408,7 +425,7 @@ export function InviteLinkDialog({
|
|||||||
)
|
)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case Step.CONFIRM_DISABLE:
|
case Step.CONFIRM_DISABLE: {
|
||||||
content = (
|
content = (
|
||||||
<>
|
<>
|
||||||
<View style={[a.align_center, a.justify_center, a.mb_lg]}>
|
<View style={[a.align_center, a.justify_center, a.mb_lg]}>
|
||||||
@@ -425,14 +442,7 @@ export function InviteLinkDialog({
|
|||||||
]}>
|
]}>
|
||||||
<Trans>Disable this invite link?</Trans>
|
<Trans>Disable this invite link?</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text style={[a.pb_2xl, a.text_center, a.text_sm, a.leading_snug]}>
|
||||||
style={[
|
|
||||||
a.pb_2xl,
|
|
||||||
a.text_center,
|
|
||||||
a.text_sm,
|
|
||||||
a.leading_snug,
|
|
||||||
t.atoms.text,
|
|
||||||
]}>
|
|
||||||
<Trans>
|
<Trans>
|
||||||
Anyone who has it will no longer be able to join or request to
|
Anyone who has it will no longer be able to join or request to
|
||||||
join. You can always create a new one.
|
join. You can always create a new one.
|
||||||
@@ -465,6 +475,7 @@ export function InviteLinkDialog({
|
|||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isOwner && (!joinLink || joinLink.enabledStatus === 'disabled')) {
|
if (!isOwner && (!joinLink || joinLink.enabledStatus === 'disabled')) {
|
||||||
@@ -472,7 +483,7 @@ export function InviteLinkDialog({
|
|||||||
content = (
|
content = (
|
||||||
<>
|
<>
|
||||||
<View style={[a.mt_lg]}>
|
<View style={[a.mt_lg]}>
|
||||||
<Text style={[a.text_sm, t.atoms.text]}>
|
<Text style={[a.text_sm]}>
|
||||||
<Trans>There is no invite link for this group chat.</Trans>
|
<Trans>There is no invite link for this group chat.</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -503,9 +514,7 @@ export function InviteLinkDialog({
|
|||||||
header={
|
header={
|
||||||
<View>
|
<View>
|
||||||
<View style={[IS_WEB ? [a.px_2xl, a.pt_xl] : {paddingTop: 10}]}>
|
<View style={[IS_WEB ? [a.px_2xl, a.pt_xl] : {paddingTop: 10}]}>
|
||||||
<Text style={[a.font_bold, a.text_2xl, a.mb_sm, t.atoms.text]}>
|
<Text style={[a.font_bold, a.text_2xl, a.mb_sm]}>{header}</Text>
|
||||||
{header}
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
<Dialog.Close />
|
<Dialog.Close />
|
||||||
</View>
|
</View>
|
||||||
@@ -517,3 +526,17 @@ export function InviteLinkDialog({
|
|||||||
</Dialog.Outer>
|
</Dialog.Outer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function joinLinkToKey(joinLink: ChatBskyGroupDefs.JoinLinkView): string {
|
||||||
|
return `${joinLink.joinRule}${joinLink.requireApproval ? ':requireApproval' : ''}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyToJoinLink(
|
||||||
|
key: string,
|
||||||
|
): Pick<ChatBskyGroupDefs.JoinLinkView, 'joinRule' | 'requireApproval'> {
|
||||||
|
const [joinRule, requireApproval] = key.split(':')
|
||||||
|
return {
|
||||||
|
joinRule,
|
||||||
|
requireApproval: requireApproval === 'requireApproval',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user