adding block and report topic

This commit is contained in:
balzack 2024-12-22 13:43:47 -08:00
parent 14edc64ca4
commit 14ff380520
5 changed files with 165 additions and 76 deletions

View File

@ -238,6 +238,11 @@ export const en = {
blockMessage: 'Block Message',
blockMessagePrompt: 'Are you sure you want to block the message?',
blockTopic: 'Blocking Topic',
blockTopicPrompt: 'Are you sure you want to block the topic?',
reportTopic: 'Flag Topic',
reportTopicPrompt: 'Are you sure you want to flag the topic for admin review?',
ignoring: 'Ignoring Contact',
connfirmIgnoring: 'Are you sure you want to ignore the request?',
denying: 'Denying Contact',
@ -283,6 +288,10 @@ export const fr = {
server: 'Serveur',
token: 'Code',
delayMessage: 'La génération de clé peut prendre plusieurs minutes.',
blockTopic: 'Bloquer le sujet',
blockTopicPrompt: 'Êtes-vous sûr de vouloir bloquer ce sujet ?',
reportTopic: 'Signaler le sujet',
reportTopicPrompt: 'Êtes-vous sûr de vouloir signaler ce sujet pour examen par un administrateur ?',
membership: 'Adhésion',
channelHost: 'Hôte du Sujet',
@ -555,6 +564,10 @@ export const sp = {
server: 'Server',
token: 'Código',
delayMessage: 'La generación de claves puede tardar varios minutos.',
blockTopic: 'Bloquear tema',
blockTopicPrompt: '¿Estás seguro de que deseas bloquear este tema?',
reportTopic: 'Reportar tema',
reportTopicPrompt: '¿Estás seguro de que deseas reportar este tema para revisión del administrador?',
membership: 'Afiliación',
channelHost: 'Anfitrión del Tema',
@ -826,6 +839,10 @@ export const pt = {
server: 'Servidor',
token: 'Code',
delayMessage: 'A geração da chave pode levar vários minutos.',
blockTopic: 'Bloquear tópico',
blockTopicPrompt: 'Tem certeza de que deseja bloquear este tópico?',
reportTopic: 'Denunciar tópico',
reportTopicPrompt: 'Tem certeza de que deseja denunciar este tópico para revisão do administrador?',
membership: 'Associação',
channelHost: 'Anfitrião do Tópico',
@ -1098,6 +1115,10 @@ export const de = {
token: 'Token',
delayMessage: 'Die Schlüsselgenerierung kann mehrere Minuten dauern.',
membership: 'Mitgliedschaft',
blockTopic: 'Thema blockieren',
blockTopicPrompt: 'Sind Sie sicher, dass Sie dieses Thema blockieren möchten?',
reportTopic: 'Thema melden',
reportTopicPrompt: 'Sind Sie sicher, dass Sie dieses Thema zur Überprüfung durch den Administrator melden möchten?',
channelHost: 'Themenhost',
channelGuest: 'Thema Gast',
@ -1369,6 +1390,10 @@ export const ru = {
token: 'Токен',
delayMessage: 'Генерация ключа может занять несколько минут.',
created: 'Созданный',
blockTopic: 'Заблокировать тему',
blockTopicPrompt: 'Вы уверены, что хотите заблокировать эту тему?',
reportTopic: 'Пожаловаться на тему',
reportTopicPrompt: 'Вы уверены, что хотите отправить эту тему на рассмотрение администратору?',
membership: 'Членство',
channelHost: 'Ведущий темы',

View File

@ -200,7 +200,8 @@ export function useContent() {
cardChannels.current.forEach((values) => {
merged.push(...values)
})
const sorted = merged.sort((a, b) => {
const filtered = merged.filter(channel => !channel.blocked);
const sorted = filtered.sort((a, b) => {
const aUpdated = a?.lastTopic?.created
const bUpdated = b?.lastTopic?.created
if (aUpdated == bUpdated) {

View File

@ -10,6 +10,8 @@ export function Details({ close }: { close: () => void }) {
const { state, actions } = useDetails()
const [saving, setSaving] = useState(false);
const [removing, setRemoving] = useState(false);
const [blocking, setBlocking] = useState(false);
const [reporting, setReporting] = useState(false);
const undo = () => {
actions.undoSubject();
@ -80,6 +82,58 @@ export function Details({ close }: { close: () => void }) {
})
}
const block = async () => {
modals.openConfirmModal({
title: state.strings.blockTopic,
withCloseButton: false,
overlayProps: {
backgroundOpacity: 0.55,
blur: 3,
},
children: <Text>{ state.strings.blockTopicPrompt }</Text>,
labels: { confirm: state.strings.block, cancel: state.strings.cancel },
onConfirm: async () => {
if (!removing) {
setBlocking(true);
try {
await actions.block();
close();
} catch (err) {
console.log(err);
showError();
}
setBlocking(false);
}
}
})
}
const report = async () => {
modals.openConfirmModal({
title: state.strings.reportTopic,
withCloseButton: false,
overlayProps: {
backgroundOpacity: 0.55,
blur: 3,
},
children: <Text>{ state.strings.reportTopicPrompt }</Text>,
labels: { confirm: state.strings.report, cancel: state.strings.cancel },
onConfirm: async () => {
if (!removing) {
setReporting(true);
try {
await actions.report();
close();
} catch (err) {
console.log(err);
showError();
}
setReporting(false);
}
}
})
}
const showError = () => {
modals.openConfirmModal({
title: state.strings.operationFailed,
@ -112,7 +166,7 @@ export function Details({ close }: { close: () => void }) {
{ state.host && (
<div className={classes.subject}>
<div className={classes.subjectLabel}>
<TextInput size="lg" placeholder={state.strings.subject} value={state.editSubject} onChange={(event) => actions.setEditSubject(event.currentTarget.value)}
<TextInput size={32} placeholder={state.strings.subject} value={state.editSubject} onChange={(event) => actions.setEditSubject(event.currentTarget.value)}
leftSectionPointerEvents="none" leftSection={<IconLabel />}
rightSectionPointerEvents="all" rightSectionWidth={64} rightSection={
<div className={classes.subjectControls}>
@ -172,20 +226,20 @@ export function Details({ close }: { close: () => void }) {
{ !state.host && (
<div className={classes.actions}>
<div className={classes.action}>
<ActionIcon variant="subtle" size="lg" loading={removing} onClick={leave}>
<IconLogout2 size="lg" />
<ActionIcon variant="subtle" size={32} loading={removing} onClick={leave}>
<IconLogout2 size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.leave}</Text>
</div>
<div className={classes.action}>
<ActionIcon variant="subtle" size="lg">
<IconEyeOff size="lg" />
<ActionIcon variant="subtle" size={32} loading={blocking} onClick={block}>
<IconEyeOff size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.block}</Text>
</div>
<div className={classes.action}>
<ActionIcon variant="subtle" size="lg">
<IconAlertHexagon size="lg" />
<ActionIcon variant="subtle" size={32} loading={reporting} onClick={report}>
<IconAlertHexagon size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.report}</Text>
</div>
@ -194,14 +248,14 @@ export function Details({ close }: { close: () => void }) {
{ state.host && (
<div className={classes.actions}>
<div className={classes.action}>
<ActionIcon variant="subtle" size="lg" loading={removing} onClick={remove}>
<IconMessageX size="lg" />
<ActionIcon variant="subtle" size={32} loading={removing} onClick={remove}>
<IconMessageX size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.remove}</Text>
</div>
<div className={classes.action}>
<ActionIcon variant="subtle" size="lg" >
<IconUserCog size="lg" />
<ActionIcon variant="subtle" size={32} >
<IconUserCog size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.members}</Text>
</div>

View File

@ -126,6 +126,15 @@ export function useDetails() {
await content.leaveChannel(state.cardId, state.channelId);
app.actions.clearFocus();
},
block: async () => {
const content = app.state.session.getContent();
await content.setBlockedChannel(state.cardId, state.channelId, true);
app.actions.clearFocus();
},
report: async () => {
const content = app.state.session.getContent();
await content.flagChannel(state.cardId, state.channelId);
},
setEditSubject: (editSubject: string) => {
updateState({ editSubject });
},

View File

@ -270,14 +270,14 @@ export function Profile({ params, close }: { params: ProfileParams; close?: () =
{state.statusLabel === 'unknownStatus' && (
<div className={classes.actions}>
<div className={classes.action} onClick={applySave}>
<ActionIcon variant="subtle" loading={saving} size="lg">
<IconDeviceFloppy size="lg" />
<ActionIcon variant="subtle" loading={saving} size={32}>
<IconDeviceFloppy size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.save}</Text>
</div>
<div className={classes.action} onClick={confirmReport}>
<ActionIcon variant="subtle" loading={reporting} size="lg">
<IconAlertHexagon size="lg" />
<ActionIcon variant="subtle" loading={reporting} size={32}>
<IconAlertHexagon size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.report}</Text>
</div>
@ -286,26 +286,26 @@ export function Profile({ params, close }: { params: ProfileParams; close?: () =
{state.statusLabel === 'savedStatus' && (
<div className={classes.actions}>
<div className={classes.action} onClick={applyConnect}>
<ActionIcon variant="subtle" loading={connecting} size="lg">
<IconRoute2 size="lg" />
<ActionIcon variant="subtle" loading={connecting} size={32}>
<IconRoute2 size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.connect}</Text>
</div>
<div className={classes.action} onClick={confirmRemove}>
<ActionIcon variant="subtle" loading={removing} size="lg">
<IconUserX size="lg" />
<ActionIcon variant="subtle" loading={removing} size={32}>
<IconUserX size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.remove}</Text>
</div>
<div className={classes.action} onClick={confirmBlock}>
<ActionIcon variant="subtle" loading={blocking} size="lg">
<IconEyeOff size="lg" />
<ActionIcon variant="subtle" loading={blocking} size={32}>
<IconEyeOff size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.block}</Text>
</div>
<div className={classes.action} onClick={confirmReport}>
<ActionIcon variant="subtle" loading={reporting} size="lg">
<IconAlertHexagon size="lg" />
<ActionIcon variant="subtle" loading={reporting} size={32}>
<IconAlertHexagon size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.report}</Text>
</div>
@ -314,44 +314,44 @@ export function Profile({ params, close }: { params: ProfileParams; close?: () =
{state.statusLabel === 'pendingStatus' && (
<div className={classes.actions}>
<div className={classes.action} onClick={applyConfirm}>
<ActionIcon variant="subtle" loading={confirming} size="lg">
<IconDeviceFloppy size="lg" />
<ActionIcon variant="subtle" loading={confirming} size={32}>
<IconDeviceFloppy size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.save}</Text>
</div>
<div className={classes.action} onClick={applyAccept}>
<ActionIcon variant="subtle" loading={accepting} size="lg">
<IconUserCheck size="lg" />
<ActionIcon variant="subtle" loading={accepting} size={32}>
<IconUserCheck size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.accept}</Text>
</div>
<div className={classes.action} onClick={confirmIgnore}>
<ActionIcon variant="subtle" loading={ignoring} size="lg">
<IconVolumeOff size="lg" />
<ActionIcon variant="subtle" loading={ignoring} size={32}>
<IconVolumeOff size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.ignore}</Text>
</div>
<div className={classes.action} onClick={confirmDeny}>
<ActionIcon variant="subtle" loading={denying} size="lg">
<IconArrowsCross size="lg" />
<ActionIcon variant="subtle" loading={denying} size={32}>
<IconArrowsCross size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.deny}</Text>
</div>
<div className={classes.action} onClick={confirmRemove}>
<ActionIcon variant="subtle" loading={removing} size="lg">
<IconUserX size="lg" />
<ActionIcon variant="subtle" loading={removing} size={32}>
<IconUserX size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.remove}</Text>
</div>
<div className={classes.action} onClick={confirmBlock}>
<ActionIcon variant="subtle" loading={blocking} size="lg">
<IconEyeOff size="lg" />
<ActionIcon variant="subtle" loading={blocking} size={32}>
<IconEyeOff size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.block}</Text>
</div>
<div className={classes.action} onClick={confirmReport}>
<ActionIcon variant="subtle" loading={reporting} size="lg">
<IconAlertHexagon size="lg" />
<ActionIcon variant="subtle" loading={reporting} size={32}>
<IconAlertHexagon size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.report}</Text>
</div>
@ -360,38 +360,38 @@ export function Profile({ params, close }: { params: ProfileParams; close?: () =
{state.statusLabel === 'requestedStatus' && (
<div className={classes.actions} onClick={applyAccept}>
<div className={classes.action}>
<ActionIcon variant="subtle" loading={accepting} size="lg">
<IconUserCheck size="lg" />
<ActionIcon variant="subtle" loading={accepting} size={32}>
<IconUserCheck size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.accept}</Text>
</div>
<div className={classes.action} onClick={confirmIgnore}>
<ActionIcon variant="subtle" loading={ignoring} size="lg">
<IconVolumeOff size="lg" />
<ActionIcon variant="subtle" loading={ignoring} size={32}>
<IconVolumeOff size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.ignore}</Text>
</div>
<div className={classes.action} onClick={confirmDeny}>
<ActionIcon variant="subtle" loading={denying} size="lg">
<IconArrowsCross size="lg" />
<ActionIcon variant="subtle" loading={denying} size={32}>
<IconArrowsCross size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.deny}</Text>
</div>
<div className={classes.action} onClick={confirmRemove}>
<ActionIcon variant="subtle" loading={removing} size="lg">
<IconUserX size="lg" />
<ActionIcon variant="subtle" loading={removing} size={32}>
<IconUserX size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.remove}</Text>
</div>
<div className={classes.action} onClick={confirmBlock}>
<ActionIcon variant="subtle" loading={blocking} size="lg">
<IconEyeOff size="lg" />
<ActionIcon variant="subtle" loading={blocking} size={32}>
<IconEyeOff size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.block}</Text>
</div>
<div className={classes.action} onClick={confirmReport}>
<ActionIcon variant="subtle" loading={reporting} size="lg">
<IconAlertHexagon size="lg" />
<ActionIcon variant="subtle" loading={reporting} size={32}>
<IconAlertHexagon size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.report}</Text>
</div>
@ -400,26 +400,26 @@ export function Profile({ params, close }: { params: ProfileParams; close?: () =
{state.statusLabel === 'connectingStatus' && (
<div className={classes.actions}>
<div className={classes.action} onClick={applyCancel}>
<ActionIcon variant="subtle" loading={canceling} size="lg">
<IconCancel size="lg" />
<ActionIcon variant="subtle" loading={canceling} size={32}>
<IconCancel size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.cancel}</Text>
</div>
<div className={classes.action} onClick={confirmRemove}>
<ActionIcon variant="subtle" loading={removing} size="lg">
<IconUserX size="lg" />
<ActionIcon variant="subtle" loading={removing} size={32}>
<IconUserX size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.remove}</Text>
</div>
<div className={classes.action} onClick={confirmBlock}>
<ActionIcon variant="subtle" loading={blocking} size="lg">
<IconEyeOff size="lg" />
<ActionIcon variant="subtle" loading={blocking} size={32}>
<IconEyeOff size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.block}</Text>
</div>
<div className={classes.action} onClick={confirmReport}>
<ActionIcon variant="subtle" loading={reporting} size="lg">
<IconAlertHexagon size="lg" />
<ActionIcon variant="subtle" loading={reporting} size={32}>
<IconAlertHexagon size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.report}</Text>
</div>
@ -428,26 +428,26 @@ export function Profile({ params, close }: { params: ProfileParams; close?: () =
{state.statusLabel === 'connectedStatus' && (
<div className={classes.actions}>
<div className={classes.action} onClick={confirmDisconnect}>
<ActionIcon variant="subtle" loading={disconnecting} size="lg">
<IconRouteX2 size="lg" />
<ActionIcon variant="subtle" loading={disconnecting} size={32}>
<IconRouteX2 size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.disconnect}</Text>
</div>
<div className={classes.action} onClick={confirmRemove}>
<ActionIcon variant="subtle" loading={removing} size="lg">
<IconUserX size="lg" />
<ActionIcon variant="subtle" loading={removing} size={32}>
<IconUserX size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.remove}</Text>
</div>
<div className={classes.action} onClick={confirmBlock}>
<ActionIcon variant="subtle" loading={blocking} size="lg">
<IconEyeOff size="lg" />
<ActionIcon variant="subtle" loading={blocking} size={32}>
<IconEyeOff size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.block}</Text>
</div>
<div className={classes.action} onClick={confirmReport}>
<ActionIcon variant="subtle" loading={reporting} size="lg">
<IconAlertHexagon size="lg" />
<ActionIcon variant="subtle" loading={reporting} size={32}>
<IconAlertHexagon size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.report}</Text>
</div>
@ -456,32 +456,32 @@ export function Profile({ params, close }: { params: ProfileParams; close?: () =
{state.statusLabel === 'offsyncStatus' && (
<div className={classes.actions}>
<div className={classes.action} onClick={applyResync}>
<ActionIcon variant="subtle" loading={resyncing} size="lg">
<IconRefresh size="lg" />
<ActionIcon variant="subtle" loading={resyncing} size={32}>
<IconRefresh size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.resync}</Text>
</div>
<div className={classes.action} onClick={confirmDisconnect}>
<ActionIcon variant="subtle" loading={disconnecting} size="lg">
<IconRouteX2 size="lg" />
<ActionIcon variant="subtle" loading={disconnecting} size={32}>
<IconRouteX2 size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.disconnect}</Text>
</div>
<div className={classes.action} onClick={confirmRemove}>
<ActionIcon variant="subtle" loading={removing} size="lg">
<IconUserX size="lg" />
<ActionIcon variant="subtle" loading={removing} size={32}>
<IconUserX size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.remove}</Text>
</div>
<div className={classes.action} onClick={confirmBlock}>
<ActionIcon variant="subtle" loading={blocking} size="lg">
<IconEyeOff size="lg" />
<ActionIcon variant="subtle" loading={blocking} size={32}>
<IconEyeOff size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.block}</Text>
</div>
<div className={classes.action} onClick={confirmReport}>
<ActionIcon variant="subtle" loading={reporting} size="lg">
<IconAlertHexagon size="lg" />
<ActionIcon variant="subtle" loading={reporting} size={32}>
<IconAlertHexagon size={32} />
</ActionIcon>
<Text className={classes.actionLabel}>{state.strings.report}</Text>
</div>