diff --git a/assets/icons/squareBehindSquare4_stroke2_corner0_rounded.svg b/assets/icons/squareBehindSquare4_stroke2_corner0_rounded.svg
new file mode 100644
index 0000000000..3fa7e5d390
--- /dev/null
+++ b/assets/icons/squareBehindSquare4_stroke2_corner0_rounded.svg
@@ -0,0 +1 @@
+
diff --git a/src/components/Link.tsx b/src/components/Link.tsx
index 25f3497014..0cd68d014c 100644
--- a/src/components/Link.tsx
+++ b/src/components/Link.tsx
@@ -171,11 +171,18 @@ export type LinkProps = Omit &
* Intended to behave as a web anchor tag. For more complex routing, use a
* `Button`.
*/
-export function Link({children, to, action = 'push', ...rest}: LinkProps) {
+export function Link({
+ children,
+ to,
+ action = 'push',
+ onPress: outerOnPress,
+ ...rest
+}: LinkProps) {
const {href, isExternal, onPress} = useLink({
to,
displayText: typeof children === 'string' ? children : '',
action,
+ onPress: outerOnPress,
})
return (
@@ -218,6 +225,7 @@ export function InlineLink({
action = 'push',
warnOnMismatchingTextChild,
style,
+ onPress: outerOnPress,
...rest
}: InlineLinkProps) {
const t = useTheme()
@@ -227,6 +235,7 @@ export function InlineLink({
displayText: stringChildren ? children : '',
action,
warnOnMismatchingTextChild,
+ onPress: outerOnPress,
})
const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState()
const {
diff --git a/src/components/ModerationServiceCard/Card.tsx b/src/components/ModerationServiceCard/Card.tsx
index 2dfff4c280..c86b4cdc7b 100644
--- a/src/components/ModerationServiceCard/Card.tsx
+++ b/src/components/ModerationServiceCard/Card.tsx
@@ -7,6 +7,7 @@ import {Text} from '#/components/Typography'
import {RichText} from '#/components/RichText'
import {RaisingHande4Finger_Stroke2_Corner0_Rounded as RaisingHand} from '#/components/icons/RaisingHand'
import {UserAvatar} from '#/view/com/util/UserAvatar'
+import {sanitizeHandle} from '#/lib/strings/handles'
export function Outer({
children,
@@ -43,6 +44,22 @@ export function Avatar({avatar}: {avatar?: string}) {
return
}
+export function Title({value}: {value: string}) {
+ return {value}
+}
+
+export function Description({value, handle}: {value?: string; handle: string}) {
+ return value ? (
+
+ ) : (
+
+
+ Moderation service managed by @{sanitizeHandle(handle, '@')}
+
+
+ )
+}
+
export function Content({
title,
description,
@@ -63,7 +80,9 @@ export function Content({
) : (
- Moderation service managed by @{handle}
+
+ Moderation service managed by @{sanitizeHandle(handle, '@')}
+
)}
diff --git a/src/components/icons/SquareBehindSquare4.tsx b/src/components/icons/SquareBehindSquare4.tsx
new file mode 100644
index 0000000000..425599cbce
--- /dev/null
+++ b/src/components/icons/SquareBehindSquare4.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const SquareBehindSquare4_Stroke2_Corner0_Rounded = createSinglePathSVG({
+ path: 'M8 8V3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1h-5v5a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1h5Zm1 8a1 1 0 0 1-1-1v-5H4v10h10v-4H9Z',
+})
diff --git a/src/screens/Moderation/SettingsDialog.tsx b/src/screens/Moderation/SettingsDialog.tsx
index 11c7596cea..b84882286c 100644
--- a/src/screens/Moderation/SettingsDialog.tsx
+++ b/src/screens/Moderation/SettingsDialog.tsx
@@ -13,6 +13,10 @@ import * as ModerationServiceCard from '#/components/ModerationServiceCard'
import {getModerationServiceTitle} from '#/lib/moderation'
import {UsePreferencesQueryResponse} from '#/state/queries/preferences'
import {useModServiceLabelGroupEnableMutation} from '#/state/queries/modservice'
+import {Divider} from '#/components/Divider'
+import {InlineLink} from '#/components/Link'
+import {useDialogStateControlContext} from '#/state/dialogs'
+import {logger} from '#/logger'
function LabelerToggle({
labelGroup,
@@ -24,14 +28,24 @@ function LabelerToggle({
preferences: UsePreferencesQueryResponse
}) {
const t = useTheme()
- const {mutateAsync, variables} = useModServiceLabelGroupEnableMutation()
+ const {
+ mutateAsync: toggleGroupEnabled,
+ variables: optimisticToggleGroupEnabled,
+ } = useModServiceLabelGroupEnableMutation()
+ const {closeAllDialogs} = useDialogStateControlContext()
- const modservicePreferences = preferences.moderationOpts.mods.find(
- ({did}) => did === labeler.creator.did,
+ const labelerPrefs = React.useMemo(
+ () =>
+ preferences.moderationOpts.mods.find(
+ ({did}) => did === labeler.creator.did,
+ ),
+ [preferences.moderationOpts.mods, labeler.creator.did],
)
- const enabled =
- variables?.enabled ??
- !modservicePreferences?.disabledLabelGroups?.includes(labelGroup)
+ const isLabelerEnabled = !!labelerPrefs?.enabled
+ const isEnabled = isLabelerEnabled
+ ? optimisticToggleGroupEnabled?.enabled ??
+ !labelerPrefs?.disabledLabelGroups?.includes(labelGroup)
+ : false
const title = getModerationServiceTitle({
displayName: labeler.creator.displayName,
handle: labeler.creator.handle,
@@ -39,42 +53,66 @@ function LabelerToggle({
const onToggleEnabled = React.useCallback(async () => {
try {
- await mutateAsync({
- // @ts-ignore TODO
- did: modservicePreferences?.did,
+ if (!labelerPrefs) throw new Error(`labelerPrefs not found`)
+
+ await toggleGroupEnabled({
+ did: labelerPrefs.did,
group: labelGroup,
- enabled: !enabled,
+ enabled: !isEnabled,
})
} catch (e: any) {
- // TODO
- console.error(e)
+ logger.error(`Failed to toggle label group enabled`, {
+ message: e.message,
+ labelGroup,
+ })
}
- }, [mutateAsync, enabled, modservicePreferences, labelGroup])
+ }, [toggleGroupEnabled, isEnabled, labelerPrefs, labelGroup])
+
return (
-
- {ctx => (
-
+
+
+
-
-
- )}
-
+
+
+
+
+
+
+
+ {isEnabled ? 'Enabled' : 'Disabled'}
+
+
+
+
+
+
+
+
+ Configure more settings for this labeler{' '}
+
+ here.
+
+
+
+
)
}
@@ -115,11 +153,10 @@ export function SettingsDialog({
a.p_md,
a.rounded_md,
a.mb_xl,
- a.border,
- t.atoms.border_contrast_low,
+ t.atoms.bg_contrast_25,
]}>
- {groupInfoStrings.name}
+ {groupInfoStrings.name}
{groupInfoStrings.description}
diff --git a/src/screens/Moderation/index.tsx b/src/screens/Moderation/index.tsx
index 620133f1ad..8ed51dd5cb 100644
--- a/src/screens/Moderation/index.tsx
+++ b/src/screens/Moderation/index.tsx
@@ -26,11 +26,12 @@ import {
} from '#/state/queries/preferences'
import {useModServicesDetailedInfoQuery} from '#/state/queries/modservice'
-import {useTheme, atoms as a, useBreakpoints} from '#/alf'
+import {useTheme, atoms as a, useBreakpoints, native} from '#/alf'
import {Divider} from '#/components/Divider'
import {CircleBanSign_Stroke2_Corner0_Rounded as CircleBanSign} from '#/components/icons/CircleBanSign'
import {Group3_Stroke2_Corner0_Rounded as Group} from '#/components/icons/Group'
import {Person_Stroke2_Corner0_Rounded as Person} from '#/components/icons/Person'
+import {SquareBehindSquare4_Stroke2_Corner0_Rounded as SquareBehindSquare} from '#/components/icons/SquareBehindSquare4'
import {Text} from '#/components/Typography'
import * as Toggle from '#/components/forms/Toggle'
import * as ToggleButton from '#/components/forms/ToggleButton'
@@ -38,7 +39,7 @@ import {InlineLink, Link} from '#/components/Link'
import {Loader} from '#/components/Loader'
import {useLabelGroupStrings} from '#/lib/moderation/useLabelGroupStrings'
import * as Dialog from '#/components/Dialog'
-import {Button} from '#/components/Button'
+import {Button, ButtonText, ButtonIcon} from '#/components/Button'
import {
getModerationServiceTitle,
useConfigurableLabelGroups,
@@ -365,79 +366,90 @@ function LabelGroup({
{!!mods.length && (
-
+
+
+ {mods.map(mod => {
+ const modservicePreferences =
+ preferences.moderationOpts.mods.find(
+ ({did}) => did === mod.creator.did,
+ )
+ const labelGroupEnabled =
+ !modservicePreferences?.disabledLabelGroups?.includes(
+ labelGroup,
+ )
+ const isLabelerEnabled = modservicePreferences?.enabled
+ const enabled = labelGroupEnabled && isLabelerEnabled
+ return (
+
+
+
+ {getModerationServiceTitle({
+ displayName: mod.creator.displayName,
+ handle: mod.creator.handle,
+ })}
+
+
+ )
+ })}
+
+
)}
diff --git a/src/screens/ProfileModerationService/Header.tsx b/src/screens/ProfileModerationService/Header.tsx
index f7ba831f02..5ff4dbe014 100644
--- a/src/screens/ProfileModerationService/Header.tsx
+++ b/src/screens/ProfileModerationService/Header.tsx
@@ -19,11 +19,9 @@ import {
} from '#/view/com/util/forms/NativeDropdown'
import {useSession} from '#/state/session'
import {useModalControls} from '#/state/modals'
-import {
- useModServiceSubscriptionMutation,
- useModServiceEnableMutation,
-} from '#/state/queries/modservice'
+import {useModServiceSubscriptionMutation} from '#/state/queries/modservice'
import {UsePreferencesQueryResponse} from '#/state/queries/preferences'
+import {logger} from '#/logger'
import {useTheme, atoms as a, tokens, web, native, useBreakpoints} from '#/alf'
import {Text} from '#/components/Typography'
@@ -54,16 +52,7 @@ export function Header({
preferences.moderationOpts.mods.find(
mod => mod.did === modservice.creator.did,
)
- const {mutateAsync: toggleEnabled, variables: enabledVariables} =
- useModServiceEnableMutation()
- const isEnabled =
- enabledVariables?.enabled ??
- preferences.moderationOpts.mods.find(
- mod => mod.did === modservice.creator.did && mod.enabled,
- ) ??
- variables?.subscribe
const [subscriptionError, setSubscriptionError] = React.useState('')
- const [enablementError, setEnablementError] = React.useState('')
const onPressBack = React.useCallback(() => {
if (navigation.canGoBack()) {
@@ -85,20 +74,10 @@ export function Header({
})
} catch (e: any) {
setSubscriptionError(e.message)
+ logger.error(`Failed to subscribe to labeler`, {message: e.message})
}
}, [toggleSubscription, isSubscribed, modservice.creator.did])
- const onPressEnable = React.useCallback(async () => {
- try {
- await toggleEnabled({
- did: modservice.creator.did,
- enabled: !isEnabled,
- })
- } catch (e: any) {
- setEnablementError(e.message)
- }
- }, [toggleEnabled, isEnabled, modservice.creator.did])
-
return (
{!gtMobile && (
@@ -179,23 +158,6 @@ export function Header({
{gtMobile && (
- {isSubscribed && (
-
- )}
-