mirror of
https://github.com/oxen-io/session-desktop.git
synced 2023-12-14 02:12:57 +01:00
add a HeaderOption to change the notification settings for each convo
This commit is contained in:
parent
f9dfe4290a
commit
4a98d911a2
8 changed files with 148 additions and 14 deletions
|
@ -1022,6 +1022,22 @@
|
|||
"description": "Conversation menu option to enable disappearing messages",
|
||||
"androidKey": "conversation_expiring_off__disappearing_messages"
|
||||
},
|
||||
"notificationForConvo": {
|
||||
"message": "Notifications",
|
||||
"description": "Conversation menu option to change the notification setting for this conversation"
|
||||
},
|
||||
"notificationForConvo_all": {
|
||||
"message": "All",
|
||||
"description": "Menu item to allow notification for this conversation for all messages"
|
||||
},
|
||||
"notificationForConvo_disabled": {
|
||||
"message": "Disabled",
|
||||
"description": "Menu item to deny notification for this conversation for all messages"
|
||||
},
|
||||
"notificationForConvo_mentions_only": {
|
||||
"message": "Mentions only",
|
||||
"description": "Menu item to allow notification for this conversation for all messages mentioning us"
|
||||
},
|
||||
"changeNickname": {
|
||||
"message": "Change Nickname",
|
||||
"description": "Conversation menu option to change user nickname"
|
||||
|
|
|
@ -15,12 +15,18 @@ import {
|
|||
} from '../session/menu/ConversationHeaderMenu';
|
||||
import { contextMenu } from 'react-contexify';
|
||||
import { DefaultTheme, withTheme } from 'styled-components';
|
||||
import { ConversationNotificationSettingType } from '../../models/conversation';
|
||||
|
||||
export interface TimerOption {
|
||||
name: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface NotificationForConvoOption {
|
||||
name: string;
|
||||
value: ConversationNotificationSettingType;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
name?: string;
|
||||
|
@ -46,6 +52,8 @@ interface Props {
|
|||
expirationSettingName?: string;
|
||||
showBackButton: boolean;
|
||||
timerOptions: Array<TimerOption>;
|
||||
notificationForConvo: Array<NotificationForConvoOption>;
|
||||
currentNotificationSetting: ConversationNotificationSettingType;
|
||||
hasNickname?: boolean;
|
||||
|
||||
isBlocked: boolean;
|
||||
|
@ -56,6 +64,7 @@ interface Props {
|
|||
|
||||
onInviteContacts: () => void;
|
||||
onSetDisappearingMessages: (seconds: number) => void;
|
||||
onSetNotificationForConvo: (selected: ConversationNotificationSettingType) => void;
|
||||
onDeleteMessages: () => void;
|
||||
onDeleteContact: () => void;
|
||||
onChangeNickname?: () => void;
|
||||
|
|
|
@ -724,21 +724,10 @@ class MessageInner extends React.PureComponent<MessageRegularProps, State> {
|
|||
const width = this.getWidth();
|
||||
const isShowingImage = this.isShowingImage();
|
||||
|
||||
// We parse the message later, but we still need to do an early check
|
||||
// to see if the message mentions us, so we can display the entire
|
||||
// message differently
|
||||
const regex = new RegExp(`@${PubKey.regexForPubkeys}`, 'g');
|
||||
const mentions = (text ? text.match(regex) : []) as Array<string>;
|
||||
const mentionMe = mentions && mentions.some(m => UserUtils.isUsFromCache(m.slice(1)));
|
||||
|
||||
const isIncoming = direction === 'incoming';
|
||||
const shouldHightlight = mentionMe && isIncoming && isPublic;
|
||||
const shouldMarkReadWhenVisible = isIncoming && isUnread;
|
||||
const divClasses = ['session-message-wrapper'];
|
||||
|
||||
if (shouldHightlight) {
|
||||
//divClasses.push('message-highlighted');
|
||||
}
|
||||
if (selected) {
|
||||
divClasses.push('message-selected');
|
||||
}
|
||||
|
|
|
@ -30,7 +30,11 @@ import { getMessageById, getPubkeysInPublicConversation } from '../../../data/da
|
|||
import autoBind from 'auto-bind';
|
||||
import { getDecryptedMediaUrl } from '../../../session/crypto/DecryptedAttachmentsManager';
|
||||
import { deleteOpenGroupMessages } from '../../../interactions/conversation';
|
||||
import { ConversationTypeEnum } from '../../../models/conversation';
|
||||
import {
|
||||
ConversationNotificationSetting,
|
||||
ConversationNotificationSettingType,
|
||||
ConversationTypeEnum,
|
||||
} from '../../../models/conversation';
|
||||
import { updateMentionsMembers } from '../../../state/ducks/mentionsInput';
|
||||
import { sendDataExtractionNotification } from '../../../session/messages/outgoing/controlMessage/DataExtractionNotificationMessage';
|
||||
|
||||
|
@ -369,10 +373,18 @@ export class SessionConversation extends React.Component<Props, State> {
|
|||
name: item.getName(),
|
||||
value: item.get('seconds'),
|
||||
})),
|
||||
notificationForConvo: ConversationNotificationSetting.map(
|
||||
(n: ConversationNotificationSettingType) => {
|
||||
// this link to the notificationForConvo_all, notificationForConvo_mentions_only, ...
|
||||
return { value: n, name: window.i18n(`notificationForConvo_${n}`) };
|
||||
}
|
||||
),
|
||||
currentNotificationSetting: conversation.get('triggerNotificationsFor'),
|
||||
hasNickname: !!conversation.getNickname(),
|
||||
selectionMode: !!selectedMessages.length,
|
||||
|
||||
onSetDisappearingMessages: conversation.updateExpirationTimer,
|
||||
onSetNotificationForConvo: conversation.setNotificationOption,
|
||||
onDeleteMessages: conversation.deleteMessages,
|
||||
onDeleteSelectedMessages: this.deleteSelectedMessages,
|
||||
onChangeNickname: conversation.changeNickname,
|
||||
|
|
|
@ -12,10 +12,12 @@ import {
|
|||
getInviteContactMenuItem,
|
||||
getLeaveGroupMenuItem,
|
||||
getMarkAllReadMenuItem,
|
||||
getNotificationForConvoMenuItem,
|
||||
getRemoveModeratorsMenuItem,
|
||||
getUpdateGroupNameMenuItem,
|
||||
} from './Menu';
|
||||
import { TimerOption } from '../../conversation/ConversationHeader';
|
||||
import { NotificationForConvoOption, TimerOption } from '../../conversation/ConversationHeader';
|
||||
import { ConversationNotificationSettingType } from '../../../models/conversation';
|
||||
|
||||
export type PropsConversationHeaderMenu = {
|
||||
triggerId: string;
|
||||
|
@ -26,6 +28,8 @@ export type PropsConversationHeaderMenu = {
|
|||
isGroup: boolean;
|
||||
isAdmin: boolean;
|
||||
timerOptions: Array<TimerOption>;
|
||||
notificationForConvo: Array<NotificationForConvoOption>;
|
||||
currentNotificationSetting: ConversationNotificationSettingType;
|
||||
isPrivate: boolean;
|
||||
isBlocked: boolean;
|
||||
hasNickname?: boolean;
|
||||
|
@ -45,6 +49,7 @@ export type PropsConversationHeaderMenu = {
|
|||
onBlockUser: () => void;
|
||||
onUnblockUser: () => void;
|
||||
onSetDisappearingMessages: (seconds: number) => void;
|
||||
onSetNotificationForConvo: (selected: ConversationNotificationSettingType) => void;
|
||||
};
|
||||
|
||||
export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => {
|
||||
|
@ -60,6 +65,8 @@ export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => {
|
|||
isPrivate,
|
||||
left,
|
||||
hasNickname,
|
||||
notificationForConvo,
|
||||
currentNotificationSetting,
|
||||
|
||||
onClearNickname,
|
||||
onChangeNickname,
|
||||
|
@ -75,6 +82,7 @@ export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => {
|
|||
onBlockUser,
|
||||
onUnblockUser,
|
||||
onSetDisappearingMessages,
|
||||
onSetNotificationForConvo,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
|
@ -88,6 +96,15 @@ export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => {
|
|||
onSetDisappearingMessages,
|
||||
window.i18n
|
||||
)}
|
||||
{getNotificationForConvoMenuItem(
|
||||
isKickedFromGroup,
|
||||
left,
|
||||
isBlocked,
|
||||
notificationForConvo,
|
||||
currentNotificationSetting,
|
||||
onSetNotificationForConvo,
|
||||
window.i18n
|
||||
)}
|
||||
{getBlockMenuItem(isMe, isPrivate, isBlocked, onBlockUser, onUnblockUser, window.i18n)}
|
||||
|
||||
{getCopyMenuItem(isPublic, isGroup, onCopyPublicKey, window.i18n)}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import React from 'react';
|
||||
import { LocalizerType } from '../../../types/Util';
|
||||
import { TimerOption } from '../../conversation/ConversationHeader';
|
||||
import { NotificationForConvoOption, TimerOption } from '../../conversation/ConversationHeader';
|
||||
import { Item, Submenu } from 'react-contexify';
|
||||
import {
|
||||
ConversationNotificationSetting,
|
||||
ConversationNotificationSettingType,
|
||||
} from '../../../models/conversation';
|
||||
|
||||
function showTimerOptions(
|
||||
isPublic: boolean,
|
||||
|
@ -12,6 +16,14 @@ function showTimerOptions(
|
|||
return !isPublic && !left && !isKickedFromGroup && !isBlocked;
|
||||
}
|
||||
|
||||
function showNotificationConvo(
|
||||
isKickedFromGroup: boolean,
|
||||
left: boolean,
|
||||
isBlocked: boolean
|
||||
): boolean {
|
||||
return !left && !isKickedFromGroup && !isBlocked;
|
||||
}
|
||||
|
||||
function showMemberMenu(isPublic: boolean, isGroup: boolean): boolean {
|
||||
return !isPublic && isGroup;
|
||||
}
|
||||
|
@ -223,6 +235,41 @@ export function getDisappearingMenuItem(
|
|||
return null;
|
||||
}
|
||||
|
||||
export function getNotificationForConvoMenuItem(
|
||||
isKickedFromGroup: boolean | undefined,
|
||||
left: boolean | undefined,
|
||||
isBlocked: boolean | undefined,
|
||||
notificationForConvoOptions: Array<NotificationForConvoOption>,
|
||||
currentNotificationSetting: ConversationNotificationSettingType,
|
||||
action: (selected: ConversationNotificationSettingType) => any,
|
||||
i18n: LocalizerType
|
||||
): JSX.Element | null {
|
||||
if (showNotificationConvo(Boolean(isKickedFromGroup), Boolean(left), Boolean(isBlocked))) {
|
||||
// const isRtlMode = isRtlBody();
|
||||
return (
|
||||
// Remove the && false to make context menu work with RTL support
|
||||
<Submenu
|
||||
label={i18n('notificationForConvo') as any}
|
||||
// rtl={isRtlMode && false}
|
||||
>
|
||||
{(notificationForConvoOptions || []).map(item => (
|
||||
// tslint:disable-next-line: use-simple-attributes
|
||||
<Item
|
||||
key={item.value}
|
||||
onClick={() => {
|
||||
action(item.value);
|
||||
}}
|
||||
disabled={item.value === currentNotificationSetting}
|
||||
>
|
||||
{item.name}
|
||||
</Item>
|
||||
))}
|
||||
</Submenu>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function isRtlBody(): boolean {
|
||||
return ($('body') as any).hasClass('rtl');
|
||||
}
|
||||
|
|
|
@ -40,12 +40,21 @@ import { ConversationInteraction } from '../interactions';
|
|||
import { OpenGroupVisibleMessage } from '../session/messages/outgoing/visibleMessage/OpenGroupVisibleMessage';
|
||||
import { OpenGroupRequestCommonType } from '../opengroup/opengroupV2/ApiUtil';
|
||||
import { getOpenGroupV2FromConversationId } from '../opengroup/utils/OpenGroupUtils';
|
||||
import { NotificationForConvoOption } from '../components/conversation/ConversationHeader';
|
||||
|
||||
export enum ConversationTypeEnum {
|
||||
GROUP = 'group',
|
||||
PRIVATE = 'private',
|
||||
}
|
||||
|
||||
/**
|
||||
* all: all notifications enabled, the default
|
||||
* disabled: no notifications at all
|
||||
* mentions_only: trigger a notification only on mentions of ourself
|
||||
*/
|
||||
export const ConversationNotificationSetting = ['all', 'disabled', 'mentions_only'] as const;
|
||||
export type ConversationNotificationSettingType = typeof ConversationNotificationSetting[number];
|
||||
|
||||
export interface ConversationAttributes {
|
||||
profileName?: string;
|
||||
id: string;
|
||||
|
@ -81,6 +90,7 @@ export interface ConversationAttributes {
|
|||
profileAvatar?: any;
|
||||
profileKey?: string;
|
||||
accessKey?: any;
|
||||
triggerNotificationsFor: ConversationNotificationSettingType;
|
||||
}
|
||||
|
||||
export interface ConversationAttributesOptionals {
|
||||
|
@ -116,6 +126,7 @@ export interface ConversationAttributesOptionals {
|
|||
profileAvatar?: any;
|
||||
profileKey?: string;
|
||||
accessKey?: any;
|
||||
triggerNotificationsFor?: ConversationNotificationSettingType;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -143,6 +154,7 @@ export const fillConvoAttributesWithDefaults = (
|
|||
expireTimer: 0,
|
||||
mentionedUs: false,
|
||||
active_at: 0,
|
||||
triggerNotificationsFor: 'all', // if the settings is not set in the db, this is the default
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -185,6 +197,10 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
return this.id;
|
||||
}
|
||||
|
||||
if (this.isPublic()) {
|
||||
return `opengroup(${this.id})`;
|
||||
}
|
||||
|
||||
return `group(${this.id})`;
|
||||
}
|
||||
|
||||
|
@ -777,6 +793,14 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
}
|
||||
}
|
||||
|
||||
public async setNotificationOption(selected: ConversationNotificationSettingType) {
|
||||
const existingSettings = this.get('triggerNotificationsFor');
|
||||
if (existingSettings !== selected) {
|
||||
this.set({ triggerNotificationsFor: selected });
|
||||
await this.commit();
|
||||
}
|
||||
}
|
||||
|
||||
public async updateExpirationTimer(
|
||||
providedExpireTimer: any,
|
||||
providedSource?: string,
|
||||
|
@ -1402,6 +1426,25 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
}
|
||||
const conversationId = this.id;
|
||||
|
||||
// make sure the notifications are not muted for this convo (and not the source convo)
|
||||
const convNotif = this.get('triggerNotificationsFor');
|
||||
if (convNotif === 'disabled') {
|
||||
window?.log?.info('notifications disabled for convo', this.idForLogging());
|
||||
return;
|
||||
}
|
||||
if (convNotif === 'mentions_only') {
|
||||
// check if the message has ourselves as mentions
|
||||
const regex = new RegExp(`@${PubKey.regexForPubkeys}`, 'g');
|
||||
const text = message.get('body');
|
||||
const mentions = text?.match(regex) || ([] as Array<string>);
|
||||
const mentionMe = mentions && mentions.some(m => UserUtils.isUsFromCache(m.slice(1)));
|
||||
if (!mentionMe) {
|
||||
window?.log?.info('notifications disabled for non mentions for convo', conversationId);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const convo = await ConversationController.getInstance().getOrCreateAndWait(
|
||||
message.get('source'),
|
||||
ConversationTypeEnum.PRIVATE
|
||||
|
|
|
@ -86,6 +86,7 @@ export class MockConversation {
|
|||
lastMessageStatus: null,
|
||||
lastMessage: null,
|
||||
zombies: [],
|
||||
triggerNotificationsFor: 'all',
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue