implemented block restore on messages and topics

This commit is contained in:
balzack 2024-12-24 08:31:36 -08:00
parent 9e23a2689d
commit 421963956b
9 changed files with 188 additions and 32 deletions

View File

@ -52,6 +52,7 @@ export const en = {
subject: 'Subject', subject: 'Subject',
noContacts: 'No Contacts', noContacts: 'No Contacts',
noTopics: 'No Topics', noTopics: 'No Topics',
noMessages: 'No Messages',
noConnected: 'No Connected Contacts', noConnected: 'No Connected Contacts',
subjectOptional: 'Subject (optional)', subjectOptional: 'Subject (optional)',
members: 'Members', members: 'Members',
@ -363,6 +364,7 @@ export const fr = {
subject: 'Sujet', subject: 'Sujet',
noContacts: 'Pas de Contacts', noContacts: 'Pas de Contacts',
noTopics: 'Pas de Sujets', noTopics: 'Pas de Sujets',
noMessages: 'Pas de Messages',
noConnected: 'Pas de Contacts Connecter', noConnected: 'Pas de Contacts Connecter',
subjectOptional: 'Sujet (optionnel)', subjectOptional: 'Sujet (optionnel)',
members: 'Membres', members: 'Membres',
@ -637,8 +639,9 @@ export const sp = {
newTopic: 'Nuevo tema', newTopic: 'Nuevo tema',
subject: 'Tema', subject: 'Tema',
noContacts: 'Sin contactos', noContacts: 'Sin Contactos',
noTopics: 'Sin temas', noTopics: 'Sin Temas',
noMessages: 'Sin Mensajes',
noConnected: 'Ningún contacto conectado', noConnected: 'Ningún contacto conectado',
subjectOptional: 'Tema (opcional)', subjectOptional: 'Tema (opcional)',
members: 'Miembros', members: 'Miembros',
@ -912,8 +915,9 @@ export const pt = {
newTopic: 'Novo tópico', newTopic: 'Novo tópico',
subject: 'Assunto', subject: 'Assunto',
noContacts: 'Sem contatos', noContacts: 'Sem Contatos',
noTopics: 'Sem tópicos', noTopics: 'Sem Tópicos',
noMessages: 'Sem Mensagem',
noConnected: 'Nenhum contato conectado', noConnected: 'Nenhum contato conectado',
subjectOptional: 'Assunto (opcional)', subjectOptional: 'Assunto (opcional)',
members: 'Membros', members: 'Membros',
@ -1189,6 +1193,7 @@ export const de = {
subject: 'Betreff', subject: 'Betreff',
noContacts: 'Keine Kontakte', noContacts: 'Keine Kontakte',
noTopics: 'Keine Themen', noTopics: 'Keine Themen',
noMessages: 'Keine Nachrichten',
noConnected: 'Keine verbundenen Kontakte', noConnected: 'Keine verbundenen Kontakte',
subjectOptional: 'Betreff (optional)', subjectOptional: 'Betreff (optional)',
members: 'Mitglieder', members: 'Mitglieder',
@ -1464,6 +1469,7 @@ export const ru = {
subject: 'Тема', subject: 'Тема',
noContacts: 'Нет контактов', noContacts: 'Нет контактов',
noTopics: 'Нет тем', noTopics: 'Нет тем',
noMessages: 'Нет сообщений',
noConnected: 'Нет подключенных контактов', noConnected: 'Нет подключенных контактов',
subjectOptional: 'Тема (необязательно)', subjectOptional: 'Тема (необязательно)',
members: 'Участники', members: 'Участники',

View File

@ -280,7 +280,7 @@
} }
.empty { .empty {
width: 600px; width: 400px;
height: 128px; height: 128px;
display: flex; display: flex;
align-items: center; align-items: center;
@ -295,7 +295,7 @@
} }
} }
.blocked { .blocked {
width: 600px; width: 400px;
min-height: 128px; min-height: 128px;
max-height: 256px; max-height: 256px;
border: 1px solid var(--mantine-color-text-7); border: 1px solid var(--mantine-color-text-7);
@ -312,6 +312,10 @@
padding-bottom: 8px; padding-bottom: 8px;
border-bottom: 1px solid var(--mantine-color-text-8); border-bottom: 1px solid var(--mantine-color-text-8);
&:hover {
background: var(--mantine-color-surface-2);
}
.blockedValue { .blockedValue {
flex-grow: 1; flex-grow: 1;
font-size: 14px; font-size: 14px;

View File

@ -45,7 +45,7 @@ export function Settings({ showLogout }: { showLogout: boolean }) {
const [mfaOpened, { open: mfaOpen, close: mfaClose }] = useDisclosure(false) const [mfaOpened, { open: mfaOpen, close: mfaClose }] = useDisclosure(false)
const [sealOpened, { open: sealOpen, close: sealClose }] = useDisclosure(false) const [sealOpened, { open: sealOpen, close: sealClose }] = useDisclosure(false)
const [blockedContactOpened, { open: blockedContactOpen, close: blockedContactClose }] = useDisclosure(false) const [blockedContactOpened, { open: blockedContactOpen, close: blockedContactClose }] = useDisclosure(false)
const [blockedTopicOpened, { open: blockedTopicOpen, close: blockedTopicClose }] = useDisclosure(false) const [blockedChannelOpened, { open: blockedChannelOpen, close: blockedChannelClose }] = useDisclosure(false)
const [blockedMessageOpened, { open: blockedMessageOpen, close: blockedMessageClose }] = useDisclosure(false) const [blockedMessageOpened, { open: blockedMessageOpen, close: blockedMessageClose }] = useDisclosure(false)
const [savingLogin, setSavingLogin] = useState(false) const [savingLogin, setSavingLogin] = useState(false)
const [savingDetails, setSavingDetails] = useState(false) const [savingDetails, setSavingDetails] = useState(false)
@ -66,19 +66,62 @@ export function Settings({ showLogout }: { showLogout: boolean }) {
blockedContactOpen(); blockedContactOpen();
} }
const unblockCard = async (cardId: string) => { const unblockCard = async (blocked: {cardId: string, timestamp: number}) => {
try { try {
await actions.unblockCard(cardId); await actions.unblockCard(blocked.cardId);
} catch (err) { } catch (err) {
console.log(err); console.log(err);
showError(); showError();
} }
} }
const blockedCards = state.blockedCards.map(blocked => ( const blockedCards = state.blockedCards.map((blocked, index) => (
<div className={classes.blockedItem}> <div key={index} className={classes.blockedItem}>
<Text className={classes.blockedValue}>CardID: { blocked.cardId }</Text> <Text className={classes.blockedValue}> { actions.getTimestamp(blocked.timestamp) }</Text>
<ActionIcon variant="subtle" size="md" onClick={()=>unblockCard(blocked.cardId)}><IconRestore /></ActionIcon> <ActionIcon variant="subtle" size="md" onClick={()=>unblockCard(blocked)}><IconRestore /></ActionIcon>
</div>
));
const showBlockedChannels = async () => {
await actions.loadBlockedChannels();
blockedChannelOpen();
}
const unblockChannel = async (blocked: {cardId: string | null, channelId: string, timestamp: number}) => {
try {
await actions.unblockChannel(blocked.cardId, blocked.channelId);
} catch (err) {
console.log(err);
showError();
}
}
const blockedChannels = state.blockedChannels.map((blocked, index) => (
<div key={index} className={classes.blockedItem}>
<Text className={classes.blockedValue}> { actions.getTimestamp(blocked.timestamp) }</Text>
<ActionIcon variant="subtle" size="md" onClick={()=>unblockChannel(blocked)}><IconRestore /></ActionIcon>
</div>
));
const showBlockedMessages = async () => {
await actions.loadBlockedMessages();
blockedMessageOpen();
}
const unblockMessage = async (blocked: {cardId: string | null, channelId: string, topicId: string, timestamp: number}) => {
try {
await actions.unblockMessage(blocked.cardId, blocked.channelId, blocked.topicId);
} catch (err) {
console.log(err);
showError();
}
}
const blockedMessages = state.blockedMessages.map((blocked, index) => (
<div key={index} className={classes.blockedItem}>
<Text className={classes.blockedValue}> { actions.getTimestamp(blocked.timestamp) }</Text>
<ActionIcon variant="subtle" size="md" onClick={()=>unblockMessage(blocked)}><IconRestore /></ActionIcon>
</div> </div>
)); ));
@ -461,13 +504,13 @@ export function Settings({ showLogout }: { showLogout: boolean }) {
</div> </div>
<Text className={classes.entryLabel}>{state.strings.blockedContacts}</Text> <Text className={classes.entryLabel}>{state.strings.blockedContacts}</Text>
</div> </div>
<div className={classes.entry} onClick={blockedTopicOpen}> <div className={classes.entry} onClick={showBlockedChannels}>
<div className={classes.entryIcon}> <div className={classes.entryIcon}>
<IconFolderCancel /> <IconFolderCancel />
</div> </div>
<Text className={classes.entryLabel}>{state.strings.blockedTopics}</Text> <Text className={classes.entryLabel}>{state.strings.blockedTopics}</Text>
</div> </div>
<div className={classes.entry} onClick={blockedMessageOpen}> <div className={classes.entry} onClick={showBlockedMessages}>
<div className={classes.entryIcon}> <div className={classes.entryIcon}>
<IconMessage2Cancel /> <IconMessage2Cancel />
</div> </div>
@ -845,11 +888,39 @@ export function Settings({ showLogout }: { showLogout: boolean }) {
</Button> </Button>
</div> </div>
</Modal> </Modal>
<Modal title={state.strings.blockedTopics} opened={blockedTopicOpened} onClose={blockedTopicClose} overlayProps={{ backgroundOpacity: 0.55, blur: 3 }} centered> <Modal title={state.strings.blockedTopics} opened={blockedChannelOpened} onClose={blockedChannelClose} overlayProps={{ backgroundOpacity: 0.55, blur: 3 }} centered>
<div className={classes.blocked}></div> { blockedChannels.length > 0 && (
<div className={classes.blocked}>
{ blockedChannels }
</div>
)}
{ blockedChannels.length === 0 && (
<div className={classes.empty}>
<Text className={classes.emptyLabel}>{ state.strings.noTopics }</Text>
</div>
)}
<div className={classes.controls}>
<Button variant="default" onClick={blockedChannelClose}>
{state.strings.close}
</Button>
</div>
</Modal> </Modal>
<Modal title={state.strings.blockedMessages} opened={blockedMessageOpened} onClose={blockedMessageClose} overlayProps={{ backgroundOpacity: 0.55, blur: 3 }} centered> <Modal title={state.strings.blockedMessages} opened={blockedMessageOpened} onClose={blockedMessageClose} overlayProps={{ backgroundOpacity: 0.55, blur: 3 }} centered>
<div className={classes.blocked}></div> { blockedMessages.length > 0 && (
<div className={classes.blocked}>
{ blockedMessages }
</div>
)}
{ blockedMessages.length === 0 && (
<div className={classes.empty}>
<Text className={classes.emptyLabel}>{ state.strings.noMessages }</Text>
</div>
)}
<div className={classes.controls}>
<Button variant="default" onClick={blockedMessageClose}>
{state.strings.close}
</Button>
</div>
</Modal> </Modal>
</> </>
) )

View File

@ -50,7 +50,9 @@ export function useSettings() {
sealConfirm: '', sealConfirm: '',
sealDelete: '', sealDelete: '',
secretCopied: false, secretCopied: false,
blockedCards: [] as {cardId: string}[], blockedCards: [] as {cardId: string, timestamp: number}[],
blockedChannels: [] as {cardId: string | null, channelId: string, timestamp: number}[],
blockedMessages: [] as {cardId: string | null, channelId: string, topicId: string, timestamp: number}[],
}) })
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -113,6 +115,30 @@ export function useSettings() {
}, [display.state]) }, [display.state])
const actions = { const actions = {
loadBlockedMessages: async () => {
const settings = app.state.session.getSettings();
const blockedMessages = await settings.getBlockedTopics();
updateState({ blockedMessages });
},
unblockMessage: async (cardId: string | null, channelId: string, topicId: string) => {
const content = app.state.session.getContent();
await content.clearBlockedChannelTopic(cardId, channelId, topicId);
const blockedMessages = state.blockedMessages.filter(blocked => (blocked.cardId != cardId || blocked.channelId != channelId || blocked.topicId != topicId));
updateState({ blockedMessages });
},
loadBlockedChannels: async () => {
console.log("LOAD BLOCKED");
const settings = app.state.session.getSettings();
const blockedChannels = await settings.getBlockedChannels();
console.log("LOADED: ", blockedChannels);
updateState({ blockedChannels });
},
unblockChannel: async (cardId: string | null, channelId: string) => {
const content = app.state.session.getContent();
await content.setBlockedChannel(cardId, channelId, false);
const blockedChannels = state.blockedChannels.filter(blocked => (blocked.cardId != cardId || blocked.channelId != channelId));
updateState({ blockedChannels });
},
loadBlockedCards: async () => { loadBlockedCards: async () => {
const settings = app.state.session.getSettings(); const settings = app.state.session.getSettings();
const blockedCards = await settings.getBlockedCards(); const blockedCards = await settings.getBlockedCards();
@ -314,6 +340,35 @@ export function useSettings() {
const data = dataUrl.split(',')[1] const data = dataUrl.split(',')[1]
await identity.setProfileImage(data) await identity.setProfileImage(data)
}, },
getTimestamp: (created: number) => {
const now = Math.floor((new Date()).getTime() / 1000)
const date = new Date(created * 1000);
const offset = now - created;
if(offset < 43200) {
if (state.timeFormat === '12h') {
return date.toLocaleTimeString("en-US", {hour: 'numeric', minute:'2-digit'});
}
else {
return date.toLocaleTimeString("en-GB", {hour: 'numeric', minute:'2-digit'});
}
}
else if (offset < 31449600) {
if (state.dateFormat === 'mm/dd') {
return date.toLocaleDateString("en-US", {day: 'numeric', month:'numeric'});
}
else {
return date.toLocaleDateString("en-GB", {day: 'numeric', month:'numeric'});
}
}
else {
if (state.dateFormat === 'mm/dd') {
return date.toLocaleDateString("en-US");
}
else {
return date.toLocaleDateString("en-GB");
}
}
}
} }
return { state, actions } return { state, actions }

View File

@ -44,9 +44,9 @@ export interface Settings {
updateSeal(password: string): Promise<void>; updateSeal(password: string): Promise<void>;
forgetSeal(): Promise<void>; forgetSeal(): Promise<void>;
getBlockedCards(): Promise<{cardId: string}[]>; getBlockedCards(): Promise<{cardId: string, timestamp: number}[]>;
getBlockedChannels(): Promise<{cardId: string | null, channelId: string}[]>; getBlockedChannels(): Promise<{cardId: string | null, channelId: string, timestamp: number}[]>;
getBlockedTopicis(): Promise<{cardId: string | null, channelId: string, topicId: string}[]>; getBlockedTopicis(): Promise<{cardId: string | null, channelId: string, topicId: string, timestamp: number}[]>;
addConfigListener(ev: (config: Config) => void): void; addConfigListener(ev: (config: Config) => void): void;
removeConfigListener(ev: (config: Config) => void): void; removeConfigListener(ev: (config: Config) => void): void;

View File

@ -199,7 +199,8 @@ export class ContactModule implements Contact {
this.blockedCard.add(cardId); this.blockedCard.add(cardId);
entry.card = this.setCard(cardId, entry.item); entry.card = this.setCard(cardId, entry.item);
this.emitCards(); this.emitCards();
await this.store.setMarker(this.guid, 'blocked_card', cardId, JSON.stringify({cardId})); const timestamp = Math.floor(Date.now() / 1000);
await this.store.setMarker(this.guid, 'blocked_card', cardId, JSON.stringify({cardId, timestamp}));
} }
private async clearCardBlocked(cardId: string) { private async clearCardBlocked(cardId: string) {
@ -231,7 +232,8 @@ export class ContactModule implements Contact {
this.blockedCardChannel.add(id); this.blockedCardChannel.add(id);
channelEntry.channel = this.setChannel(cardId, channelId, channelEntry.item); channelEntry.channel = this.setChannel(cardId, channelId, channelEntry.item);
this.emitChannels(cardId); this.emitChannels(cardId);
await this.store.setMarker(this.guid, 'blocked_card_channel', id, JSON.stringify({ cardId, channelId })); const timestamp = Math.floor(Date.now() / 1000);
await this.store.setMarker(this.guid, 'blocked_card_channel', id, JSON.stringify({ cardId, channelId, timestamp }));
} }
private async clearChannelBlocked(cardId: string, channelId: string) { private async clearChannelBlocked(cardId: string, channelId: string) {
@ -871,6 +873,7 @@ export class ContactModule implements Contact {
} }
public async clearBlockedChannelTopic(cardId: string, channelId: string, topicId: string) { public async clearBlockedChannelTopic(cardId: string, channelId: string, topicId: string) {
const { guid } = this;
const id = `${cardId}:${channelId}:${topicId}` const id = `${cardId}:${channelId}:${topicId}`
await this.store.clearMarker(guid, 'blocked_topic', id); await this.store.clearMarker(guid, 'blocked_topic', id);
if (this.focus) { if (this.focus) {

View File

@ -822,7 +822,8 @@ export class FocusModule implements Focus {
this.blocked.add(id); this.blocked.add(id);
entry.topic = this.setTopic(topicId, entry.item); entry.topic = this.setTopic(topicId, entry.item);
this.emitTopics(); this.emitTopics();
await this.store.setMarker(guid, 'blocked_topic', id, JSON.stringify({ cardId, channelId, topicId })); const timestamp = Math.floor(Date.now() / 1000);
await this.store.setMarker(guid, 'blocked_topic', id, JSON.stringify({ cardId, channelId, topicId, timestamp }));
} }
} }

View File

@ -280,7 +280,7 @@ export class SettingsModule implements Settings {
await setAccountLogin(node, secure, token, username, password); await setAccountLogin(node, secure, token, username, password);
} }
public async getBlockedCards(): Promise<{cardId: string}[]> { public async getBlockedCards(): Promise<{cardId: string, timestamp: number}[]> {
const { guid } = this; const { guid } = this;
const blockedContacts = await this.store.getMarkers(guid, 'blocked_card'); const blockedContacts = await this.store.getMarkers(guid, 'blocked_card');
return blockedContacts.map(marker => { return blockedContacts.map(marker => {
@ -292,13 +292,28 @@ export class SettingsModule implements Settings {
}); });
} }
public async getBlockedChannels(): Promise<{cardId: string | null, channelId: string}[]> { public async getBlockedChannels(): Promise<{cardId: string | null, channelId: string, timestamp: number}[]> {
const blockedChannels = await this.store.getMarkers(guid, 'blocked_card_channel'); const { guid } = this;
return []; const blockedChannels = await this.store.getMarkers(guid, 'blocked_channel');
const blockedCardChannels = await this.store.getMarkers(guid, 'blocked_card_channel');
return blockedChannels.concat(blockedCardChannels).map(marker => {
try {
return JSON.parse(marker.value);
} catch (err) {
return {};
}
});
} }
public async getBlockedTopics(): Promise<{cardId: string | null, channelId: string, topicId: string}[]> { public async getBlockedTopics(): Promise<{cardId: string | null, channelId: string, topicId: string, timestamp: number}[]> {
const { guid } = this;
const blockedTopics = await this.store.getMarkers(guid, 'blocked_topic'); const blockedTopics = await this.store.getMarkers(guid, 'blocked_topic');
return []; return blockedTopics.map(marker => {
try {
return JSON.parse(marker.value);
} catch (err) {
return {};
}
});
} }
} }

View File

@ -509,7 +509,8 @@ export class StreamModule {
this.blocked.add(channelId); this.blocked.add(channelId);
entry.channel = this.setChannel(channelId, entry.item); entry.channel = this.setChannel(channelId, entry.item);
this.emitChannels(); this.emitChannels();
await this.store.setMarker(this.guid, 'blocked_channel', channelId, JSON.stringify({ cardId: null, channelId })); const timestamp = Math.floor(Date.now() / 1000);
await this.store.setMarker(this.guid, 'blocked_channel', channelId, JSON.stringify({ cardId: null, channelId, timestamp }));
} }
private async clearChannelBlocked(channelId: string) { private async clearChannelBlocked(channelId: string) {