mirror of
https://github.com/balzack/databag.git
synced 2025-04-23 01:55:17 +00:00
implementing blocked card restore
This commit is contained in:
parent
90e153fdd1
commit
9e23a2689d
@ -3,7 +3,7 @@ import { Focus } from 'databag-client-sdk'
|
||||
import classes from './Conversation.module.css'
|
||||
import { useConversation } from './useConversation.hook';
|
||||
import { IconSend, IconTextSize, IconTextColor, IconVideo, IconFile, IconDisc, IconCamera, IconX, IconSettings, IconHome, IconServer, IconShield, IconLock, IconExclamationCircle } from '@tabler/icons-react'
|
||||
import { Menu, Divider, Text, Textarea, ActionIcon, Loader } from '@mantine/core'
|
||||
import { CloseButton, Menu, Divider, Text, Textarea, ActionIcon, Loader } from '@mantine/core'
|
||||
import { Message } from '../message/Message';
|
||||
import { modals } from '@mantine/modals'
|
||||
import { ImageFile } from './imageFile/ImageFile';
|
||||
@ -187,7 +187,7 @@ export function Conversation({ openDetails }: { openDetails: ()=>void }) {
|
||||
)}
|
||||
</div>
|
||||
<div className={classes.control}>
|
||||
<IconX size={24} className={classes.close} onClick={actions.close} />
|
||||
<CloseButton className={classes.close} onClick={actions.close} />
|
||||
</div>
|
||||
</div>
|
||||
<div ref={thread} className={classes.frame} style={state.loadingMore ? { overflow: 'hidden' } : { overflow: 'auto' }} onScroll={onScroll}>
|
||||
|
@ -5,6 +5,7 @@ import { IconUserCog, IconEyeOff, IconAlertHexagon, IconMessageX, IconLogout2, I
|
||||
import { Switch, Button, Modal, Divider, Text, Textarea, Image, TextInput, ActionIcon } from '@mantine/core'
|
||||
import { Card } from '../card/Card';
|
||||
import { modals } from '@mantine/modals'
|
||||
import { useDisclosure } from '@mantine/hooks'
|
||||
|
||||
export function Details({ showClose, close }: { showClose: boolean, close: () => void }) {
|
||||
const { state, actions } = useDetails()
|
||||
@ -12,7 +13,7 @@ export function Details({ showClose, close }: { showClose: boolean, close: () =>
|
||||
const [removing, setRemoving] = useState(false);
|
||||
const [blocking, setBlocking] = useState(false);
|
||||
const [reporting, setReporting] = useState(false);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [showModal, { open: setShowModal, close: clearShowModal }] = useDisclosure(false)
|
||||
|
||||
const undo = () => {
|
||||
actions.undoSubject();
|
||||
@ -291,7 +292,7 @@ export function Details({ showClose, close }: { showClose: boolean, close: () =>
|
||||
<Text className={classes.actionLabel}>{state.strings.remove}</Text>
|
||||
</div>
|
||||
<div className={classes.action}>
|
||||
<ActionIcon variant="subtle" size={32} onClick={()=>setShowModal(true)}>
|
||||
<ActionIcon variant="subtle" size={32} onClick={setShowModal}>
|
||||
<IconUserCog size={32} />
|
||||
</ActionIcon>
|
||||
<Text className={classes.actionLabel}>{state.strings.members}</Text>
|
||||
@ -324,7 +325,7 @@ export function Details({ showClose, close }: { showClose: boolean, close: () =>
|
||||
<Text>{ state.strings.syncError }</Text>
|
||||
</div>
|
||||
)}
|
||||
<Modal title={state.strings.editMembership} opened={showModal} onClose={() => setShowModal(false)} overlayProps={{ backgroundOpacity: 0.65, blur: 3 }} centered>
|
||||
<Modal title={state.strings.editMembership} opened={showModal} onClose={clearShowModal} overlayProps={{ backgroundOpacity: 0.65, blur: 3 }} centered>
|
||||
<div className={classes.modalContainer}>
|
||||
{ members.length > 0 && (
|
||||
<div className={classes.cardMembers}>
|
||||
@ -337,7 +338,7 @@ export function Details({ showClose, close }: { showClose: boolean, close: () =>
|
||||
</div>
|
||||
)}
|
||||
<div className={classes.controls}>
|
||||
<Button variant="default" onClick={() => setShowModal(false)}>
|
||||
<Button variant="default" onClick={clearShowModal}>
|
||||
{state.strings.close}
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -208,8 +208,12 @@ export function Message({ topic, card, profile, host }: { topic: Topic, card: Ca
|
||||
{ (host || profile) && (
|
||||
<IconTrash className={classes.careful} onClick={remove} />
|
||||
)}
|
||||
<IconForbid className={classes.careful} onClick={block} />
|
||||
<IconFlag className={classes.careful} onClick={report} />
|
||||
{ !profile && (
|
||||
<IconForbid className={classes.careful} onClick={block} />
|
||||
)}
|
||||
{ !profile && (
|
||||
<IconFlag className={classes.careful} onClick={report} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -18,7 +18,7 @@ import {
|
||||
IconCancel,
|
||||
IconDeviceFloppy,
|
||||
} from '@tabler/icons-react'
|
||||
import { Text, Image, ActionIcon } from '@mantine/core'
|
||||
import { CloseButton, Text, Image, ActionIcon } from '@mantine/core'
|
||||
|
||||
export type ProfileParams = {
|
||||
guid: string
|
||||
@ -239,9 +239,9 @@ export function Profile({ params, showClose, close }: { params: ProfileParams; s
|
||||
return (
|
||||
<div className={classes.contact}>
|
||||
<div className={classes.header}>
|
||||
{showClose && <IconX size={28} className={classes.match} />}
|
||||
{showClose && <CloseButton className={classes.match} />}
|
||||
<Text className={classes.label}>{`${state.handle}${state.node ? '/' + state.node : ''}`}</Text>
|
||||
{showClose && <IconX size={30} className={classes.close} onClick={close} />}
|
||||
{showClose && <CloseButton className={classes.close} onClick={close} />}
|
||||
</div>
|
||||
<div className={classes.detail}>
|
||||
<div className={classes.image}>
|
||||
|
@ -278,3 +278,53 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
width: 600px;
|
||||
height: 128px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid var(--mantine-color-text-7);
|
||||
border-radius: 4px;
|
||||
overflow: auto;
|
||||
background: var(--mantine-color-surface-1);
|
||||
|
||||
.emptyLabel {
|
||||
color: var(--mantine-color-text-6);
|
||||
}
|
||||
}
|
||||
.blocked {
|
||||
width: 600px;
|
||||
min-height: 128px;
|
||||
max-height: 256px;
|
||||
border: 1px solid var(--mantine-color-text-7);
|
||||
border-radius: 4px;
|
||||
overflow: auto;
|
||||
background: var(--mantine-color-surface-1);
|
||||
|
||||
.blockedItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid var(--mantine-color-text-8);
|
||||
|
||||
.blockedValue {
|
||||
flex-grow: 1;
|
||||
font-size: 14px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
padding-top: 8px;
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useSettings } from './useSettings.hook'
|
||||
import { Modal, Textarea, TextInput, PasswordInput, Radio, Group, Select, Switch, Text, PinInput, Image, Button, UnstyledButton } from '@mantine/core'
|
||||
import { Modal, Textarea, TextInput, PasswordInput, ActionIcon, Radio, Group, Select, Switch, Text, PinInput, Image, Button, UnstyledButton } from '@mantine/core'
|
||||
import classes from './Settings.module.css'
|
||||
import {
|
||||
IconLock,
|
||||
@ -11,6 +11,7 @@ import {
|
||||
IconVideo,
|
||||
IconMicrophone,
|
||||
IconWorld,
|
||||
IconRestore,
|
||||
IconBrightness,
|
||||
IconTicket,
|
||||
IconCloudLock,
|
||||
@ -43,6 +44,9 @@ export function Settings({ showLogout }: { showLogout: boolean }) {
|
||||
const [imageOpened, { open: imageOpen, close: imageClose }] = useDisclosure(false)
|
||||
const [mfaOpened, { open: mfaOpen, close: mfaClose }] = useDisclosure(false)
|
||||
const [sealOpened, { open: sealOpen, close: sealClose }] = useDisclosure(false)
|
||||
const [blockedContactOpened, { open: blockedContactOpen, close: blockedContactClose }] = useDisclosure(false)
|
||||
const [blockedTopicOpened, { open: blockedTopicOpen, close: blockedTopicClose }] = useDisclosure(false)
|
||||
const [blockedMessageOpened, { open: blockedMessageOpen, close: blockedMessageClose }] = useDisclosure(false)
|
||||
const [savingLogin, setSavingLogin] = useState(false)
|
||||
const [savingDetails, setSavingDetails] = useState(false)
|
||||
const [savingImage, setSavingImage] = useState(false)
|
||||
@ -57,6 +61,27 @@ export function Settings({ showLogout }: { showLogout: boolean }) {
|
||||
const [sealConfig, setSealConfig] = useState(false)
|
||||
const [authMessage, setAuthMessage] = useState('')
|
||||
|
||||
const showBlockedCards = async () => {
|
||||
await actions.loadBlockedCards();
|
||||
blockedContactOpen();
|
||||
}
|
||||
|
||||
const unblockCard = async (cardId: string) => {
|
||||
try {
|
||||
await actions.unblockCard(cardId);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
showError();
|
||||
}
|
||||
}
|
||||
|
||||
const blockedCards = state.blockedCards.map(blocked => (
|
||||
<div className={classes.blockedItem}>
|
||||
<Text className={classes.blockedValue}>CardID: { blocked.cardId }</Text>
|
||||
<ActionIcon variant="subtle" size="md" onClick={()=>unblockCard(blocked.cardId)}><IconRestore /></ActionIcon>
|
||||
</div>
|
||||
));
|
||||
|
||||
const logout = () =>
|
||||
modals.openConfirmModal({
|
||||
title: state.strings.confirmLogout,
|
||||
@ -430,19 +455,19 @@ export function Settings({ showLogout }: { showLogout: boolean }) {
|
||||
</Text>
|
||||
</div>
|
||||
<div className={classes.divider} />
|
||||
<div className={classes.entry}>
|
||||
<div className={classes.entry} onClick={showBlockedCards}>
|
||||
<div className={classes.entryIcon}>
|
||||
<IconUserCancel />
|
||||
</div>
|
||||
<Text className={classes.entryLabel}>{state.strings.blockedContacts}</Text>
|
||||
</div>
|
||||
<div className={classes.entry}>
|
||||
<div className={classes.entry} onClick={blockedTopicOpen}>
|
||||
<div className={classes.entryIcon}>
|
||||
<IconFolderCancel />
|
||||
</div>
|
||||
<Text className={classes.entryLabel}>{state.strings.blockedTopics}</Text>
|
||||
</div>
|
||||
<div className={classes.entry}>
|
||||
<div className={classes.entry} onClick={blockedMessageOpen}>
|
||||
<div className={classes.entryIcon}>
|
||||
<IconMessage2Cancel />
|
||||
</div>
|
||||
@ -803,6 +828,29 @@ export function Settings({ showLogout }: { showLogout: boolean }) {
|
||||
)}
|
||||
</>
|
||||
</Modal>
|
||||
<Modal title={state.strings.blockedContacts} size="auto" opened={blockedContactOpened} onClose={blockedContactClose} overlayProps={{ backgroundOpacity: 0.55, blur: 3 }} centered>
|
||||
{ blockedCards.length > 0 && (
|
||||
<div className={classes.blocked}>
|
||||
{ blockedCards }
|
||||
</div>
|
||||
)}
|
||||
{ blockedCards.length === 0 && (
|
||||
<div className={classes.empty}>
|
||||
<Text className={classes.emptyLabel}>{ state.strings.noContacts }</Text>
|
||||
</div>
|
||||
)}
|
||||
<div className={classes.controls}>
|
||||
<Button variant="default" onClick={blockedContactClose}>
|
||||
{state.strings.close}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal title={state.strings.blockedTopics} opened={blockedTopicOpened} onClose={blockedTopicClose} overlayProps={{ backgroundOpacity: 0.55, blur: 3 }} centered>
|
||||
<div className={classes.blocked}></div>
|
||||
</Modal>
|
||||
<Modal title={state.strings.blockedMessages} opened={blockedMessageOpened} onClose={blockedMessageClose} overlayProps={{ backgroundOpacity: 0.55, blur: 3 }} centered>
|
||||
<div className={classes.blocked}></div>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ export function useSettings() {
|
||||
sealConfirm: '',
|
||||
sealDelete: '',
|
||||
secretCopied: false,
|
||||
blockedCards: [] as {cardId: string}[],
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@ -112,6 +113,17 @@ export function useSettings() {
|
||||
}, [display.state])
|
||||
|
||||
const actions = {
|
||||
loadBlockedCards: async () => {
|
||||
const settings = app.state.session.getSettings();
|
||||
const blockedCards = await settings.getBlockedCards();
|
||||
updateState({ blockedCards });
|
||||
},
|
||||
unblockCard: async (cardId: string) => {
|
||||
const contact = app.state.session.getContact();
|
||||
await contact.setBlockedCard(cardId, false);
|
||||
const blockedCards = state.blockedCards.filter(blocked => blocked.cardId != cardId);
|
||||
updateState({ blockedCards });
|
||||
},
|
||||
getUsernameStatus: async (username: string) => {
|
||||
const { settings } = getSession()
|
||||
return await settings.getUsernameStatus(username)
|
||||
|
@ -44,6 +44,10 @@ export interface Settings {
|
||||
updateSeal(password: string): Promise<void>;
|
||||
forgetSeal(): Promise<void>;
|
||||
|
||||
getBlockedCards(): Promise<{cardId: string}[]>;
|
||||
getBlockedChannels(): Promise<{cardId: string | null, channelId: string}[]>;
|
||||
getBlockedTopicis(): Promise<{cardId: string | null, channelId: string, topicId: string}[]>;
|
||||
|
||||
addConfigListener(ev: (config: Config) => void): void;
|
||||
removeConfigListener(ev: (config: Config) => void): void;
|
||||
}
|
||||
@ -69,7 +73,6 @@ export interface Contact {
|
||||
resyncCard(cardId: string): Promise<void>;
|
||||
flagCard(cardId: string): Promise<void>;
|
||||
setBlockedCard(cardId: string, blocked: boolean): Promise<void>;
|
||||
getBlockedCards(): Promise<Card[]>;
|
||||
getRegistry(handle: string | null, server: string | null): Promise<Profile[]>;
|
||||
|
||||
addCardListener(ev: (cards: Card[]) => void): void;
|
||||
@ -91,7 +94,7 @@ export interface Content {
|
||||
|
||||
flagChannel(cardId: string | null, channelId: string): Promise<void>;
|
||||
setBlockedChannel(cardId: string | null, channelId: string, blocked: boolean): Promise<void>;
|
||||
getBlockedChannels(): Promise<Channel[]>;
|
||||
clearBlockedChannelTopic(cardId: string | null, channelId: string, topicId: string): Promise<void>;
|
||||
|
||||
addChannelListener(ev: (arg: { channels: Channel[]; cardId: string | null }) => void): void;
|
||||
removeChannelListener(ev: (arg: { channels: Channel[]; cardId: string | null }) => void): void;
|
||||
|
@ -199,7 +199,7 @@ export class ContactModule implements Contact {
|
||||
this.blockedCard.add(cardId);
|
||||
entry.card = this.setCard(cardId, entry.item);
|
||||
this.emitCards();
|
||||
await this.store.setMarker(this.guid, 'blocked_card', cardId, '');
|
||||
await this.store.setMarker(this.guid, 'blocked_card', cardId, JSON.stringify({cardId}));
|
||||
}
|
||||
|
||||
private async clearCardBlocked(cardId: string) {
|
||||
@ -231,7 +231,7 @@ export class ContactModule implements Contact {
|
||||
this.blockedCardChannel.add(id);
|
||||
channelEntry.channel = this.setChannel(cardId, channelId, channelEntry.item);
|
||||
this.emitChannels(cardId);
|
||||
await this.store.setMarker(this.guid, 'blocked_card_channel', id, '');
|
||||
await this.store.setMarker(this.guid, 'blocked_card_channel', id, JSON.stringify({ cardId, channelId }));
|
||||
}
|
||||
|
||||
private async clearChannelBlocked(cardId: string, channelId: string) {
|
||||
@ -841,30 +841,6 @@ export class ContactModule implements Contact {
|
||||
}
|
||||
}
|
||||
|
||||
public async getBlockedCards(): Promise<Card[]> {
|
||||
return Array.from(this.cardEntries.entries())
|
||||
.filter(([key, value]) => this.isCardBlocked(key))
|
||||
.map(([key, value]) => value.card);
|
||||
}
|
||||
|
||||
public async getBlockedChannels(): Promise<Channel[]> {
|
||||
const channels: Channel[] = [];
|
||||
this.channelEntries.forEach((card, cardId) => {
|
||||
const cardChannels = Array.from(card.entries())
|
||||
.filter(([key, value]) => this.isChannelBlocked(cardId, key))
|
||||
.map(([key, value]) => value.channel);
|
||||
cardChannels.forEach((channel) => {
|
||||
channels.push(channel);
|
||||
});
|
||||
});
|
||||
return channels;
|
||||
}
|
||||
|
||||
public async getBlockedArticles(): Promise<Article[]> {
|
||||
const articles: Article[] = [];
|
||||
return articles;
|
||||
}
|
||||
|
||||
public async setBlockedCard(cardId: string, blocked: boolean): Promise<void> {
|
||||
const entry = this.cardEntries.get(cardId);
|
||||
if (entry) {
|
||||
@ -894,6 +870,14 @@ export class ContactModule implements Contact {
|
||||
}
|
||||
}
|
||||
|
||||
public async clearBlockedChannelTopic(cardId: string, channelId: string, topicId: string) {
|
||||
const id = `${cardId}:${channelId}:${topicId}`
|
||||
await this.store.clearMarker(guid, 'blocked_topic', id);
|
||||
if (this.focus) {
|
||||
await this.focus.clearBlockedChannelTopic(cardId, channelId, topicId);
|
||||
}
|
||||
}
|
||||
|
||||
public async setBlockedArticle(cardId: string, articleId: string, blocked: boolean): Promise<void> {
|
||||
const entries = this.articleEntries.get(cardId);
|
||||
if (entries) {
|
||||
|
@ -99,6 +99,13 @@ export class ContentModule implements Content {
|
||||
return channels.concat(cardChannels);
|
||||
}
|
||||
|
||||
public async clearBlockedChannelTopic(cardId: string | null, channelId: string, topicId: string): Promise<void> {
|
||||
if (cardId) {
|
||||
return await this.contact.clearBlockedChannelTopic(cardId, channelId, topicId);
|
||||
}
|
||||
return await this.stream.clearBlockedChannelTopic(channelId, topicId);
|
||||
}
|
||||
|
||||
public addChannelListener(ev: (arg: { channels: Channel[]; cardId: string | null }) => void): void {
|
||||
this.stream.addChannelListener(ev);
|
||||
this.contact.addChannelListener(ev);
|
||||
|
@ -822,7 +822,7 @@ export class FocusModule implements Focus {
|
||||
this.blocked.add(id);
|
||||
entry.topic = this.setTopic(topicId, entry.item);
|
||||
this.emitTopics();
|
||||
await this.store.setMarker(guid, 'blocked_topic', id, '');
|
||||
await this.store.setMarker(guid, 'blocked_topic', id, JSON.stringify({ cardId, channelId, topicId }));
|
||||
}
|
||||
}
|
||||
|
||||
@ -838,6 +838,12 @@ export class FocusModule implements Focus {
|
||||
}
|
||||
}
|
||||
|
||||
public async clearBlockedChannelTopic(cardId: string | null, channelId: string, topicId: string) {
|
||||
if (cardId === this.cardId && channelId === this.channelId) {
|
||||
await this.clearBlockTopic(topicId);
|
||||
}
|
||||
}
|
||||
|
||||
private isTopicBlocked(topicId: string): boolean {
|
||||
const { cardId, channelId, guid } = this;
|
||||
const id = `${cardId ? cardId : ''}:${channelId}:${topicId}`
|
||||
|
@ -279,4 +279,26 @@ export class SettingsModule implements Settings {
|
||||
const { node, secure, token } = this;
|
||||
await setAccountLogin(node, secure, token, username, password);
|
||||
}
|
||||
|
||||
public async getBlockedCards(): Promise<{cardId: string}[]> {
|
||||
const { guid } = this;
|
||||
const blockedContacts = await this.store.getMarkers(guid, 'blocked_card');
|
||||
return blockedContacts.map(marker => {
|
||||
try {
|
||||
return JSON.parse(marker.value);
|
||||
} catch (err) {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async getBlockedChannels(): Promise<{cardId: string | null, channelId: string}[]> {
|
||||
const blockedChannels = await this.store.getMarkers(guid, 'blocked_card_channel');
|
||||
return [];
|
||||
}
|
||||
|
||||
public async getBlockedTopics(): Promise<{cardId: string | null, channelId: string, topicId: string}[]> {
|
||||
const blockedTopics = await this.store.getMarkers(guid, 'blocked_topic');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -383,10 +383,12 @@ export class StreamModule {
|
||||
}
|
||||
}
|
||||
|
||||
public async getBlockedChannels(): Promise<Channel[]> {
|
||||
return Array.from(this.channelEntries.entries())
|
||||
.filter(([key, value]) => this.isChannelBlocked(key))
|
||||
.map(([key, value]) => value.channel);
|
||||
public async clearBlockedChannelTopic(channelId: string, topicId: string) {
|
||||
const id = `'':${channelId}:${topicId}`
|
||||
await this.store.clearMarker(guid, 'blocked_topic', id);
|
||||
if (this.focus) {
|
||||
await this.focus.clearBlockedChannelTopic(null, channelId, topicId);
|
||||
}
|
||||
}
|
||||
|
||||
public async flagChannel(channelId: string): Promise<void> {
|
||||
@ -507,7 +509,7 @@ export class StreamModule {
|
||||
this.blocked.add(channelId);
|
||||
entry.channel = this.setChannel(channelId, entry.item);
|
||||
this.emitChannels();
|
||||
await this.store.setMarker(this.guid, 'blocked_channel', channelId, '');
|
||||
await this.store.setMarker(this.guid, 'blocked_channel', channelId, JSON.stringify({ cardId: null, channelId }));
|
||||
}
|
||||
|
||||
private async clearChannelBlocked(channelId: string) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user