diff --git a/src/app/core/notifications/details.nim b/src/app/core/notifications/details.nim index 2a1429cc4c..a88deec87c 100644 --- a/src/app/core/notifications/details.nim +++ b/src/app/core/notifications/details.nim @@ -6,25 +6,38 @@ include ../../../app_service/common/json_utils type NotificationType* {.pure.} = enum - NewContactRequest = 1, + TestNotification, + NewContactRequest, AcceptedContactRequest, JoinCommunityRequest, MyRequestToJoinCommunityAccepted, MyRequestToJoinCommunityRejected, NewMessage, - NewMention + NewMessageWithPersonalMention, + NewMessageWithGlobalMention, + IdentityVerificationRequest NotificationDetails* = object notificationType*: NotificationType sectionId*: string + isCommunitySection*: bool + sectionActive*: bool chatId*: string + chatActive*: bool + isOneToOne*: bool + isGroupChat*: bool messageId*: string proc toNotificationDetails*(jsonObj: JsonNode): NotificationDetails = var notificationType: int if (not (jsonObj.getProp("notificationType", notificationType) and jsonObj.getProp("sectionId", result.sectionId) and + jsonObj.getProp("isCommunitySection", result.isCommunitySection) and + jsonObj.getProp("sectionActive", result.sectionActive) and jsonObj.getProp("chatId", result.chatId) and + jsonObj.getProp("chatActive", result.chatActive) and + jsonObj.getProp("isOneToOne", result.isOneToOne) and + jsonObj.getProp("isGroupChat", result.isGroupChat) and jsonObj.getProp("messageId", result.messageId))): return NotificationDetails() @@ -34,6 +47,11 @@ proc toJsonNode*(self: NotificationDetails): JsonNode = result = %* { "notificationType": self.notificationType.int, "sectionId": self.sectionId, + "isCommunitySection": self.isCommunitySection, + "sectionActive": self.sectionActive, "chatId": self.chatId, + "chatActive": self.chatActive, + "isOneToOne": self.isOneToOne, + "isGroupChat": self.isGroupChat, "messageId": self.messageId } diff --git a/src/app/core/notifications/notifications_manager.nim b/src/app/core/notifications/notifications_manager.nim index 28e49759a8..fccffcfac0 100644 --- a/src/app/core/notifications/notifications_manager.nim +++ b/src/app/core/notifications/notifications_manager.nim @@ -13,6 +13,7 @@ logScope: const NOTIFICATION_SOUND = "qrc:/imports/assets/audio/notification.wav" # Signals which may be emitted by this class: +const SIGNAL_ADD_NOTIFICATION_TO_ACTIVITY_CENTER* = "addNotificationToActivityCenter" const SIGNAL_DISPLAY_APP_NOTIFICATION* = "displayAppNotification" const SIGNAL_OS_NOTIFICATION_CLICKED* = "osNotificationClicked" @@ -63,18 +64,18 @@ QtObject: self.soundManager = newStatusSoundManager() signalConnect(self.osNotification, "notificationClicked(QString)", self, "onOSNotificationClicked(QString)", 2) - signalConnect(singletonInstance.globalEvents, "showNormalMessageNotification(QString, QString, QString, QString, QString)", - self, "onShowNormalMessageNotification(QString, QString, QString, QString, QString)", 2) - signalConnect(singletonInstance.globalEvents, "showMentionMessageNotification(QString, QString, QString, QString, QString)", - self, "onShowMentionMessageNotification(QString, QString, QString, QString, QString)", 2) + signalConnect(singletonInstance.globalEvents, "showTestNotification(QString, QString)", + self, "onShowTestNotification(QString, QString)", 2) + signalConnect(singletonInstance.globalEvents, "showMessageNotification(QString, QString, QString, bool, bool, QString, bool, QString, int, bool, bool)", + self, "onShowMessageNotification(QString, QString, QString, bool, bool, QString, bool, QString, int, bool, bool)", 2) signalConnect(singletonInstance.globalEvents, "showNewContactRequestNotification(QString, QString, QString)", self, "onShowNewContactRequestNotification(QString, QString, QString)", 2) signalConnect(singletonInstance.globalEvents, "newCommunityMembershipRequestNotification(QString, QString, QString)", self, "onNewCommunityMembershipRequestNotification(QString, QString, QString)", 2) - signalConnect(singletonInstance.globalEvents, "myRequestToJoinCommunityHasBeenAcccepted(QString, QString, QString)", - self, "onMyRequestToJoinCommunityHasBeenAcccepted(QString, QString, QString)", 2) + signalConnect(singletonInstance.globalEvents, "myRequestToJoinCommunityAcccepted(QString, QString, QString)", + self, "onMyRequestToJoinCommunityAcccepted(QString, QString, QString)", 2) signalConnect(singletonInstance.globalEvents, "myRequestToJoinCommunityHasBeenRejected(QString, QString, QString)", - self, "onMyRequestToJoinCommunityHasBeenRejected(QString, QString, QString)", 2) + self, "onMyRequestToJoinCommunityRejected(QString, QString, QString)", 2) self.notificationSetUp = true proc showOSNotification(self: NotificationsManager, title: string, message: string, identifier: string) = @@ -91,23 +92,35 @@ QtObject: app_makeItActive(singletonInstance.engine) let details = toNotificationDetails(parseJson(identifier)) + if(details.notificationType == NotificationType.TestNotification): + info "Test notification was clicked" + return + if(details.notificationType == NotificationType.NewMessage or - details.notificationType == NotificationType.NewMention): + details.notificationType == NotificationType.NewMessageWithPersonalMention or + details.notificationType == NotificationType.NewMessageWithGlobalMention): let data = ActiveSectionChatArgs(sectionId: details.sectionId, chatId: details.chatId, messageId: details.messageId) self.events.emit(SIGNAL_MAKE_SECTION_CHAT_ACTIVE, data) else: self.events.emit(SIGNAL_OS_NOTIFICATION_CLICKED, ClickedNotificationArgs(details: details)) - proc onShowNormalMessageNotification(self: NotificationsManager, title: string, message: string, sectionId: string, - chatId: string, messageId: string) {.slot.} = - let details = NotificationDetails(notificationType: NotificationType.NewMessage, sectionId: sectionId, - chatId: chatId, messageId: messageId) + proc onShowTestNotification(self: NotificationsManager, title: string, message: string) {.slot.} = + let details = NotificationDetails(notificationType: NotificationType.TestNotification) self.processNotification(title, message, details) - proc onShowMentionMessageNotification(self: NotificationsManager, title: string, message: string, sectionId: string, - chatId: string, messageId: string) {.slot.} = - let details = NotificationDetails(notificationType: NotificationType.NewMention, sectionId: sectionId, - chatId: chatId, messageId: messageId) + proc onShowMessageNotification(self: NotificationsManager, title: string, message: string, sectionId: string, + isCommunitySection: bool, isSectionActive: bool, chatId: string, isChatActive: bool, messageId: string, + notificationType: int, isOneToOne: bool, isGroupChat: bool) {.slot.} = + let details = NotificationDetails( + notificationType: notificationType.NotificationType, + sectionId: sectionId, + isCommunitySection: isCommunitySection, + sectionActive: isSectionActive, + chatId: chatId, + chatActive: isChatActive, + isOneToOne: isOneToOne, + isGroupChat: isGroupChat, + messageId: messageId) self.processNotification(title, message, details) proc onShowNewContactRequestNotification*(self: NotificationsManager, title: string, message: string, @@ -120,70 +133,171 @@ QtObject: let details = NotificationDetails(notificationType: NotificationType.JoinCommunityRequest, sectionId: sectionId) self.processNotification(title, message, details) - proc onMyRequestToJoinCommunityHasBeenAcccepted*(self: NotificationsManager, title: string, message: string, + proc onMyRequestToJoinCommunityAcccepted*(self: NotificationsManager, title: string, message: string, sectionId: string) {.slot.} = let details = NotificationDetails(notificationType: NotificationType.MyRequestToJoinCommunityAccepted, sectionId: sectionId) self.processNotification(title, message, details) - proc onMyRequestToJoinCommunityHasBeenRejected*(self: NotificationsManager, title: string, message: string, + proc onMyRequestToJoinCommunityRejected*(self: NotificationsManager, title: string, message: string, sectionId: string) {.slot.} = let details = NotificationDetails(notificationType: NotificationType.MyRequestToJoinCommunityRejected, sectionId: sectionId) self.processNotification(title, message, details) + proc getExemptions(self: NotificationsManager, id: string): JsonNode = + # This proc returns exemptions as json object for the passed `id` if there are no set exemptions, + # json object with the default values will be returned. + let allExemptions = singletonInstance.localAccountSensitiveSettings.getNotifSettingExemptionsAsJson() + result = %* { + EXEMPTION_KEY_MUTE_ALL_MESSAGES: false, + EXEMPTION_KEY_PERSONAL_MENTIONS: LSS_VALUE_NOTIF_SEND_ALERTS, + EXEMPTION_KEY_GLOBAL_MENTIONS: LSS_VALUE_NOTIF_SEND_ALERTS, + EXEMPTION_KEY_OTHER_MESSAGES: LSS_VALUE_NOTIF_SEND_TURN_OFF + } + if(allExemptions.contains(id)): + let obj = allExemptions[id] + if(obj.contains(EXEMPTION_KEY_MUTE_ALL_MESSAGES)): + result[EXEMPTION_KEY_MUTE_ALL_MESSAGES] = obj[EXEMPTION_KEY_MUTE_ALL_MESSAGES] + if(obj.contains(EXEMPTION_KEY_PERSONAL_MENTIONS)): + result[EXEMPTION_KEY_PERSONAL_MENTIONS] = obj[EXEMPTION_KEY_PERSONAL_MENTIONS] + if(obj.contains(EXEMPTION_KEY_GLOBAL_MENTIONS)): + result[EXEMPTION_KEY_GLOBAL_MENTIONS] = obj[EXEMPTION_KEY_GLOBAL_MENTIONS] + if(obj.contains(EXEMPTION_KEY_OTHER_MESSAGES)): + result[EXEMPTION_KEY_OTHER_MESSAGES] = obj[EXEMPTION_KEY_OTHER_MESSAGES] + + proc notificationCheck(self: NotificationsManager, title: string, message: string, details: NotificationDetails, + notificationWay: string) = + var data = NotificationArgs(title: title, message: message, details: details) + # All but the NewMessage notifications go to Activity Center + if(details.notificationType != NotificationType.NewMessage): + debug "Add AC notification", title=title, message=message + self.events.emit(SIGNAL_ADD_NOTIFICATION_TO_ACTIVITY_CENTER, data) + + # An exemption from the diagrams, at least for now, is that we don't need to implement the "Badge Check" block here, + # cause that's already handled in appropriate modules. + + if(details.notificationType == NotificationType.NewMessage or + details.notificationType == NotificationType.NewMessageWithPersonalMention or + details.notificationType == NotificationType.NewMessageWithGlobalMention or + details.notificationType == NotificationType.NewContactRequest or + details.notificationType == NotificationType.IdentityVerificationRequest): + + if(notificationWay == LSS_VALUE_NOTIF_SEND_DELIVER_QUIETLY): + return + + if((details.notificationType == NotificationType.NewMessage or + details.notificationType == NotificationType.NewMessageWithPersonalMention or + details.notificationType == NotificationType.NewMessageWithGlobalMention) and + details.sectionActive and + details.chatActive): + return + + let appIsActive = app_isActive(singletonInstance.engine) + if(appIsActive): + debug "Add APP notification", title=title, message=message + self.events.emit(SIGNAL_DISPLAY_APP_NOTIFICATION, data) + + if(not appIsActive or details.notificationType == NotificationType.TestNotification): + # Check anonymity level + if(singletonInstance.localAccountSensitiveSettings.getNotificationMessagePreviewSetting() == PREVIEW_ANONYMOUS): + data.title = "Status" + data.message = "You have a new message" + elif(singletonInstance.localAccountSensitiveSettings.getNotificationMessagePreviewSetting() == PREVIEW_NAME_ONLY): + data.message = "You have a new message" + let identifier = $(details.toJsonNode()) + debug "Add OS notification", title=data.title, message=data.message, identifier=identifier + self.showOSNotification(data.title, data.message, identifier) + + if(singletonInstance.localAccountSensitiveSettings.getNotificationSoundsEnabled()): + self.soundManager.setPlayerVolume(singletonInstance.localAccountSensitiveSettings.getVolume()) + self.soundManager.playSound(NOTIFICATION_SOUND) + proc processNotification(self: NotificationsManager, title: string, message: string, details: NotificationDetails) = ## This is the main method which need to be called to process an event according to the preferences set in the - ## notifications panel of the settings section. + ## "Notifications & Sounds" panel of the "Settings" section. ## ## This method determines whether a notification need to be displayed or not, what notification to display, whether - ## to display App or OS notification, what level of anonymous to apply, whether to play sound or not and so... + ## to display App or OS notification and/or add notification to Activity Center, what level of anonymous to apply, + ## whether to play sound or not and so... - # I am not 100% sure about this. - # According to this: - # https://github.com/status-im/status-desktop/pull/4789#discussion_r805028513 - # The app should use OS notification only in case it is not the active app or it's minimized at the moment - # we're processing an event. - if(singletonInstance.localAccountSensitiveSettings.getUseOSNotifications() and - app_isActive(singletonInstance.engine)): + # The flow used here follows these diagrams: + # - https://drive.google.com/file/d/1L_9c2CMObcDcSuhVUu97s9-_26gtutES/view + # - https://drive.google.com/file/d/1KmG7lJDJIx6R_HJWeFvMYT2wk32RoTJQ/view + + if(not singletonInstance.localAccountSensitiveSettings.getNotifSettingAllowNotifications()): return - var finalTitle = title - var finalMessage = message + # In case of contact request + if(details.notificationType == NotificationType.NewContactRequest): + if(singletonInstance.localAccountSensitiveSettings.getNotifSettingContactRequests() != LSS_VALUE_NOTIF_SEND_TURN_OFF): + self.notificationCheck(title, message, details, singletonInstance.localAccountSensitiveSettings.getNotifSettingContactRequests()) + return - # Check if notification need to be displayed - if(singletonInstance.localAccountSensitiveSettings.getNotificationSetting() == NOTIFY_NOTHING_ABOUT and - (details.notificationType == NotificationType.NewMessage or - details.notificationType == NotificationType.NewMention)): - return + # In case of identity verification request + elif(details.notificationType == NotificationType.IdentityVerificationRequest): + if(singletonInstance.localAccountSensitiveSettings.getNotifSettingIdentityVerificationRequests() != LSS_VALUE_NOTIF_SEND_TURN_OFF): + self.notificationCheck(title, message, details, singletonInstance.localAccountSensitiveSettings.getNotifSettingIdentityVerificationRequests()) + return - if(singletonInstance.localAccountSensitiveSettings.getNotificationSetting() == NOTIFY_JUST_ABOUT_MENTIONS and - details.notificationType == NotificationType.NewMessage): - return + # In case of new message (regardless it's message with mention or not) + elif(details.notificationType == NotificationType.NewMessage or + details.notificationType == NotificationType.NewMessageWithPersonalMention or + details.notificationType == NotificationType.NewMessageWithGlobalMention): + if(singletonInstance.localAccountSensitiveSettings.getNotifSettingAllMessages() != LSS_VALUE_NOTIF_SEND_TURN_OFF): + self.notificationCheck(title, message, details, singletonInstance.localAccountSensitiveSettings.getNotifSettingAllMessages()) + return - if(not singletonInstance.localAccountSensitiveSettings.getNotifyOnNewRequests() and - details.notificationType == NotificationType.NewContactRequest): - return + let messageBelongsToCommunity = details.isCommunitySection + if(messageBelongsToCommunity): + let exemptionObj = self.getExemptions(details.sectionId) + if(exemptionObj[EXEMPTION_KEY_MUTE_ALL_MESSAGES].getBool): + return - # Check anonymous level - if(singletonInstance.localAccountSensitiveSettings.getNotificationMessagePreviewSetting() == PREVIEW_ANONYMOUS): - finalTitle = "Status" - finalMessage = "You have a new message" - elif(singletonInstance.localAccountSensitiveSettings.getNotificationMessagePreviewSetting() == PREVIEW_NAME_ONLY): - finalMessage = "You have a new message" - - # Check whether to display APP or OS notification - if(singletonInstance.localAccountSensitiveSettings.getUseOSNotifications()): - let identifier = $(details.toJsonNode()) - debug "Add OS notification", title=finalTitle, message=finalMessage, identifier=identifier - self.showOSNotification(finalTitle, finalMessage, identifier) + if(details.notificationType == NotificationType.NewMessageWithPersonalMention and + exemptionObj[EXEMPTION_KEY_PERSONAL_MENTIONS].getStr != LSS_VALUE_NOTIF_SEND_TURN_OFF): + self.notificationCheck(title, message, details, exemptionObj[EXEMPTION_KEY_PERSONAL_MENTIONS].getStr) + return + + if(details.notificationType == NotificationType.NewMessageWithGlobalMention and + exemptionObj[EXEMPTION_KEY_GLOBAL_MENTIONS].getStr != LSS_VALUE_NOTIF_SEND_TURN_OFF): + self.notificationCheck(title, message, details, exemptionObj[EXEMPTION_KEY_GLOBAL_MENTIONS].getStr) + return + + if(details.notificationType == NotificationType.NewMessage and + exemptionObj[EXEMPTION_KEY_OTHER_MESSAGES].getStr != LSS_VALUE_NOTIF_SEND_TURN_OFF): + self.notificationCheck(title, message, details, exemptionObj[EXEMPTION_KEY_OTHER_MESSAGES].getStr) + return + + return + else: + if(details.isOneToOne or details.isGroupChat): + let exemptionObj = self.getExemptions(details.chatId) + if(exemptionObj[EXEMPTION_KEY_MUTE_ALL_MESSAGES].getBool): + return + + if(details.notificationType == NotificationType.NewMessageWithPersonalMention and + singletonInstance.localAccountSensitiveSettings.getNotifSettingPersonalMentions() != LSS_VALUE_NOTIF_SEND_TURN_OFF): + self.notificationCheck(title, message, details, singletonInstance.localAccountSensitiveSettings.getNotifSettingPersonalMentions()) + return + + if(details.notificationType == NotificationType.NewMessageWithGlobalMention and + singletonInstance.localAccountSensitiveSettings.getNotifSettingGlobalMentions() != LSS_VALUE_NOTIF_SEND_TURN_OFF): + self.notificationCheck(title, message, details, singletonInstance.localAccountSensitiveSettings.getNotifSettingGlobalMentions()) + return + + if(details.notificationType == NotificationType.NewMessage): + if(details.isOneToOne and + singletonInstance.localAccountSensitiveSettings.getNotifSettingOneToOneChats() != LSS_VALUE_NOTIF_SEND_TURN_OFF): + self.notificationCheck(title, message, details, singletonInstance.localAccountSensitiveSettings.getNotifSettingOneToOneChats()) + return + + if(details.isGroupChat and + singletonInstance.localAccountSensitiveSettings.getNotifSettingGroupChats() != LSS_VALUE_NOTIF_SEND_TURN_OFF): + self.notificationCheck(title, message, details, singletonInstance.localAccountSensitiveSettings.getNotifSettingGroupChats()) + return + + # In all other cases (TestNotification, AcceptedContactRequest, JoinCommunityRequest, MyRequestToJoinCommunityAccepted, + # MyRequestToJoinCommunityRejected) else: - let data = NotificationArgs(title: finalTitle, message: finalMessage, details: details) - debug "Add APP notification", title=finalTitle, message=finalMessage - self.events.emit(SIGNAL_DISPLAY_APP_NOTIFICATION, data) - - # Check whether to play a sound - if(singletonInstance.localAccountSensitiveSettings.getNotificationSoundsEnabled()): - let currentVolume = singletonInstance.localAccountSensitiveSettings.getVolume() * 10 - self.soundManager.setPlayerVolume(currentVolume) - self.soundManager.playSound(NOTIFICATION_SOUND) \ No newline at end of file + self.notificationCheck(title, message, details, "") \ No newline at end of file diff --git a/src/app/global/global_events.nim b/src/app/global/global_events.nim index 1f589c7efd..5a07c6825d 100644 --- a/src/app/global/global_events.nim +++ b/src/app/global/global_events.nim @@ -13,16 +13,20 @@ QtObject: new(result, delete) result.setup - proc showNormalMessageNotification*(self: GlobalEvents, title: string, message: string, sectionId: string, - chatId: string, messageId: string) {.signal.} - proc showMentionMessageNotification*(self: GlobalEvents, title: string, message: string, sectionId: string, - chatId: string, messageId: string) {.signal.} + proc showTestNotification*(self: GlobalEvents, title: string, message: string) {.signal.} + + proc showMessageNotification*(self: GlobalEvents, title: string, message: string, sectionId: string, + isCommunitySection: bool, isSectionActive: bool, chatId: string, isChatActive: bool, messageId: string, + notificationType: int, isOneToOne: bool, isGroupChat: bool) {.signal.} + proc showNewContactRequestNotification*(self: GlobalEvents, title: string, message: string, sectionId: string) {.signal.} + proc newCommunityMembershipRequestNotification*(self: GlobalEvents, title: string, message: string, sectionId: string) {.signal.} - proc myRequestToJoinCommunityHasBeenAcccepted*(self: GlobalEvents, title: string, message: string, + + proc myRequestToJoinCommunityAcccepted*(self: GlobalEvents, title: string, message: string, sectionId: string) {.signal.} - proc myRequestToJoinCommunityHasBeenRejected*(self: GlobalEvents, title: string, message: string, - sectionId: string) {.signal.} - \ No newline at end of file + + proc myRequestToJoinCommunityRejected*(self: GlobalEvents, title: string, message: string, + sectionId: string) {.signal.} \ No newline at end of file diff --git a/src/app/global/local_account_sensitive_settings.nim b/src/app/global/local_account_sensitive_settings.nim index e4edf46775..62f3109ffc 100644 --- a/src/app/global/local_account_sensitive_settings.nim +++ b/src/app/global/local_account_sensitive_settings.nim @@ -1,4 +1,4 @@ -import NimQml, os +import NimQml, os, json, chronicles import ../../constants @@ -37,17 +37,11 @@ const DEFAULT_HIDDEN_COMMUNITY_CHANNELS_AND_CATEGORIES_BANNERS = "" const LSS_KEY_HIDDEN_COMMUNITY_BACKUP_BANNERS* = "hiddenCommunityBackUpBanners" const DEFAULT_HIDDEN_COMMUNITY_BACKUP_BANNERS = "" const LSS_KEY_VOLUME* = "volume" -const DEFAULT_VOLUME = 2 -const LSS_KEY_NOTIFICATION_SETTING* = "notificationSetting" -const DEFAULT_NOTIFICATION_SETTING = 1 #notifyJustMentions from qml +const DEFAULT_VOLUME = 50 const LSS_KEY_NOTIFICATION_SOUNDS_ENABLED* = "notificationSoundsEnabled" const DEFAULT_NOTIFICATION_SOUNDS_ENABLED = true -const LSS_KEY_USE_OS_NOTIFICATIONS* = "useOSNotifications" -const DEFAULT_USE_OS_NOTIFICATIONS = true const LSS_KEY_NOTIFICATION_MESSAGE_PREVIEW_SETTING* = "notificationMessagePreviewSetting" const DEFAULT_NOTIFICATION_MESSAGE_PREVIEW_SETTING = 2 #notificationPreviewNameAndMessage from qml -const LSS_KEY_NOTIFY_ON_NEW_REQUESTS* = "notifyOnNewRequests" -const DEFAULT_NOTIFY_ON_NEW_REQUESTS = true const LSS_KEY_WITHLISTED_UNFURLING_SITES* = "whitelistedUnfurlingSites" const DEFAULT_WITHLISTED_UNFURLING_SITES = "" const LSS_KEY_NEVER_ASK_ABOUT_UNFURLING_AGAIN* = "neverAskAboutUnfurlingAgain" @@ -107,6 +101,40 @@ const DEFAULT_IS_DDMMYY_DATE_FORMAT = false const LSS_KEY_IS_24H_TIME_FORMAT* = "is_24h_time_format" const DEFAULT_IS_24H_TIME_FORMAT = false +# Exemption Keys +const EXEMPTION_KEY_MUTE_ALL_MESSAGES* = "muteAllMessages" +const EXEMPTION_KEY_PERSONAL_MENTIONS* = "personalMentions" +const EXEMPTION_KEY_GLOBAL_MENTIONS* = "globalMentions" +const EXEMPTION_KEY_OTHER_MESSAGES* = "otherMessages" + +# Exemption Values +const LSS_VALUE_NOTIF_SEND_ALERTS* = "sendAlerts" +const LSS_VALUE_NOTIF_SEND_DELIVER_QUIETLY* = "deliverQuietly" +const LSS_VALUE_NOTIF_SEND_TURN_OFF* = "turnOff" + +# Notifications +const LSS_KEY_NOTIF_SETTING_ALLOW_NOTIFICATIONS* = "allowNotifications" +const DEFAULT_NOTIF_SETTING_ALLOW_NOTIFICATIONS = true +const LSS_KEY_NOTIF_SETTING_ONE_TO_ONE_CHATS* = "notifSettingOneToOneChats" +const DEFAULT_NOTIF_SETTING_ONE_TO_ONE_CHATS = LSS_VALUE_NOTIF_SEND_ALERTS +const LSS_KEY_NOTIF_SETTING_GROUP_CHATS* = "notifSettingGroupChats" +const DEFAULT_NOTIF_SETTING_GROUP_CHATS = LSS_VALUE_NOTIF_SEND_ALERTS +const LSS_KEY_NOTIF_SETTING_PERSONAL_MENTIONS* = "notifSettingPersonalMentions" +const DEFAULT_NOTIF_SETTING_PERSONAL_MENTIONS = LSS_VALUE_NOTIF_SEND_ALERTS +const LSS_KEY_NOTIF_SETTING_GLOBAL_MENTIONS* = "notifSettingGlobalMentions" +const DEFAULT_NOTIF_SETTING_GLOBAL_MENTIONS = LSS_VALUE_NOTIF_SEND_ALERTS +const LSS_KEY_NOTIF_SETTING_ALL_MESSAGES* = "notifSettingAllMessages" +const DEFAULT_NOTIF_SETTING_ALL_MESSAGES = LSS_VALUE_NOTIF_SEND_TURN_OFF +const LSS_KEY_NOTIF_SETTING_CONTACT_REQUESTS* = "notifSettingContactRequests" +const DEFAULT_NOTIF_SETTING_CONTACT_REQUESTS = LSS_VALUE_NOTIF_SEND_ALERTS +const LSS_KEY_NOTIF_SETTING_IDENTITY_VERIF_REQUESTS* = "notifSettingIdentityVerificationRequests" +const DEFAULT_NOTIF_SETTING_IDENTITY_VERIF_REQUESTS = LSS_VALUE_NOTIF_SEND_ALERTS +const LSS_KEY_NOTIF_SETTING_EXEMPTIONS* = "notificationsExemptions" +const DEFAULT_NOTIF_SETTING_EXEMPTIONS = "" + +logScope: + topics = "la-sensitive-settings" + QtObject: type LocalAccountSensitiveSettings* = ref object of QObject settingsFileDir: string @@ -400,26 +428,12 @@ QtObject: proc setVolume*(self: LocalAccountSensitiveSettings, value: int) {.slot.} = setSettingsProp(self, LSS_KEY_VOLUME, newQVariant(value)): self.volumeChanged() - QtProperty[int] volume: read = getVolume write = setVolume notify = volumeChanged - proc notificationSettingChanged*(self: LocalAccountSensitiveSettings) {.signal.} - proc getNotificationSetting*(self: LocalAccountSensitiveSettings): int {.slot.} = - getSettingsProp[int](self, LSS_KEY_NOTIFICATION_SETTING, newQVariant(DEFAULT_NOTIFICATION_SETTING)) - proc setNotificationSetting*(self: LocalAccountSensitiveSettings, value: int) {.slot.} = - setSettingsProp(self, LSS_KEY_NOTIFICATION_SETTING, newQVariant(value)): - self.notificationSettingChanged() - - QtProperty[int] notificationSetting: - read = getNotificationSetting - write = setNotificationSetting - notify = notificationSettingChanged - - proc notificationSoundsEnabledChanged*(self: LocalAccountSensitiveSettings) {.signal.} proc getNotificationSoundsEnabled*(self: LocalAccountSensitiveSettings): bool {.slot.} = getSettingsProp[bool](self, LSS_KEY_NOTIFICATION_SOUNDS_ENABLED, newQVariant(DEFAULT_NOTIFICATION_SOUNDS_ENABLED)) @@ -433,19 +447,6 @@ QtObject: notify = notificationSoundsEnabledChanged - proc useOSNotificationsChanged*(self: LocalAccountSensitiveSettings) {.signal.} - proc getUseOSNotifications*(self: LocalAccountSensitiveSettings): bool {.slot.} = - getSettingsProp[bool](self, LSS_KEY_USE_OS_NOTIFICATIONS, newQVariant(DEFAULT_USE_OS_NOTIFICATIONS)) - proc setUseOSNotifications*(self: LocalAccountSensitiveSettings, value: bool) {.slot.} = - setSettingsProp(self, LSS_KEY_USE_OS_NOTIFICATIONS, newQVariant(value)): - self.useOSNotificationsChanged() - - QtProperty[bool] useOSNotifications: - read = getUseOSNotifications - write = setUseOSNotifications - notify = useOSNotificationsChanged - - proc notificationMessagePreviewSettingChanged*(self: LocalAccountSensitiveSettings) {.signal.} proc getNotificationMessagePreviewSetting*(self: LocalAccountSensitiveSettings): int {.slot.} = getSettingsProp[int](self, LSS_KEY_NOTIFICATION_MESSAGE_PREVIEW_SETTING, newQVariant(DEFAULT_NOTIFICATION_MESSAGE_PREVIEW_SETTING)) @@ -459,19 +460,6 @@ QtObject: notify = notificationMessagePreviewSettingChanged - proc notifyOnNewRequestsChanged*(self: LocalAccountSensitiveSettings) {.signal.} - proc getNotifyOnNewRequests*(self: LocalAccountSensitiveSettings): bool {.slot.} = - getSettingsProp[bool](self, LSS_KEY_NOTIFY_ON_NEW_REQUESTS, newQVariant(DEFAULT_NOTIFY_ON_NEW_REQUESTS)) - proc setNotifyOnNewRequests*(self: LocalAccountSensitiveSettings, value: bool) {.slot.} = - setSettingsProp(self, LSS_KEY_NOTIFY_ON_NEW_REQUESTS, newQVariant(value)): - self.notifyOnNewRequestsChanged() - - QtProperty[bool] notifyOnNewRequests: - read = getNotifyOnNewRequests - write = setNotifyOnNewRequests - notify = notifyOnNewRequestsChanged - - proc whitelistedUnfurlingSitesChanged*(self: LocalAccountSensitiveSettings) {.signal.} proc getWhitelistedUnfurlingSites*(self: LocalAccountSensitiveSettings): QVariant {.slot.} = getSettingsPropQVariant(self, LSS_KEY_WITHLISTED_UNFURLING_SITES, newQVariant(DEFAULT_WITHLISTED_UNFURLING_SITES)) @@ -847,6 +835,117 @@ QtObject: write = setIs24hTimeFormat notify = is24hTimeFormatChanged + proc notifSettingAllowNotificationsChanged*(self: LocalAccountSensitiveSettings) {.signal.} + proc getNotifSettingAllowNotifications*(self: LocalAccountSensitiveSettings): bool {.slot.} = + getSettingsProp[bool](self, LSS_KEY_NOTIF_SETTING_ALLOW_NOTIFICATIONS, newQVariant(DEFAULT_NOTIF_SETTING_ALLOW_NOTIFICATIONS)) + proc setNotifSettingAllowNotifications*(self: LocalAccountSensitiveSettings, value: bool) {.slot.} = + setSettingsProp(self, LSS_KEY_NOTIF_SETTING_ALLOW_NOTIFICATIONS, newQVariant(value)): + self.notifSettingAllowNotificationsChanged() + QtProperty[bool] notifSettingAllowNotifications: + read = getNotifSettingAllowNotifications + write = setNotifSettingAllowNotifications + notify = notifSettingAllowNotificationsChanged + + proc notifSettingOneToOneChatsChanged*(self: LocalAccountSensitiveSettings) {.signal.} + proc getNotifSettingOneToOneChats*(self: LocalAccountSensitiveSettings): string {.slot.} = + getSettingsProp[string](self, LSS_KEY_NOTIF_SETTING_ONE_TO_ONE_CHATS, newQVariant(DEFAULT_NOTIF_SETTING_ONE_TO_ONE_CHATS)) + proc setNotifSettingOneToOneChats*(self: LocalAccountSensitiveSettings, value: string) {.slot.} = + setSettingsProp(self, LSS_KEY_NOTIF_SETTING_ONE_TO_ONE_CHATS, newQVariant(value)): + self.notifSettingOneToOneChatsChanged() + QtProperty[string] notifSettingOneToOneChats: + read = getNotifSettingOneToOneChats + write = setNotifSettingOneToOneChats + notify = notifSettingOneToOneChatsChanged + + proc notifSettingGroupChatsChanged*(self: LocalAccountSensitiveSettings) {.signal.} + proc getNotifSettingGroupChats*(self: LocalAccountSensitiveSettings): string {.slot.} = + getSettingsProp[string](self, LSS_KEY_NOTIF_SETTING_GROUP_CHATS, newQVariant(DEFAULT_NOTIF_SETTING_GROUP_CHATS)) + proc setNotifSettingGroupChats*(self: LocalAccountSensitiveSettings, value: string) {.slot.} = + setSettingsProp(self, LSS_KEY_NOTIF_SETTING_GROUP_CHATS, newQVariant(value)): + self.notifSettingGroupChatsChanged() + QtProperty[string] notifSettingGroupChats: + read = getNotifSettingGroupChats + write = setNotifSettingGroupChats + notify = notifSettingGroupChatsChanged + + proc notifSettingPersonalMentionsChanged*(self: LocalAccountSensitiveSettings) {.signal.} + proc getNotifSettingPersonalMentions*(self: LocalAccountSensitiveSettings): string {.slot.} = + getSettingsProp[string](self, LSS_KEY_NOTIF_SETTING_PERSONAL_MENTIONS, newQVariant(DEFAULT_NOTIF_SETTING_PERSONAL_MENTIONS)) + proc setNotifSettingPersonalMentions*(self: LocalAccountSensitiveSettings, value: string) {.slot.} = + setSettingsProp(self, LSS_KEY_NOTIF_SETTING_PERSONAL_MENTIONS, newQVariant(value)): + self.notifSettingPersonalMentionsChanged() + QtProperty[string] notifSettingPersonalMentions: + read = getNotifSettingPersonalMentions + write = setNotifSettingPersonalMentions + notify = notifSettingPersonalMentionsChanged + + proc notifSettingGlobalMentionsChanged*(self: LocalAccountSensitiveSettings) {.signal.} + proc getNotifSettingGlobalMentions*(self: LocalAccountSensitiveSettings): string {.slot.} = + getSettingsProp[string](self, LSS_KEY_NOTIF_SETTING_GLOBAL_MENTIONS, newQVariant(DEFAULT_NOTIF_SETTING_GLOBAL_MENTIONS)) + proc setNotifSettingGlobalMentions*(self: LocalAccountSensitiveSettings, value: string) {.slot.} = + setSettingsProp(self, LSS_KEY_NOTIF_SETTING_GLOBAL_MENTIONS, newQVariant(value)): + self.notifSettingGlobalMentionsChanged() + QtProperty[string] notifSettingGlobalMentions: + read = getNotifSettingGlobalMentions + write = setNotifSettingGlobalMentions + notify = notifSettingGlobalMentionsChanged + + proc notifSettingAllMessagesChanged*(self: LocalAccountSensitiveSettings) {.signal.} + proc getNotifSettingAllMessages*(self: LocalAccountSensitiveSettings): string {.slot.} = + getSettingsProp[string](self, LSS_KEY_NOTIF_SETTING_ALL_MESSAGES, newQVariant(DEFAULT_NOTIF_SETTING_ALL_MESSAGES)) + proc setNotifSettingAllMessages*(self: LocalAccountSensitiveSettings, value: string) {.slot.} = + setSettingsProp(self, LSS_KEY_NOTIF_SETTING_ALL_MESSAGES, newQVariant(value)): + self.notifSettingAllMessagesChanged() + QtProperty[string] notifSettingAllMessages: + read = getNotifSettingAllMessages + write = setNotifSettingAllMessages + notify = notifSettingAllMessagesChanged + + proc notifSettingContactRequestsChanged*(self: LocalAccountSensitiveSettings) {.signal.} + proc getNotifSettingContactRequests*(self: LocalAccountSensitiveSettings): string {.slot.} = + getSettingsProp[string](self, LSS_KEY_NOTIF_SETTING_CONTACT_REQUESTS, newQVariant(DEFAULT_NOTIF_SETTING_CONTACT_REQUESTS)) + proc setNotifSettingContactRequests*(self: LocalAccountSensitiveSettings, value: string) {.slot.} = + setSettingsProp(self, LSS_KEY_NOTIF_SETTING_CONTACT_REQUESTS, newQVariant(value)): + self.notifSettingContactRequestsChanged() + QtProperty[string] notifSettingContactRequests: + read = getNotifSettingContactRequests + write = setNotifSettingContactRequests + notify = notifSettingContactRequestsChanged + + proc notifSettingIdentityVerificationRequestsChanged*(self: LocalAccountSensitiveSettings) {.signal.} + proc getNotifSettingIdentityVerificationRequests*(self: LocalAccountSensitiveSettings): string {.slot.} = + getSettingsProp[string](self, LSS_KEY_NOTIF_SETTING_IDENTITY_VERIF_REQUESTS, newQVariant(DEFAULT_NOTIF_SETTING_IDENTITY_VERIF_REQUESTS)) + proc setNotifSettingIdentityVerificationRequests*(self: LocalAccountSensitiveSettings, value: string) {.slot.} = + setSettingsProp(self, LSS_KEY_NOTIF_SETTING_IDENTITY_VERIF_REQUESTS, newQVariant(value)): + self.notifSettingIdentityVerificationRequestsChanged() + QtProperty[string] notifSettingIdentityVerificationRequests: + read = getNotifSettingIdentityVerificationRequests + write = setNotifSettingIdentityVerificationRequests + notify = notifSettingIdentityVerificationRequestsChanged + + proc notifSettingExemptionsChanged*(self: LocalAccountSensitiveSettings) {.signal.} + proc getNotifSettingExemptions*(self: LocalAccountSensitiveSettings): string {.slot.} = + getSettingsProp[string](self, LSS_KEY_NOTIF_SETTING_EXEMPTIONS, newQVariant(DEFAULT_NOTIF_SETTING_EXEMPTIONS)) + proc setNotifSettingExemptions*(self: LocalAccountSensitiveSettings, value: string) {.slot.} = + setSettingsProp(self, LSS_KEY_NOTIF_SETTING_EXEMPTIONS, newQVariant(value)): + self.notifSettingExemptionsChanged() + QtProperty[string] notifSettingExemptions: + read = getNotifSettingExemptions + write = setNotifSettingExemptions + notify = notifSettingExemptionsChanged + + proc getNotifSettingExemptionsAsJson*(self: LocalAccountSensitiveSettings): JsonNode = + var allExemptions = newJObject() + let exemptions = self.getNotifSettingExemptions() + if(exemptions.len == 0): + return allExemptions + try: + let exemptionsObj = exemptions.parseJson + allExemptions = exemptionsObj + except Exception as e: + error "stored exemptions are corrupted, will be overwritten", msg=e.msg + return allExemptions + proc removeKey*(self: LocalAccountSensitiveSettings, key: string) = if(self.settings.isNil): return @@ -873,11 +972,8 @@ QtObject: of LSS_KEY_HIDDEN_COMMUNITY_CHANNELS_AND_CATEGORIES_BANNERS: self.hiddenCommunityChannelAndCategoriesBannersChanged() of LSS_KEY_HIDDEN_COMMUNITY_BACKUP_BANNERS: self.hiddenCommunityBackUpBannersChanged() of LSS_KEY_VOLUME: self.volumeChanged() - of LSS_KEY_NOTIFICATION_SETTING: self.notificationSettingChanged() of LSS_KEY_NOTIFICATION_SOUNDS_ENABLED: self.notificationSoundsEnabledChanged() - of LSS_KEY_USE_OS_NOTIFICATIONS: self.useOSNotificationsChanged() of LSS_KEY_NOTIFICATION_MESSAGE_PREVIEW_SETTING: self.notificationMessagePreviewSettingChanged() - of LSS_KEY_NOTIFY_ON_NEW_REQUESTS: self.notifyOnNewRequestsChanged() of LSS_KEY_WITHLISTED_UNFURLING_SITES: self.whitelistedUnfurlingSitesChanged() of LSS_KEY_NEVER_ASK_ABOUT_UNFURLING_AGAIN: self.neverAskAboutUnfurlingAgainChanged() of LSS_KEY_HIDE_CHANNEL_SUGGESTIONS: self.hideChannelSuggestionsChanged() @@ -907,4 +1003,12 @@ QtObject: of LSS_KEY_STICKERS_ENS_ROPSTEN: self.stickersEnsRopstenChanged() of LSS_KEY_IS_DDMMYY_DATE_FORMAT: self.isDDMMYYDateFormatChanged() of LSS_KEY_IS_24H_TIME_FORMAT: self.is24hTimeFormatChanged() - + of LSS_KEY_NOTIF_SETTING_ALLOW_NOTIFICATIONS: self.notifSettingAllowNotificationsChanged() + of LSS_KEY_NOTIF_SETTING_ONE_TO_ONE_CHATS: self.notifSettingOneToOneChatsChanged() + of LSS_KEY_NOTIF_SETTING_GROUP_CHATS: self.notifSettingGroupChatsChanged() + of LSS_KEY_NOTIF_SETTING_PERSONAL_MENTIONS: self.notifSettingPersonalMentionsChanged() + of LSS_KEY_NOTIF_SETTING_GLOBAL_MENTIONS: self.notifSettingGlobalMentionsChanged() + of LSS_KEY_NOTIF_SETTING_ALL_MESSAGES: self.notifSettingAllMessagesChanged() + of LSS_KEY_NOTIF_SETTING_CONTACT_REQUESTS: self.notifSettingContactRequestsChanged() + of LSS_KEY_NOTIF_SETTING_IDENTITY_VERIF_REQUESTS: self.notifSettingIdentityVerificationRequestsChanged() + of LSS_KEY_NOTIF_SETTING_EXEMPTIONS: self.notifSettingExemptionsChanged() \ No newline at end of file diff --git a/src/app/modules/main/chat_section/controller.nim b/src/app/modules/main/chat_section/controller.nim index b448394ec6..12b36b7154 100644 --- a/src/app/modules/main/chat_section/controller.nim +++ b/src/app/modules/main/chat_section/controller.nim @@ -63,11 +63,10 @@ proc getActiveChatId*(self: Controller): string = proc init*(self: Controller) = self.events.on(SIGNAL_NEW_MESSAGE_RECEIVED) do(e: Args): let args = MessagesArgs(e) - if(self.isCommunitySection and args.chatType != ChatType.CommunityChat or - not self.isCommunitySection and args.chatType == ChatType.CommunityChat): - return - self.delegate.onNewMessagesReceived(args.chatId, args.unviewedMessagesCount, args.unviewedMentionsCount, - args.messages) + if (self.sectionId != args.sectionId or args.messages.len == 0): + return + self.delegate.onNewMessagesReceived(args.sectionId, args.chatId, args.chatType, args.unviewedMessagesCount, + args.unviewedMentionsCount, args.messages[0]) self.events.on(message_service.SIGNAL_MENTIONED_IN_EDITED_MESSAGE) do(e: Args): let args = MessageEditedArgs(e) diff --git a/src/app/modules/main/chat_section/io_interface.nim b/src/app/modules/main/chat_section/io_interface.nim index 42bcb370b4..7ff8a0b9a6 100644 --- a/src/app/modules/main/chat_section/io_interface.nim +++ b/src/app/modules/main/chat_section/io_interface.nim @@ -81,8 +81,8 @@ method addChatIfDontExist*(self: AccessInterface, setChatAsActive: bool = true) {.base.} = raise newException(ValueError, "No implementation available") -method onNewMessagesReceived*(self: AccessInterface, chatId: string, unviewedMessagesCount: int, - unviewedMentionsCount: int, messages: seq[MessageDto]) {.base.} = +method onNewMessagesReceived*(self: AccessInterface, sectionIdMsgBelongsTo: string, chatIdMsgBelongsTo: string, + chatTypeMsgBelongsTo: ChatType, unviewedMessagesCount: int, unviewedMentionsCount: int, message: MessageDto) {.base.} = raise newException(ValueError, "No implementation available") method onChatMuted*(self: AccessInterface, chatId: string) {.base.} = diff --git a/src/app/modules/main/chat_section/module.nim b/src/app/modules/main/chat_section/module.nim index 3443864bdb..1b5de93895 100644 --- a/src/app/modules/main/chat_section/module.nim +++ b/src/app/modules/main/chat_section/module.nim @@ -12,6 +12,7 @@ import chat_content/module as chat_content_module import ../../../global/app_sections_config as conf import ../../../global/global_singleton import ../../../core/eventemitter +import ../../../core/notifications/details as notification_details import ../../../../app_service/service/settings/service as settings_service import ../../../../app_service/service/contacts/service as contact_service import ../../../../app_service/service/chat/service as chat_service @@ -636,25 +637,29 @@ method onContactDetailsUpdated*(self: Module, publicKey: string) = let chatImage = contactDetails.icon self.view.chatsModel().updateItemDetails(publicKey, chatName, chatImage) -method onNewMessagesReceived*(self: Module, chatId: string, unviewedMessagesCount: int, unviewedMentionsCount: int, - messages: seq[MessageDto]) = - if(self.controller.getMySectionId() != self.delegate.getActiveSectionId() or - self.controller.getActiveChatId() != chatId): +method onNewMessagesReceived*(self: Module, sectionIdMsgBelongsTo: string, chatIdMsgBelongsTo: string, + chatTypeMsgBelongsTo: ChatType, unviewedMessagesCount: int, unviewedMentionsCount: int, message: MessageDto) = + let messageBelongsToActiveSection = sectionIdMsgBelongsTo == self.controller.getMySectionId() and + self.controller.getMySectionId() == self.delegate.getActiveSectionId() + let messageBelongsToActiveChat = self.controller.getActiveChatId() == chatIdMsgBelongsTo + if(not messageBelongsToActiveSection or not messageBelongsToActiveChat): let hasUnreadMessages = unviewedMessagesCount > 0 - self.updateBadgeNotifications(chatId, hasUnreadMessages, unviewedMentionsCount) + self.updateBadgeNotifications(chatIdMsgBelongsTo, hasUnreadMessages, unviewedMentionsCount) - # Prepare bubble notification + # Prepare notification let myPK = singletonInstance.userProfile.getPubKey() - for m in messages: - let contactDetails = self.controller.getContactDetails(m.`from`) - let renderedMessageText = self.controller.getRenderedText(m.parsedText) - let plainText = singletonInstance.utils.plainText(renderedMessageText) - if(m.isUserWithPkMentioned(myPK)): - singletonInstance.globalEvents.showMentionMessageNotification(contactDetails.displayName, plainText, - self.controller.getMySectionId(), chatId, m.id) - else: - singletonInstance.globalEvents.showNormalMessageNotification(contactDetails.displayName, plainText, - self.controller.getMySectionId(), chatId, m.id) + var notificationType = notification_details.NotificationType.NewMessage + if(message.isPersonalMention(myPK)): + notificationType = notification_details.NotificationType.NewMessageWithPersonalMention + elif(message.isGlobalMention()): + notificationType = notification_details.NotificationType.NewMessageWithGlobalMention + let contactDetails = self.controller.getContactDetails(message.`from`) + let renderedMessageText = self.controller.getRenderedText(message.parsedText) + let plainText = singletonInstance.utils.plainText(renderedMessageText) + singletonInstance.globalEvents.showMessageNotification(contactDetails.displayName, plainText, sectionIdMsgBelongsTo, + self.controller.isCommunity(), messageBelongsToActiveSection, chatIdMsgBelongsTo, messageBelongsToActiveChat, + message.id, notificationType.int, + chatTypeMsgBelongsTo == ChatType.OneToOne, chatTypeMsgBelongsTo == ChatType.PrivateGroupChat) method onMeMentionedInEditedMessage*(self: Module, chatId: string, editedMessage : MessageDto) = if((editedMessage.communityId.len == 0 and diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index e7ba698e31..354f9725c8 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -56,6 +56,8 @@ import ../../core/eventemitter export io_interface +const COMMUNITY_PERMISSION_ACCESS_ON_REQUEST = 3 + type Module*[T: io_interface.DelegateInterface] = ref object of io_interface.AccessInterface delegate: T @@ -588,6 +590,12 @@ method communityJoined*[T]( else: self.view.model().addItem(communitySectionItem) + if(community.permissions.access == COMMUNITY_PERMISSION_ACCESS_ON_REQUEST and + community.requestedToJoinAt > 0 and + community.joined): + singletonInstance.globalEvents.myRequestToJoinCommunityAcccepted("Community Request Accepted", + fmt "Your request to join community {community.name} is accepted", community.id) + if setActive: self.setActiveSection(communitySectionItem) diff --git a/src/app/modules/main/profile_section/devices/model.nim b/src/app/modules/main/profile_section/devices/model.nim index 25cce9d103..001e21e9c6 100644 --- a/src/app/modules/main/profile_section/devices/model.nim +++ b/src/app/modules/main/profile_section/devices/model.nim @@ -23,6 +23,13 @@ QtObject: new(result, delete) result.setup + proc countChanged(self: Model) {.signal.} + proc getCount(self: Model): int {.slot.} = + self.items.len + QtProperty[int] count: + read = getCount + notify = countChanged + method rowCount(self: Model, index: QModelIndex = nil): int = return self.items.len @@ -64,6 +71,7 @@ QtObject: self.beginInsertRows(parentModelIndex, first, last) self.items.add(items) self.endInsertRows() + self.countChanged() proc addItem*(self: Model, item: Item) = let parentModelIndex = newQModelIndex() @@ -72,6 +80,7 @@ QtObject: self.beginInsertRows(parentModelIndex, self.items.len, self.items.len) self.items.add(item) self.endInsertRows() + self.countChanged() proc findIndexByInstallationId(self: Model, installationId: string): int = for i in 0.. 0): + return + self.delegate.addCommunity(args.community) + + self.events.on(SIGNAL_COMMUNITY_CREATED) do(e:Args): + let args = CommunityArgs(e) + if(args.error.len > 0): + return + self.delegate.addCommunity(args.community) + + self.events.on(SIGNAL_COMMUNITY_IMPORTED) do(e:Args): + let args = CommunityArgs(e) + if(args.error.len > 0): + return + self.delegate.addCommunity(args.community) + + self.events.on(SIGNAL_COMMUNITY_LEFT) do(e:Args): + let args = CommunityIdArgs(e) + self.delegate.removeItemWithId(args.communityId) + + self.events.on(SIGNAL_COMMUNITY_EDITED) do(e:Args): + let args = CommunityArgs(e) + self.delegate.editCommunity(args.community) + + self.events.on(SIGNAL_COMMUNITIES_UPDATE) do(e:Args): + let args = CommunitiesArgs(e) + for community in args.communities: + self.delegate.editCommunity(community) + + self.events.on(chat_service.SIGNAL_CHAT_ADDED_OR_UPDATED) do(e: Args): let args = chat_service.ChatArgs(e) - self.delegate.onChatUnmuted(args.chatId) + self.delegate.addChat(args.chatId) self.events.on(chat_service.SIGNAL_CHAT_LEFT) do(e: Args): let args = chat_service.ChatArgs(e) - self.delegate.onChatLeft(args.chatId) + self.delegate.removeItemWithId(args.chatId) - ## We need to add leave community handler here, once we have appropriate signal in place + self.events.on(SIGNAL_CHAT_UPDATE) do(e: Args): + var args = ChatUpdateArgsNew(e) + for chat in args.chats: + let belongsToCommunity = chat.communityId.len > 0 + self.delegate.addChat(chat) -proc getAllChats*(self: Controller): seq[ChatDto] = - return self.chatService.getAllChats() + self.events.on(SIGNAL_CHAT_RENAMED) do(e: Args): + var args = ChatRenameArgs(e) + self.delegate.setName(args.id, args.newName) + + self.events.on(SIGNAL_CHAT_SWITCH_TO_OR_CREATE_1_1_CHAT) do(e:Args): + let args = ChatExtArgs(e) + self.delegate.addChat(args.chatId) + +proc getChannelGroups*(self: Controller): seq[ChannelGroupDto] = + return self.chatService.getChannelGroups() proc getChatDetails*(self: Controller, chatId: string): ChatDto = return self.chatService.getChatById(chatId) - -proc getOneToOneChatNameAndImage*(self: Controller, chatId: string): - tuple[name: string, image: string] = - return self.chatService.getOneToOneChatNameAndImage(chatId) - -proc unmuteChat*(self: Controller, chatId: string) = - self.chatService.unmuteChat(chatId) + +proc getContactDetails*(self: Controller, id: string): ContactDetails = + return self.contactService.getContactDetails(id) \ No newline at end of file diff --git a/src/app/modules/main/profile_section/notifications/io_interface.nim b/src/app/modules/main/profile_section/notifications/io_interface.nim index 75921fa8f4..d50353b72d 100644 --- a/src/app/modules/main/profile_section/notifications/io_interface.nim +++ b/src/app/modules/main/profile_section/notifications/io_interface.nim @@ -1,4 +1,7 @@ import NimQml +import ../../../../global/app_signals +from ../../../../../app_service/service/community/dto/community import CommunityDto +from ../../../../../app_service/service/chat/dto/chat import ChatDto type AccessInterface* {.pure inheritable.} = ref object of RootObj @@ -9,23 +12,39 @@ method delete*(self: AccessInterface) {.base.} = method load*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") +method viewDidLoad*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + method isLoaded*(self: AccessInterface): bool {.base.} = raise newException(ValueError, "No implementation available") +method sendTestNotification*(self: AccessInterface, title: string, message: string) {.base.} = + raise newException(ValueError, "No implementation available") + method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} = raise newException(ValueError, "No implementation available") -method onChatMuted*(self: AccessInterface, chatId: string) {.base.} = +method saveExemptions*(self: AccessInterface, itemId: string, muteAllMessages: bool, personalMentions: string, + globalMentions: string, allMessages: string) {.base.} = raise newException(ValueError, "No implementation available") -method onChatUnmuted*(self: AccessInterface, chatId: string) {.base.} = +method onToggleSection*(self: AccessInterface, sectionType: SectionType) {.base.} = raise newException(ValueError, "No implementation available") -method onChatLeft*(self: AccessInterface, chatId: string) {.base.} = +method addCommunity*(self: AccessInterface, communityDto: CommunityDto) {.base.} = raise newException(ValueError, "No implementation available") -method viewDidLoad*(self: AccessInterface) {.base.} = +method editCommunity*(self: AccessInterface, communityDto: CommunityDto) {.base.} = raise newException(ValueError, "No implementation available") -method unmuteChat*(self: AccessInterface, chatId: string) {.base.} = +method removeItemWithId*(self: AccessInterface, itemId: string) {.base.} = raise newException(ValueError, "No implementation available") + +method addChat*(self: AccessInterface, chatDto: ChatDto) {.base.} = + raise newException(ValueError, "No implementation available") + +method addChat*(self: AccessInterface, itemId: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method setName*(self: AccessInterface, itemId: string, name: string) {.base.} = + raise newException(ValueError, "No implementation available") \ No newline at end of file diff --git a/src/app/modules/main/profile_section/notifications/item.nim b/src/app/modules/main/profile_section/notifications/item.nim index fcc6bbc033..2057410929 100644 --- a/src/app/modules/main/profile_section/notifications/item.nim +++ b/src/app/modules/main/profile_section/notifications/item.nim @@ -1,25 +1,86 @@ +import ../../../../global/local_account_sensitive_settings + type - Item* = object + Type* {.pure.} = enum + Community + OneToOneChat + GroupChat + +type + Item* = ref object id: string name: string - icon: string + image: string color: string + joinedTimestamp: int64 + itemType: Type + muteAllMessages: bool + personalMentions: string + globalMentions: string + otherMessages: string -proc initItem*(id, name, icon: string, color: string): Item = +proc initItem*(id, name, image, color: string, joinedTimestamp: int64, itemType: Type, muteAllMessages = false, + personalMentions = LSS_VALUE_NOTIF_SEND_ALERTS, globalMentions = LSS_VALUE_NOTIF_SEND_ALERTS, + otherMessages = LSS_VALUE_NOTIF_SEND_TURN_OFF): Item = result = Item() result.id = id result.name = name - result.icon = icon + result.image = image result.color = color + result.joinedTimestamp = joinedTimestamp + result.itemType = itemType + result.muteAllMessages = muteAllMessages + result.personalMentions = personalMentions + result.globalMentions = globalMentions + result.otherMessages = otherMessages proc id*(self: Item): string = - self.id + return self.id proc name*(self: Item): string = - self.name + return self.name -proc icon*(self: Item): string = - self.icon +proc `name=`*(self: Item, value: string) = + self.name = value + +proc image*(self: Item): string = + return self.image proc color*(self: Item): string = self.color + +proc joinedTimestamp*(self: Item): int64 = + return self.joinedTimestamp + +proc itemType*(self: Item): Type = + return self.itemType + +proc customized*(self: Item): bool = + return self.muteAllMessages or + self.personalMentions != LSS_VALUE_NOTIF_SEND_ALERTS or + self.globalMentions != LSS_VALUE_NOTIF_SEND_ALERTS or + self.otherMessages != LSS_VALUE_NOTIF_SEND_TURN_OFF + +proc muteAllMessages*(self: Item): bool = + return self.muteAllMessages + +proc `muteAllMessages=`*(self: Item, value: bool) = + self.muteAllMessages = value + +proc personalMentions*(self: Item): string = + return self.personalMentions + +proc `personalMentions=`*(self: Item, value: string) = + self.personalMentions = value + +proc globalMentions*(self: Item): string = + return self.globalMentions + +proc `globalMentions=`*(self: Item, value: string) = + self.globalMentions = value + +proc otherMessages*(self: Item): string = + return self.otherMessages + +proc `otherMessages=`*(self: Item, value: string) = + self.otherMessages = value \ No newline at end of file diff --git a/src/app/modules/main/profile_section/notifications/model.nim b/src/app/modules/main/profile_section/notifications/model.nim index 9b848bd3bc..19d54f65c0 100644 --- a/src/app/modules/main/profile_section/notifications/model.nim +++ b/src/app/modules/main/profile_section/notifications/model.nim @@ -1,13 +1,20 @@ import NimQml, Tables - import item +import ../../../../global/local_account_sensitive_settings + type ModelRole {.pure.} = enum Id = UserRole + 1 Name - Icon + Image Color + Type + Customized + MuteAllMessages + PersonalMentions + GlobalMentions + OtherMessages QtObject: type @@ -26,10 +33,8 @@ QtObject: result.setup proc countChanged(self: Model) {.signal.} - proc getCount(self: Model): int {.slot.} = self.items.len - QtProperty[int] count: read = getCount notify = countChanged @@ -41,8 +46,14 @@ QtObject: { ModelRole.Id.int:"itemId", ModelRole.Name.int:"name", - ModelRole.Icon.int:"icon", - ModelRole.Color.int:"color" + ModelRole.Image.int:"image", + ModelRole.Color.int:"color", + ModelRole.Type.int:"type", + ModelRole.Customized.int:"customized", + ModelRole.MuteAllMessages.int:"muteAllMessages", + ModelRole.PersonalMentions.int:"personalMentions", + ModelRole.GlobalMentions.int:"globalMentions", + ModelRole.OtherMessages.int:"otherMessages" }.toTable method data(self: Model, index: QModelIndex, role: int): QVariant = @@ -60,39 +71,107 @@ QtObject: result = newQVariant(item.id) of ModelRole.Name: result = newQVariant(item.name) - of ModelRole.Icon: - result = newQVariant(item.icon) + of ModelRole.Image: + result = newQVariant(item.image) of ModelRole.Color: result = newQVariant(item.color) + of ModelRole.Type: + result = newQVariant(item.itemType.int) + of ModelRole.Customized: + result = newQVariant(item.customized) + of ModelRole.MuteAllMessages: + result = newQVariant(item.muteAllMessages) + of ModelRole.PersonalMentions: + result = newQVariant(item.personalMentions) + of ModelRole.GlobalMentions: + result = newQVariant(item.globalMentions) + of ModelRole.OtherMessages: + result = newQVariant(item.otherMessages) proc addItem*(self: Model, item: Item) = + # add most recent item on top + var position = -1 + for i in 0 ..< self.items.len: + if(item.joinedTimestamp >= self.items[i].joinedTimestamp): + position = i + break + + if(position == -1): + position = self.items.len + let parentModelIndex = newQModelIndex() defer: parentModelIndex.delete - self.beginInsertRows(parentModelIndex, self.items.len, self.items.len) - self.items.add(item) + self.beginInsertRows(parentModelIndex, position, position) + self.items.insert(item, position) self.endInsertRows() - self.countChanged() - proc getItemIdxById*(self: Model, id: string): int = - var idx = 0 - for it in self.items: - if(it.id == id): - return idx - idx.inc - return -1 - - proc removeItemById*(self: Model, id: string) = - let idx = self.getItemIdxById(id) - if idx == -1: + proc setItems*(self: Model, items: seq[Item]) = + if(items.len == 0): return let parentModelIndex = newQModelIndex() defer: parentModelIndex.delete - self.beginRemoveRows(parentModelIndex, idx, idx) - self.items.delete(idx) + self.beginInsertRows(parentModelIndex, 0, items.len - 1) + self.items = items + self.endInsertRows() + self.countChanged() + + proc findIndexForItemId*(self: Model, id: string): int = + var ind = 0 + for it in self.items: + if(it.id == id): + return ind + ind.inc + return -1 + + proc removeItemById*(self: Model, id: string) = + let ind = self.findIndexForItemId(id) + if(ind == -1): + return + + let parentModelIndex = newQModelIndex() + defer: parentModelIndex.delete + + self.beginRemoveRows(parentModelIndex, ind, ind) + self.items.delete(ind) self.endRemoveRows() self.countChanged() + + proc removeItemsByType*(self: Model, itemType: Type) = + let items = self.items + for i in items: + if(i.itemType == itemType): + self.removeItemById(i.id) + + iterator modelIterator*(self: Model): Item = + for i in 0 ..< self.items.len: + yield self.items[i] + + proc updateExemptions*(self: Model, id: string, muteAllMessages = false, personalMentions = LSS_VALUE_NOTIF_SEND_ALERTS, + globalMentions = LSS_VALUE_NOTIF_SEND_ALERTS, otherMessages = LSS_VALUE_NOTIF_SEND_TURN_OFF) = + let ind = self.findIndexForItemId(id) + if(ind == -1): + return + + self.items[ind].muteAllMessages = muteAllMessages + self.items[ind].personalMentions = personalMentions + self.items[ind].globalMentions = globalMentions + self.items[ind].otherMessages = otherMessages + + let index = self.createIndex(ind, 0, nil) + self.dataChanged(index, index, @[ModelRole.MuteAllMessages.int, ModelRole.PersonalMentions.int, + ModelRole.GlobalMentions.int, ModelRole.OtherMessages.int, ModelRole.Customized.int]) + + proc updateName*(self: Model, id: string, name: string) = + let ind = self.findIndexForItemId(id) + if(ind == -1): + return + + self.items[ind].name = name + + let index = self.createIndex(ind, 0, nil) + self.dataChanged(index, index, @[ModelRole.Name.int]) \ No newline at end of file diff --git a/src/app/modules/main/profile_section/notifications/module.nim b/src/app/modules/main/profile_section/notifications/module.nim index 234477aa1c..97a284ce99 100644 --- a/src/app/modules/main/profile_section/notifications/module.nim +++ b/src/app/modules/main/profile_section/notifications/module.nim @@ -1,10 +1,14 @@ -import NimQml, chronicles +import NimQml, algorithm, json, chronicles import io_interface import ../io_interface as delegate_interface import view, controller, model, item +import ../../../../global/app_signals +import ../../../../global/global_singleton import ../../../../core/eventemitter import ../../../../../app_service/service/chat/service as chat_service +import ../../../../../app_service/service/contacts/service as contact_service +from ../../../../../app_service/service/community/dto/community import CommunityDto export io_interface @@ -21,12 +25,13 @@ type proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter, - chatService: chat_service.Service): Module = + chatService: chat_service.Service, + contactService: contact_service.Service): Module = result = Module() result.delegate = delegate result.view = view.newView(result) result.viewVariant = newQVariant(result.view) - result.controller = controller.newController(result, events, chatService) + result.controller = controller.newController(result, events, chatService, contactService) result.moduleLoaded = false method delete*(self: Module) = @@ -34,6 +39,11 @@ method delete*(self: Module) = self.viewVariant.delete self.controller.delete +proc comp[T](x,y: T):int = + if x.joinedTimestamp > y.joinedTimestamp: return 1 + elif x.joinedTimestamp < y.joinedTimestamp: return -1 + else: return 0 + method load*(self: Module) = self.controller.init() self.view.load() @@ -41,19 +51,52 @@ method load*(self: Module) = method isLoaded*(self: Module): bool = return self.moduleLoaded -proc initModel(self: Module) = - let chats = self.controller.getAllChats() - for c in chats: - if(not c.muted): - continue +proc createItem(self: Module, id, name, image, color: string, joinedTimestamp: int64, itemType: Type): Item = + let allExemptions = singletonInstance.localAccountSensitiveSettings.getNotifSettingExemptionsAsJson() + var item = initItem(id, name, image, color, joinedTimestamp, itemType) + if(allExemptions.contains(id)): + let obj = allExemptions[id] + if(obj.contains(EXEMPTION_KEY_MUTE_ALL_MESSAGES)): + item.muteAllMessages = obj[EXEMPTION_KEY_MUTE_ALL_MESSAGES].getBool + if(obj.contains(EXEMPTION_KEY_PERSONAL_MENTIONS)): + item.personalMentions = obj[EXEMPTION_KEY_PERSONAL_MENTIONS].getStr + if(obj.contains(EXEMPTION_KEY_GLOBAL_MENTIONS)): + item.globalMentions = obj[EXEMPTION_KEY_GLOBAL_MENTIONS].getStr + if(obj.contains(EXEMPTION_KEY_OTHER_MESSAGES)): + item.otherMessages = obj[EXEMPTION_KEY_OTHER_MESSAGES].getStr + return item - if(c.chatType == ChatType.OneToOne): - let (chatName, chatImage) = self.controller.getOneToOneChatNameAndImage(c.id) - let item = initItem(c.id, chatName, chatImage, c.color) - self.view.mutedContactsModel().addItem(item) - else: - let item = initItem(c.id, c.name, c.icon, c.color) - self.view.mutedChatsModel().addItem(item) +proc createChatItem(self: Module, chatDto: ChatDto): Item = + var chatName = chatDto.name + var chatImage = chatDto.icon + var itemType = item.Type.GroupChat + if(chatDto.chatType == ChatType.OneToOne): + let contactDetails = self.controller.getContactDetails(chatDto.id) + chatName = contactDetails.displayName + chatImage = contactDetails.icon + itemType = item.Type.OneToOneChat + + return self.createItem(chatDto.id, chatName, chatImage, chatDto.color, chatDto.joined, itemType) + +proc initModel(self: Module) = + let channelGroups = self.controller.getChannelGroups() + var items: seq[Item] + for cg in channelGroups: + if cg.channelGroupType == ChannelGroupType.Community: + if(not singletonInstance.localAccountSensitiveSettings.getCommunitiesEnabled()): + continue + let item = self.createItem(cg.id, cg.name, cg.images.thumbnail, cg.color, joinedTimestamp = 0, item.Type.Community) + items.add(item) + elif cg.channelGroupType == ChannelGroupType.Personal: + for c in cg.chats: + if c.chatType != ChatType.OneToOne and c.chatType != ChatType.PrivateGroupChat: + continue + let item = self.createChatItem(c) + items.add(item) + + # Sort to get most recent first + items.sort(comp, SortOrder.Descending) + self.view.exemptionsModel().setItems(items) method viewDidLoad*(self: Module) = self.initModel() @@ -63,23 +106,72 @@ method viewDidLoad*(self: Module) = method getModuleAsVariant*(self: Module): QVariant = return self.viewVariant -method unmuteChat*(self: Module, chatId: string) = - self.controller.unmuteChat(chatId) +method sendTestNotification*(self: Module, title: string, message: string) = + singletonInstance.globalEvents.showTestNotification(title, message) -method onChatMuted*(self: Module, chatId: string) = - let chat = self.controller.getChatDetails(chatId) - if(chat.chatType == ChatType.OneToOne): - let (chatName, chatImage) = self.controller.getOneToOneChatNameAndImage(chat.id) - let item = initItem(chat.id, chatName, chatImage, chat.color) - self.view.mutedContactsModel().addItem(item) +method saveExemptions*(self: Module, itemId: string, muteAllMessages: bool, personalMentions: string, + globalMentions: string, otherMessages: string) = + var allExemptions = singletonInstance.localAccountSensitiveSettings.getNotifSettingExemptionsAsJson() + allExemptions[itemId] = %* { + EXEMPTION_KEY_MUTE_ALL_MESSAGES: muteAllMessages, + EXEMPTION_KEY_PERSONAL_MENTIONS: personalMentions, + EXEMPTION_KEY_GLOBAL_MENTIONS: globalMentions, + EXEMPTION_KEY_OTHER_MESSAGES: otherMessages + } + + self.view.exemptionsModel().updateExemptions(itemId, muteAllMessages, personalMentions, globalMentions, otherMessages) + + singletonInstance.localAccountSensitiveSettings.setNotifSettingExemptions($allExemptions) + +method onToggleSection*(self: Module, sectionType: SectionType) = + if(sectionType != SectionType.Community): + return + + if(singletonInstance.localAccountSensitiveSettings.getCommunitiesEnabled()): + let channelGroups = self.controller.getChannelGroups() + for cg in channelGroups: + if cg.channelGroupType == ChannelGroupType.Community: + let item = self.createItem(cg.id, cg.name, cg.images.thumbnail, cg.color, joinedTimestamp = 0, item.Type.Community) + self.view.exemptionsModel().addItem(item) else: - let item = initItem(chat.id, chat.name, chat.icon, chat.color) - self.view.mutedChatsModel().addItem(item) + let allExemptions = singletonInstance.localAccountSensitiveSettings.getNotifSettingExemptionsAsJson() + for item in self.view.exemptionsModel().modelIterator(): + if(allExemptions.contains(item.id)): + allExemptions.delete(item.id) + singletonInstance.localAccountSensitiveSettings.setNotifSettingExemptions($allExemptions) + self.view.exemptionsModel().removeItemsByType(item.Type.Community) + +method addCommunity*(self: Module, communityDto: CommunityDto) = + let item = self.createItem(communityDto.id, communityDto.name, communityDto.images.thumbnail, communityDto.color, + joinedTimestamp = 0, item.Type.Community) + self.view.exemptionsModel().addItem(item) -method onChatUnmuted*(self: Module, chatId: string) = - self.view.mutedContactsModel().removeItemById(chatId) - self.view.mutedChatsModel().removeItemById(chatId) +method editCommunity*(self: Module, communityDto: CommunityDto) = + self.view.exemptionsModel().removeItemById(communityDto.id) + let item = self.createItem(communityDto.id, communityDto.name, communityDto.images.thumbnail, communityDto.color, + joinedTimestamp = 0, item.Type.Community) + self.view.exemptionsModel().addItem(item) -method onChatLeft*(self: Module, chatId: string) = - self.view.mutedContactsModel().removeItemById(chatId) - self.view.mutedChatsModel().removeItemById(chatId) +method removeItemWithId*(self: Module, itemId: string) = + var allExemptions = singletonInstance.localAccountSensitiveSettings.getNotifSettingExemptionsAsJson() + if(allExemptions.contains(itemId)): + allExemptions.delete(itemId) + singletonInstance.localAccountSensitiveSettings.setNotifSettingExemptions($allExemptions) + self.view.exemptionsModel().removeItemById(itemId) + +method addChat*(self: Module, chatDto: ChatDto) = + let ind = self.view.exemptionsModel().findIndexForItemId(chatDto.id) + if(ind != -1): + return + let item = self.createChatItem(chatDto) + self.view.exemptionsModel().addItem(item) + +method addChat*(self: Module, itemId: string) = + let ind = self.view.exemptionsModel().findIndexForItemId(itemId) + if(ind != -1): + return + let chatDto = self.controller.getChatDetails(itemId) + self.addChat(chatDto) + +method setName*(self: Module, itemId: string, name: string) = + self.view.exemptionsModel().updateName(itemId, name) \ No newline at end of file diff --git a/src/app/modules/main/profile_section/notifications/view.nim b/src/app/modules/main/profile_section/notifications/view.nim index 5763a23382..74f65772a7 100644 --- a/src/app/modules/main/profile_section/notifications/view.nim +++ b/src/app/modules/main/profile_section/notifications/view.nim @@ -5,49 +5,37 @@ QtObject: type View* = ref object of QObject delegate: io_interface.AccessInterface - mutedContactsModel: Model - mutedContactsModelVariant: QVariant - mutedChatsModel: Model - mutedChatsModelVariant: QVariant - + exemptionsModel: Model + exemptionsModelVariant: QVariant + proc delete*(self: View) = - self.mutedContactsModel.delete - self.mutedContactsModelVariant.delete - self.mutedChatsModel.delete - self.mutedChatsModelVariant.delete + self.exemptionsModel.delete + self.exemptionsModelVariant.delete self.QObject.delete proc newView*(delegate: io_interface.AccessInterface): View = new(result, delete) result.QObject.setup result.delegate = delegate - result.mutedContactsModel = newModel() - result.mutedContactsModelVariant = newQVariant(result.mutedContactsModel) - result.mutedChatsModel = newModel() - result.mutedChatsModelVariant = newQVariant(result.mutedChatsModel) - + result.exemptionsModel = newModel() + result.exemptionsModelVariant = newQVariant(result.exemptionsModel) + proc load*(self: View) = self.delegate.viewDidLoad() - proc mutedContactsModel*(self: View): Model = - return self.mutedContactsModel + proc sendTestNotification*(self: View, title: string, message: string) {.slot.} = + self.delegate.sendTestNotification(title, message) - proc mutedContactsModelChanged*(self: View) {.signal.} - proc getMutedContactsModel(self: View): QVariant {.slot.} = - return self.mutedContactsModelVariant - QtProperty[QVariant] mutedContactsModel: - read = getMutedContactsModel - notify = mutedContactsModelChanged + proc exemptionsModel*(self: View): Model = + return self.exemptionsModel - proc mutedChatsModel*(self: View): Model = - return self.mutedChatsModel + proc exemptionsModelChanged*(self: View) {.signal.} + proc getExemptionsModel(self: View): QVariant {.slot.} = + return self.exemptionsModelVariant + QtProperty[QVariant] exemptionsModel: + read = getExemptionsModel + notify = exemptionsModelChanged - proc mutedChatsModelChanged*(self: View) {.signal.} - proc getMutedChatsModel(self: View): QVariant {.slot.} = - return self.mutedChatsModelVariant - QtProperty[QVariant] mutedChatsModel: - read = getMutedChatsModel - notify = mutedChatsModelChanged - - proc unmuteChat*(self: View, chatId: string) {.slot.} = - self.delegate.unmuteChat(chatId) + proc saveExemptions*(self: View, itemId: string, muteAllMessages: bool, personalMentions: string, + globalMentions: string, otherMessages: string) {.slot.} = + self.delegate.saveExemptions(itemId, muteAllMessages, personalMentions, globalMentions, otherMessages) \ No newline at end of file diff --git a/src/app_service/service/accounts/service.nim b/src/app_service/service/accounts/service.nim index 82bb699605..17344fb438 100644 --- a/src/app_service/service/accounts/service.nim +++ b/src/app_service/service/accounts/service.nim @@ -1,4 +1,4 @@ -import json, sequtils, strutils, uuids, os +import json, sequtils, strutils, uuids import json_serialization, chronicles import ./dto/accounts as dto_accounts diff --git a/src/app_service/service/chat/service.nim b/src/app_service/service/chat/service.nim index 5ecab3c10e..0e93aedafc 100644 --- a/src/app_service/service/chat/service.nim +++ b/src/app_service/service/chat/service.nim @@ -85,6 +85,7 @@ const SIGNAL_CHAT_MEMBERS_ADDED* = "chatMemberAdded" const SIGNAL_CHAT_MEMBER_REMOVED* = "chatMemberRemoved" const SIGNAL_CHAT_MEMBER_UPDATED* = "chatMemberUpdated" const SIGNAL_CHAT_SWITCH_TO_OR_CREATE_1_1_CHAT* = "switchToOrCreateOneToOneChat" +const SIGNAL_CHAT_ADDED_OR_UPDATED* = "chatAddedOrUpdated" QtObject: type Service* = ref object of QObject @@ -166,6 +167,7 @@ QtObject: proc updateOrAddChat*(self: Service, chat: ChatDto) = self.chats[chat.id] = chat + self.events.emit(SIGNAL_CHAT_ADDED_OR_UPDATED, ChatArgs(communityId: chat.communityId, chatId: chat.id)) var channelGroupId = chat.communityId if (channelGroupId == ""): diff --git a/src/app_service/service/message/dto/message.nim b/src/app_service/service/message/dto/message.nim index 6a809b72f6..cbe596bcc5 100644 --- a/src/app_service/service/message/dto/message.nim +++ b/src/app_service/service/message/dto/message.nim @@ -174,13 +174,17 @@ proc containsContactMentions*(self: MessageDto): bool = return true return false -proc isUserWithPkMentioned*(self: MessageDto, publicKey: string): bool = +proc isPersonalMention*(self: MessageDto, publicKey: string): bool = for pText in self.parsedText: for child in pText.children: if (child.type == PARSED_TEXT_CHILD_TYPE_MENTION and child.literal.contains(publicKey)): return true return false +proc isGlobalMention*(self: MessageDto): bool = + # TODO: we should check here if message contains global mention. + return false + proc mentionedUsersPks*(self: MessageDto): seq[string] = for pText in self.parsedText: for child in pText.children: diff --git a/src/app_service/service/message/service.nim b/src/app_service/service/message/service.nim index 42cc360339..29e268c962 100644 --- a/src/app_service/service/message/service.nim +++ b/src/app_service/service/message/service.nim @@ -56,6 +56,7 @@ include async_tasks type MessagesArgs* = ref object of Args + sectionId*: string chatId*: string chatType*: ChatType unviewedMessagesCount*: int @@ -157,6 +158,7 @@ QtObject: # if (not chats[0].active): # return + let sectionId = if chats[0].communityId.len > 0: chats[0].communityId else: singletonInstance.userProfile.getPubKey() let chatId = chats[0].id let chatType = chats[0].chatType let unviewedMessagesCount = chats[0].unviewedMessagesCount @@ -187,6 +189,7 @@ QtObject: removeMessageWithId(messages, msgId) let data = MessagesArgs( + sectionId: sectionId, chatId: chatId, chatType: chatType, unviewedMessagesCount: unviewedMessagesCount, diff --git a/ui/app/AppLayouts/Profile/ProfileLayout.qml b/ui/app/AppLayouts/Profile/ProfileLayout.qml index 83684a8083..8b08547712 100644 --- a/ui/app/AppLayouts/Profile/ProfileLayout.qml +++ b/ui/app/AppLayouts/Profile/ProfileLayout.qml @@ -106,7 +106,6 @@ StatusAppTwoPanelLayout { EnsView { // TODO: we need to align structure for the entire this part using `SettingsContentBase` as root component - // TODO: handle structure for this subsection to match style used in onther sections // using `SettingsContentBase` component as base. id: ensContainer @@ -158,14 +157,6 @@ StatusAppTwoPanelLayout { systemPalette: profileView.systemPalette } - SoundsView { - Layout.fillWidth: true - Layout.fillHeight: true - - sectionTitle: profileView.store.getNameForSubsection(Constants.settingsSubsection.sound) - contentWidth: d.contentWidth - } - LanguageView { Layout.fillWidth: true Layout.fillHeight: true @@ -181,6 +172,7 @@ StatusAppTwoPanelLayout { Layout.fillHeight: true notificationsStore: profileView.store.notificationsStore + devicesStore: profileView.store.devicesStore sectionTitle: profileView.store.getNameForSubsection(Constants.settingsSubsection.notifications) contentWidth: d.contentWidth } diff --git a/ui/app/AppLayouts/Profile/controls/NotificationSelect.qml b/ui/app/AppLayouts/Profile/controls/NotificationSelect.qml new file mode 100644 index 0000000000..1a09220caa --- /dev/null +++ b/ui/app/AppLayouts/Profile/controls/NotificationSelect.qml @@ -0,0 +1,71 @@ +import QtQuick 2.13 +import QtQuick.Controls 2.13 + +import StatusQ.Controls 0.1 +import StatusQ.Popups 0.1 + +import utils 1.0 + +Item { + id: root + + signal sendAlertsClicked() + signal deliverQuietlyClicked() + signal turnOffClicked() + + property string selected: Constants.settingsSection.notifications.sendAlertsValue + + implicitWidth: button.width + implicitHeight: button.height + + QtObject { + id: d + readonly property string sendAlertsText: qsTr("Send Alerts") + readonly property string deliverQuietlyText: qsTr("Deliver Quietly") + readonly property string turnOffText: qsTr("Turn Off") + } + + StatusButton { + id: button + text: root.selected === Constants.settingsSection.notifications.turnOffValue? d.turnOffText : + root.selected === Constants.settingsSection.notifications.deliverQuietlyValue? d.deliverQuietlyText : + d.sendAlertsText + icon.name: "chevron-down" + + onClicked: { + if (selectMenu.opened) { + selectMenu.close() + } else { + selectMenu.popup(button.x, button.y + button.height + 8) + } + } + } + + StatusPopupMenu { + id: selectMenu + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + width: parent.width + clip: true + + StatusMenuItem { + text: d.sendAlertsText + onTriggered: { + root.sendAlertsClicked() + } + } + + StatusMenuItem { + text: d.deliverQuietlyText + onTriggered: { + root.deliverQuietlyClicked() + } + } + + StatusMenuItem { + text: d.turnOffText + onTriggered: { + root.turnOffClicked() + } + } + } +} diff --git a/ui/app/AppLayouts/Profile/popups/ExemptionNotificationsModal.qml b/ui/app/AppLayouts/Profile/popups/ExemptionNotificationsModal.qml new file mode 100644 index 0000000000..948d1fdcfc --- /dev/null +++ b/ui/app/AppLayouts/Profile/popups/ExemptionNotificationsModal.qml @@ -0,0 +1,159 @@ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Components 0.1 +import StatusQ.Popups 0.1 + +import utils 1.0 +import shared.panels 1.0 + +import "../controls" + +StatusModal { + id: root + + property var notificationsStore + property var item: ({ + name: "", + image: "", + color: "", + customized: false, + type: Constants.settingsSection.exemptions.community, + muteAllMessages: false, + personalMentions: Constants.settingsSection.notifications.sendAlertsValue, + globalMentions: Constants.settingsSection.notifications.sendAlertsValue, + otherMessages: Constants.settingsSection.notifications.turnOffValue + }) + + header.title: qsTr("%1 exemption").arg(root.item.name) + header.image.source: root.item.image + header.icon: StatusIconSettings { + // Once we introduce StatusSmartIdenticon in popup header, we should use the folowing +// color: root.item.type === Constants.settingsSection.exemptions.oneToOneChat? +// Theme.palette.userCustomizationColors[Utils.colorIdForPubkey(root.item.itemId)] : +// root.item.color + // until then the following is used + background.color: root.item.type === Constants.settingsSection.exemptions.oneToOneChat? + Theme.palette.userCustomizationColors[Utils.colorIdForPubkey(root.item.itemId)] : + root.item.color + charactersLen: root.item.type === Constants.settingsSection.exemptions.oneToOneChat? 2 : 1 + isLetterIdenticon: root.item.image === "" + height: isLetterIdenticon ? 40 : 20 + width: isLetterIdenticon ? 40 : 20 + } + + QtObject { + id: d + readonly property bool isOneToOneChat: root.item.type === Constants.settingsSection.exemptions.oneToOneChat + readonly property int contentSpacing: 0 + property bool muteAllMessages: root.item.muteAllMessages + property string personalMentions: root.item.personalMentions + property string globalMentions: root.item.globalMentions + property string otherMessages: root.item.otherMessages + property bool customized: d.muteAllMessages || + d.personalMentions !== Constants.settingsSection.notifications.sendAlertsValue || + d.globalMentions !== Constants.settingsSection.notifications.sendAlertsValue || + d.otherMessages !== Constants.settingsSection.notifications.turnOffValue + } + + contentItem: Column { + width: root.width + spacing: d.contentSpacing + + StatusListItem { + width: parent.width + title: qsTr("Mute all messages") + enabled: false + components: [ + StatusSwitch { + id: muteAllMessagesSwitch + checked: d.muteAllMessages + onClicked: { + d.muteAllMessages = !d.muteAllMessages + } + } + ] + sensor.onClicked: { + muteAllMessagesSwitch.clicked() + } + } + + Separator { + visible: !d.isOneToOneChat + } + + StatusListItem { + width: parent.width + title: qsTr("Personal @ Mentions") + enabled: false + visible: !d.isOneToOneChat + components: [ + NotificationSelect { + selected: d.personalMentions + onSendAlertsClicked: d.personalMentions = Constants.settingsSection.notifications.sendAlertsValue + onDeliverQuietlyClicked: d.personalMentions = Constants.settingsSection.notifications.deliverQuietlyValue + onTurnOffClicked: d.personalMentions = Constants.settingsSection.notifications.turnOffValue + } + ] + } + + StatusListItem { + width: parent.width + title: qsTr("Global @ Mentions") + enabled: false + visible: !d.isOneToOneChat + components: [ + NotificationSelect { + selected: d.globalMentions + onSendAlertsClicked: d.globalMentions = Constants.settingsSection.notifications.sendAlertsValue + onDeliverQuietlyClicked: d.globalMentions = Constants.settingsSection.notifications.deliverQuietlyValue + onTurnOffClicked: d.globalMentions = Constants.settingsSection.notifications.turnOffValue + } + ] + } + + StatusListItem { + width: parent.width + title: qsTr("Other Messages") + enabled: false + visible: !d.isOneToOneChat + components: [ + NotificationSelect { + selected: d.otherMessages + onSendAlertsClicked: d.otherMessages = Constants.settingsSection.notifications.sendAlertsValue + onDeliverQuietlyClicked: d.otherMessages = Constants.settingsSection.notifications.deliverQuietlyValue + onTurnOffClicked: d.otherMessages = Constants.settingsSection.notifications.turnOffValue + } + ] + } + } + + rightButtons: [ + StatusFlatButton { + text: qsTr("Clear Exemptions") + enabled: d.customized + onClicked: { + d.muteAllMessages = false + d.personalMentions = Constants.settingsSection.notifications.sendAlertsValue + d.globalMentions = Constants.settingsSection.notifications.sendAlertsValue + d.otherMessages = Constants.settingsSection.notifications.turnOffValue + } + }, + StatusButton { + id: btnCreateEdit + text: qsTr("Done") + onClicked: { + root.notificationsStore.saveExemptions(root.item.itemId, + d.muteAllMessages, + d.personalMentions, + d.globalMentions, + d.otherMessages) + root.close() + } + } + ] +} diff --git a/ui/app/AppLayouts/Profile/stores/NotificationsStore.qml b/ui/app/AppLayouts/Profile/stores/NotificationsStore.qml index 64e20be3d2..ff1b865ea0 100644 --- a/ui/app/AppLayouts/Profile/stores/NotificationsStore.qml +++ b/ui/app/AppLayouts/Profile/stores/NotificationsStore.qml @@ -6,10 +6,13 @@ QtObject { property var notificationsModule - property var mutedContactsModel: notificationsModule.mutedContactsModel - property var mutedChatsModel: notificationsModule.mutedChatsModel + property var exemptionsModel: notificationsModule.exemptionsModel - function unmuteChat(chatId) { - return root.notificationsModule.unmuteChat(chatId) + function sendTestNotification(title, message) { + root.notificationsModule.sendTestNotification(title, message) + } + + function saveExemptions(itemId, muteAllMessages, personalMentions, globalMentions, allMessages) { + root.notificationsModule.saveExemptions(itemId, muteAllMessages, personalMentions, globalMentions, allMessages) } } diff --git a/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml b/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml index 8b7bd8b4ad..3b91fe5671 100644 --- a/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml +++ b/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml @@ -96,14 +96,11 @@ QtObject { append({subsection: Constants.settingsSubsection.appearance, text: qsTr("Appearance"), icon: "appearance"}) - append({subsection: Constants.settingsSubsection.sound, - text: qsTr("Sound"), - icon: "sound"}) append({subsection: Constants.settingsSubsection.language, text: qsTr("Language & Currency"), icon: "language"}) append({subsection: Constants.settingsSubsection.notifications, - text: qsTr("Notifications"), + text: qsTr("Notifications & Sounds"), icon: "notification"}) append({subsection: Constants.settingsSubsection.devicesSettings, text: qsTr("Devices settings"), diff --git a/ui/app/AppLayouts/Profile/views/NotificationsView.qml b/ui/app/AppLayouts/Profile/views/NotificationsView.qml index e7ef40cf58..9e8e349ecb 100644 --- a/ui/app/AppLayouts/Profile/views/NotificationsView.qml +++ b/ui/app/AppLayouts/Profile/views/NotificationsView.qml @@ -1,406 +1,551 @@ import QtQuick 2.13 import QtQuick.Controls 2.13 -import QtGraphicalEffects 1.13 import QtQuick.Layouts 1.13 import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Components 0.1 +import StatusQ.Popups 0.1 import utils 1.0 - import shared.panels 1.0 -import shared.status 1.0 import shared.controls 1.0 import "../stores" -import "../popups" +import "../controls" import "../panels" -import "./" +import "../popups" SettingsContentBase { id: root - property NotificationsStore notificationsStore + property DevicesStore devicesStore - Item { - id: notificationsContainer - width: root.contentWidth - height: this.childrenRect.height + 100 + ColumnLayout { + id: contentColumn + spacing: Constants.settingsSection.itemSpacing - property Component mutedChatsModalComponent: MutedChatsModal {} + ButtonGroup { + id: messageSetting + } + Loader { + id: exemptionNotificationsModal + active: false - ButtonGroup { - id: notificationSetting - } + function open(item) { + active = true + exemptionNotificationsModal.item.item = item + exemptionNotificationsModal.item.open() + } + function close() { + active = false + } - ButtonGroup { - id: soundSetting - } + sourceComponent: ExemptionNotificationsModal { + anchors.centerIn: parent + notificationsStore: root.notificationsStore - ButtonGroup { - id: messageSetting - } - - Column { - id: column - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - - RadioButtonSelector { - anchors.leftMargin: 0 - anchors.rightMargin: 0 - //% "All messages" - title: qsTrId("all-messages") - buttonGroup: notificationSetting - checked: localAccountSensitiveSettings.notificationSetting === Constants.notifyAllMessages - onCheckedChanged: { - if (checked) { - localAccountSensitiveSettings.notificationSetting = Constants.notifyAllMessages + onClosed: { + exemptionNotificationsModal.close(); } } } - RadioButtonSelector { - anchors.leftMargin: 0 - anchors.rightMargin: 0 - //% "Just @mentions" - title: qsTrId("just--mentions") - buttonGroup: notificationSetting - checked: localAccountSensitiveSettings.notificationSetting === Constants.notifyJustMentions - onCheckedChanged: { - if (checked) { - localAccountSensitiveSettings.notificationSetting = Constants.notifyJustMentions - } - } - } + Component { + id: exemptionDelegateComponent + StatusListItem { + property string lowerCaseSearchString: searchBox.text.toLowerCase() - RadioButtonSelector { - anchors.leftMargin: 0 - anchors.rightMargin: 0 - //% "Nothing" - title: qsTrId("nothing") - buttonGroup: notificationSetting - checked: localAccountSensitiveSettings.notificationSetting === Constants.notifyNone - onCheckedChanged: { - if (checked) { - localAccountSensitiveSettings.notificationSetting = Constants.notifyNone - } - } - } - } - - Separator { - id: separator - anchors.top: column.bottom - anchors.topMargin: Style.current.bigPadding - anchors.left: parent.left - anchors.right: parent.right - } - - StatusSectionHeadline { - id: sectionHeadlineSound - //% "Appearance" - text: qsTrId("appearance") - anchors.top: separator.bottom - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: Style.current.padding - anchors.rightMargin: Style.current.padding - } - - Column { - id: column2 - anchors.top: sectionHeadlineSound.bottom - anchors.topMargin: Style.current.smallPadding - anchors.left: parent.left - anchors.right: parent.right - width: parent.width - - // TODO: replace with StatusListItem - StatusSettingsLineButton { - anchors.leftMargin: 0 - anchors.rightMargin: 0 - //% "Play a sound when receiving a notification" - text: qsTrId("play-a-sound-when-receiving-a-notification") - isSwitch: true - switchChecked: localAccountSensitiveSettings.notificationSoundsEnabled - onClicked: { - localAccountSensitiveSettings.notificationSoundsEnabled = checked - } - } - - // TODO: replace with StatusListItem - StatusSettingsLineButton { - anchors.leftMargin: 0 - anchors.rightMargin: 0 - //% "Use your operating system's notifications" - text: qsTrId("use-your-operating-system-s-notifications") - isSwitch: true - switchChecked: localAccountSensitiveSettings.useOSNotifications - onClicked: { - localAccountSensitiveSettings.useOSNotifications = checked - } - - StatusBaseText { - id: detailText - //% "Setting this to false will instead use Status' notification style as seen below" - text: qsTrId("setting-this-to-false-will-instead-use-status--notification-style-as-seen-below") - color: Theme.palette.baseColor1 width: parent.width - font.pixelSize: 12 - wrapMode: Text.WordWrap - anchors.left: parent.left - anchors.leftMargin: Style.current.padding - anchors.bottom: parent.bottom - anchors.topMargin: 2 + height: visible ? implicitHeight : 0 + visible: lowerCaseSearchString === "" || + model.itemId.toLowerCase().includes(lowerCaseSearchString) || + model.name.toLowerCase().includes(lowerCaseSearchString) + title: model.name + subTitle: { + if(model.type === Constants.settingsSection.exemptions.community) + return qsTr("Community") + else if(model.type === Constants.settingsSection.exemptions.oneToOneChat) + return qsTr("1:1 Chat") + else if(model.type === Constants.settingsSection.exemptions.groupChat) + return qsTr("Group Chat") + else + return "" + } + label: { + if(!model.customized) + return "" + + let l = "" + if(model.muteAllMessages) + l += qsTr("Muted") + else { + let nbOfChanges = 0 + + if(model.personalMentions !== Constants.settingsSection.notifications.sendAlertsValue) + { + nbOfChanges++ + let valueText = model.personalMentions === Constants.settingsSection.notifications.turnOffValue? + qsTr("Off") : + qsTr("Quiet") + l = qsTr("Personal @ Mentions %1").arg(valueText) + } + + if(model.globalMentions !== Constants.settingsSection.notifications.sendAlertsValue) + { + nbOfChanges++ + let valueText = model.globalMentions === Constants.settingsSection.notifications.turnOffValue? + qsTr("Off") : + qsTr("Quiet") + l = qsTr("Global @ Mentions %1").arg(valueText) + } + + if(model.otherMessages !== Constants.settingsSection.notifications.turnOffValue) + { + nbOfChanges++ + let valueText = model.otherMessages === Constants.settingsSection.notifications.sendAlertsValue? + qsTr("Alerts") : + qsTr("Quiet") + l = qsTr("Other Messages %1").arg(valueText) + } + + if(nbOfChanges > 1) + l = qsTr("Multiple Exemptions") + } + + return l + } + + // Maybe we need to redo `StatusListItem` to display identicon ring, but that's not in Figma design for now. + image.source: model.image + ringSettings.ringSpecModel: Utils.getColorHashAsJson(model.itemId) + icon: StatusIconSettings { + color: model.type === Constants.settingsSection.exemptions.oneToOneChat? + Theme.palette.userCustomizationColors[Utils.colorIdForPubkey(model.itemId)] : + model.color + charactersLen: model.type === Constants.settingsSection.exemptions.oneToOneChat? 2 : 1 + isLetterIdenticon: model.image === "" + height: isLetterIdenticon ? 40 : 20 + width: isLetterIdenticon ? 40 : 20 + } + + components: [ + StatusIcon { + visible: model.customized + icon: "chevron-down" + rotation: 270 + color: Theme.palette.baseColor1 + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + exemptionNotificationsModal.open(model) + } + } + }, + StatusIcon { + visible: !model.customized + icon: "add" + rotation: 270 + color: Theme.palette.primaryColor1 + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + exemptionNotificationsModal.open(model) + } + } + }] } } - } - Column { - id: column3 - spacing: Style.current.bigPadding - anchors.top: column2.bottom - anchors.topMargin: Style.current.padding*2 - anchors.left: parent.left - anchors.right: parent.right + Rectangle { + Layout.preferredWidth: root.contentWidth + implicitHeight: col1.height + 2 * Style.current.padding + visible: Qt.platform.os == "osx" + radius: Constants.settingsSection.radius + color: Theme.palette.primaryColor3 - StatusBaseText { - //% "Message preview" - text: qsTrId("message-preview") - font.pixelSize: 15 - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: Style.current.padding - anchors.rightMargin: Style.current.padding - color: Theme.palette.directColor1 - } + ColumnLayout { + id: col1 + anchors.margins: Style.current.padding + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + spacing: Constants.settingsSection.infoSpacing - Column { - anchors.left: parent.left - anchors.right: parent.right - spacing: 10 + StatusBaseText { + Layout.preferredWidth: parent.width + text: qsTr("Enable Notifications in macOS Settings") + font.pixelSize: Constants.settingsSection.infoFontSize + lineHeight: Constants.settingsSection.infoLineHeight + lineHeightMode: Text.FixedHeight + color: Theme.palette.primaryColor1 - NotificationAppearancePreviewPanel { - //% "Anonymous" - name: qsTrId("anonymous") - notificationTitle: "Status" - //% "You have a new message" - notificationMessage: qsTrId("you-have-a-new-message") - buttonGroup: messageSetting - checked: localAccountSensitiveSettings.notificationMessagePreviewSetting === Constants.notificationPreviewAnonymous - onRadioCheckedChanged: { - if (checked) { - localAccountSensitiveSettings.notificationMessagePreviewSetting = Constants.notificationPreviewAnonymous + } + + StatusBaseText { + Layout.preferredWidth: parent.width + text: qsTr("To receive Status notifications, make sure you've enabled them in" + + " your computer's settings under System Preferences > Notifications") + font.pixelSize: Constants.settingsSection.infoFontSize + lineHeight: Constants.settingsSection.infoLineHeight + lineHeightMode: Text.FixedHeight + color: Theme.palette.baseColor1 + wrapMode: Text.WordWrap + } } } - } - NotificationAppearancePreviewPanel { - //% "Name only" - name: qsTrId("name-only") - notificationTitle: "Vitalik Buterin" - //% "You have a new message" - notificationMessage: qsTrId("you-have-a-new-message") - buttonGroup: messageSetting - checked: localAccountSensitiveSettings.notificationMessagePreviewSetting === Constants.notificationPreviewNameOnly - onRadioCheckedChanged: { - if (checked) { - localAccountSensitiveSettings.notificationMessagePreviewSetting = Constants.notificationPreviewNameOnly + Rectangle { + Layout.preferredWidth: root.contentWidth + implicitHeight: row1.height + 2 * Style.current.padding + radius: Constants.settingsSection.radius + color: Theme.palette.pinColor2 + + RowLayout { + id: row1 + anchors.margins: Style.current.padding + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + visible: root.devicesStore.devicesModel.count > 0 + + StatusBaseText { + Layout.fillWidth: true + text: qsTr("Sync your devices to share notifications preferences") + font.pixelSize: Constants.settingsSection.infoFontSize + lineHeight: Constants.settingsSection.infoLineHeight + lineHeightMode: Text.FixedHeight + color: Theme.palette.pinColor1 + } + + StatusBaseText { + text: qsTr("Syncing >") + font.pixelSize: Constants.settingsSection.infoFontSize + lineHeight: Constants.settingsSection.infoLineHeight + lineHeightMode: Text.FixedHeight + color: Theme.palette.pinColor1 + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + root.devicesStore.syncAll() + } + } + } } } - } - NotificationAppearancePreviewPanel { - //% "Name & Message" - name: qsTrId("name---message") - notificationTitle: "Vitalik Buterin" - //% "Hi there! Yes, no problem, let me know if I can help." - notificationMessage: qsTrId("hi-there--yes--no-problem--let-me-know-if-i-can-help-") - buttonGroup: messageSetting - checked: localAccountSensitiveSettings.notificationMessagePreviewSetting === Constants.notificationPreviewNameAndMessage - onRadioCheckedChanged: { - if (checked) { - localAccountSensitiveSettings.notificationMessagePreviewSetting = Constants.notificationPreviewNameAndMessage + StatusListItem { + Layout.preferredWidth: root.contentWidth + title: qsTr("Allow Notifications") + components: [ + StatusSwitch { + id: allowNotifSwitch + checked: localAccountSensitiveSettings.notifSettingAllowNotifications + onClicked: { + localAccountSensitiveSettings.notifSettingAllowNotifications = !localAccountSensitiveSettings.notifSettingAllowNotifications + } + } + ] + sensor.onClicked: { + allowNotifSwitch.clicked() } } - } - } - StatusBaseText { - //% "No preview or Advanced? Go to Notification Center" - text: qsTrId("no-preview-or-advanced--go-to-notification-center") - font.pixelSize: 15 - anchors.left: parent.left - anchors.leftMargin: Style.current.padding - anchors.rightMargin: Style.current.padding - color: Theme.palette.directColor1 - } - } - - Separator { - id: separator2 - anchors.top: column3.bottom - anchors.topMargin: Style.current.bigPadding - anchors.left: parent.left - anchors.right: parent.right - } - - StatusSectionHeadline { - id: sectionHeadlineContacts - //% "Contacts & Users" - text: qsTrId("contacts---users") - anchors.top: separator2.bottom - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: Style.current.padding - anchors.rightMargin: Style.current.padding - } - - Column { - id: column4 - anchors.top: sectionHeadlineContacts.bottom - anchors.topMargin: Style.current.smallPadding - anchors.left: parent.left - anchors.right: parent.right - width: parent.width - - // TODO: replace with StatusListItem - StatusSettingsLineButton { - anchors.leftMargin: 0 - anchors.rightMargin: 0 - //% "Notify on new requests" - text: qsTrId("notify-on-new-requests") - isSwitch: true - switchChecked: localAccountSensitiveSettings.notifyOnNewRequests - onClicked: { - localAccountSensitiveSettings.notifyOnNewRequests = checked - } - } - - // TODO: replace with StatusListItem - StatusSettingsLineButton { - anchors.leftMargin: 0 - anchors.rightMargin: 0 - //% "Muted users" - text: qsTrId("muted-users") - currentValue: root.notificationsStore.mutedContactsModel.count > 0 ? - //% "None" - root.notificationsStore.mutedContactsModel.count : qsTrId("none") - isSwitch: false - onClicked: { - const mutedChatsModal = notificationsContainer.mutedChatsModalComponent.createObject(notificationsContainer) - mutedChatsModal.model = root.notificationsStore.notificationsModule.mutedContactsModel - //% "Muted contacts" - mutedChatsModal.title = qsTrId("muted-contacts"); - //% "Muted contacts will appear here" - mutedChatsModal.noContentText = qsTrId("muted-contacts-will-appear-here"); - - mutedChatsModal.unmuteChat.connect(function(chatId){ - root.notificationsStore.unmuteChat(chatId) - }) - - mutedChatsModal.open(); - } - } - - // TODO: replace with StatusListItem - StatusSettingsLineButton { - anchors.leftMargin: 0 - anchors.rightMargin: 0 - //% "Muted chats" - text: qsTrId("muted-chats") - currentValue: root.notificationsStore.mutedChatsModel.count > 0 ? - //% "None" - root.notificationsStore.mutedChatsModel.count : qsTrId("none") - isSwitch: false - onClicked: { - const mutedChatsModal = notificationsContainer.mutedChatsModalComponent.createObject(notificationsContainer) - mutedChatsModal.model = root.notificationsStore.notificationsModule.mutedChatsModel - //% "Muted chats" - mutedChatsModal.title = qsTrId("muted-chats"); - //% "Muted chats will appear here" - mutedChatsModal.noContentText = qsTrId("muted-chats-will-appear-here"); - - mutedChatsModal.unmuteChat.connect(function(chatId){ - root.notificationsStore.unmuteChat(chatId) - }) - - mutedChatsModal.open(); - } - - StatusBaseText { - //% "You can limit what gets shown in notifications" - text: qsTrId("you-can-limit-what-gets-shown-in-notifications") - color: Theme.palette.baseColor1 - width: parent.width - font.pixelSize: 12 - wrapMode: Text.WordWrap - anchors.left: parent.left - anchors.leftMargin: Style.current.padding - anchors.bottom: parent.bottom - anchors.topMargin: 2 - } - } - } - - Separator { - id: separator3 - anchors.top: column4.bottom - anchors.topMargin: Style.current.bigPadding - anchors.left: parent.left - anchors.right: parent.right - } - - Column { - id: column5 - spacing: Style.current.smallPadding - anchors.top: separator3.bottom - anchors.topMargin: Style.current.bigPadding - anchors.left: parent.left - anchors.right: parent.right - width: parent.width - - StatusBaseText { - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: Style.current.padding - anchors.rightMargin: Style.current.padding - //% "Reset notification settings" - text: qsTrId("reset-notification-settings") - font.pixelSize: 15 - color: Theme.palette.dangerColor1 - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - onEntered: { - parent.font.underline = true + StatusBaseText { + Layout.preferredWidth: root.contentWidth + Layout.leftMargin: Style.current.padding + text: qsTr("Messages") + font.pixelSize: Constants.settingsSection.subHeaderFontSize + color: Theme.palette.baseColor1 } - onExited: { - parent.font.underline = false + + StatusListItem { + Layout.preferredWidth: root.contentWidth + title: qsTr("1:1 Chats") + components: [ + NotificationSelect { + selected: localAccountSensitiveSettings.notifSettingOneToOneChats + onSendAlertsClicked: localAccountSensitiveSettings.notifSettingOneToOneChats = Constants.settingsSection.notifications.sendAlertsValue + onDeliverQuietlyClicked: localAccountSensitiveSettings.notifSettingOneToOneChats = Constants.settingsSection.notifications.deliverQuietlyValue + onTurnOffClicked: localAccountSensitiveSettings.notifSettingOneToOneChats = Constants.settingsSection.notifications.turnOffValue + } + ] } - onClicked: { - localAccountSensitiveSettings.notificationSetting = Constants.notifyAllMessages - localAccountSensitiveSettings.notificationSoundsEnabled = true - localAccountSensitiveSettings.notificationMessagePreviewSetting = Constants.notificationPreviewNameAndMessage - localAccountSensitiveSettings.allowNotificationsFromNonContacts = false + + StatusListItem { + Layout.preferredWidth: root.contentWidth + title: qsTr("Group Chats") + components: [ + NotificationSelect { + selected: localAccountSensitiveSettings.notifSettingGroupChats + onSendAlertsClicked: localAccountSensitiveSettings.notifSettingGroupChats = Constants.settingsSection.notifications.sendAlertsValue + onDeliverQuietlyClicked: localAccountSensitiveSettings.notifSettingGroupChats = Constants.settingsSection.notifications.deliverQuietlyValue + onTurnOffClicked: localAccountSensitiveSettings.notifSettingGroupChats = Constants.settingsSection.notifications.turnOffValue + } + ] + } + + StatusListItem { + Layout.preferredWidth: root.contentWidth + title: qsTr("Personal @ Mentions") + tertiaryTitle: qsTr("Messages containing @%1").arg(userProfile.name) + components: [ + NotificationSelect { + selected: localAccountSensitiveSettings.notifSettingPersonalMentions + onSendAlertsClicked: localAccountSensitiveSettings.notifSettingPersonalMentions = Constants.settingsSection.notifications.sendAlertsValue + onDeliverQuietlyClicked: localAccountSensitiveSettings.notifSettingPersonalMentions = Constants.settingsSection.notifications.deliverQuietlyValue + onTurnOffClicked: localAccountSensitiveSettings.notifSettingPersonalMentions = Constants.settingsSection.notifications.turnOffValue + } + ] + } + + StatusListItem { + Layout.preferredWidth: root.contentWidth + title: qsTr("Global @ Mentions") + tertiaryTitle: qsTr("Messages containing @here and @channel") + components: [ + NotificationSelect { + selected: localAccountSensitiveSettings.notifSettingGlobalMentions + onSendAlertsClicked: localAccountSensitiveSettings.notifSettingGlobalMentions = Constants.settingsSection.notifications.sendAlertsValue + onDeliverQuietlyClicked: localAccountSensitiveSettings.notifSettingGlobalMentions = Constants.settingsSection.notifications.deliverQuietlyValue + onTurnOffClicked: localAccountSensitiveSettings.notifSettingGlobalMentions = Constants.settingsSection.notifications.turnOffValue + } + ] + } + + StatusListItem { + Layout.preferredWidth: root.contentWidth + title: qsTr("All Messages") + components: [ + NotificationSelect { + selected: localAccountSensitiveSettings.notifSettingAllMessages + onSendAlertsClicked: localAccountSensitiveSettings.notifSettingAllMessages = Constants.settingsSection.notifications.sendAlertsValue + onDeliverQuietlyClicked: localAccountSensitiveSettings.notifSettingAllMessages = Constants.settingsSection.notifications.deliverQuietlyValue + onTurnOffClicked: localAccountSensitiveSettings.notifSettingAllMessages = Constants.settingsSection.notifications.turnOffValue + } + ] + } + + StatusBaseText { + Layout.preferredWidth: root.contentWidth + Layout.leftMargin: Style.current.padding + text: qsTr("Others") + font.pixelSize: Constants.settingsSection.subHeaderFontSize + color: Theme.palette.baseColor1 + } + + StatusListItem { + Layout.preferredWidth: root.contentWidth + title: qsTr("Contact Requests") + components: [ + NotificationSelect { + selected: localAccountSensitiveSettings.notifSettingContactRequests + onSendAlertsClicked: localAccountSensitiveSettings.notifSettingContactRequests = Constants.settingsSection.notifications.sendAlertsValue + onDeliverQuietlyClicked: localAccountSensitiveSettings.notifSettingContactRequests = Constants.settingsSection.notifications.deliverQuietlyValue + onTurnOffClicked: localAccountSensitiveSettings.notifSettingContactRequests = Constants.settingsSection.notifications.turnOffValue + } + ] + } + + StatusListItem { + Layout.preferredWidth: root.contentWidth + title: qsTr("Identity Verification Requests") + components: [ + NotificationSelect { + selected: localAccountSensitiveSettings.notifSettingIdentityVerificationRequests + onSendAlertsClicked: localAccountSensitiveSettings.notifSettingIdentityVerificationRequests = Constants.settingsSection.notifications.sendAlertsValue + onDeliverQuietlyClicked: localAccountSensitiveSettings.notifSettingIdentityVerificationRequests = Constants.settingsSection.notifications.deliverQuietlyValue + onTurnOffClicked: localAccountSensitiveSettings.notifSettingIdentityVerificationRequests = Constants.settingsSection.notifications.turnOffValue + } + ] + } + + Separator { + Layout.preferredWidth: root.contentWidth + Layout.preferredHeight: Style.current.bigPadding + } + + StatusBaseText { + Layout.preferredWidth: root.contentWidth + Layout.leftMargin: Style.current.padding + text: qsTr("Notification Content") + font.pixelSize: Constants.settingsSection.subHeaderFontSize + color: Theme.palette.directColor1 + } + + NotificationAppearancePreviewPanel { + id: notifNameAndMsg + Layout.preferredWidth: root.contentWidth + Layout.leftMargin: Style.current.padding + name: qsTr("Show Name and Message") + notificationTitle: "Vitalik Buterin" + notificationMessage: qsTr("Hi there! So EIP-1559 will defini...") + buttonGroup: messageSetting + checked: localAccountSensitiveSettings.notificationMessagePreviewSetting === Constants.settingsSection.notificationsBubble.previewNameAndMessage + onRadioCheckedChanged: { + if (checked) { + localAccountSensitiveSettings.notificationMessagePreviewSetting = Constants.settingsSection.notificationsBubble.previewNameAndMessage + } + } + } + + NotificationAppearancePreviewPanel { + Layout.preferredWidth: root.contentWidth + Layout.leftMargin: Style.current.padding + name: qsTr("Name Only") + notificationTitle: "Vitalik Buterin" + notificationMessage: qsTr("You have a new message") + buttonGroup: messageSetting + checked: localAccountSensitiveSettings.notificationMessagePreviewSetting === Constants.settingsSection.notificationsBubble.previewNameOnly + onRadioCheckedChanged: { + if (checked) { + localAccountSensitiveSettings.notificationMessagePreviewSetting = Constants.settingsSection.notificationsBubble.previewNameOnly + } + } + } + + NotificationAppearancePreviewPanel { + Layout.preferredWidth: root.contentWidth + Layout.leftMargin: Style.current.padding + name: qsTr("Anonymous") + notificationTitle: "Status" + notificationMessage: qsTr("You have a new message") + buttonGroup: messageSetting + checked: localAccountSensitiveSettings.notificationMessagePreviewSetting === Constants.settingsSection.notificationsBubble.previewAnonymous + onRadioCheckedChanged: { + if (checked) { + localAccountSensitiveSettings.notificationMessagePreviewSetting = Constants.settingsSection.notificationsBubble.previewAnonymous + } + } + } + + StatusListItem { + Layout.preferredWidth: root.contentWidth + title: qsTr("Play a Sound When Receiving a Notification") + components: [ + StatusSwitch { + id: soundSwitch + checked: localAccountSensitiveSettings.notificationSoundsEnabled + onClicked: { + localAccountSensitiveSettings.notificationSoundsEnabled = !localAccountSensitiveSettings.notificationSoundsEnabled + } + } + ] + sensor.onClicked: { + soundSwitch.clicked() + } + } + + StatusBaseText { + Layout.preferredWidth: root.contentWidth + Layout.leftMargin: Style.current.padding + text: qsTr("Volume") + font.pixelSize: Constants.settingsSection.subHeaderFontSize + color: Theme.palette.directColor1 + } + + Item { + Layout.preferredWidth: root.contentWidth + Layout.preferredHeight: Constants.settingsSection.itemHeight + Style.current.padding + + StatusSlider { + id: volumeSlider + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: Style.current.bigPadding + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding + from: 0 + to: 100 + stepSize: 1 + + onValueChanged: { + localAccountSensitiveSettings.volume = value + } + + Component.onCompleted: { + value = localAccountSensitiveSettings.volume + } + } + + RowLayout { + anchors.top: volumeSlider.bottom + anchors.left: volumeSlider.left + anchors.topMargin: Style.current.halfPadding + width: volumeSlider.width + + StatusBaseText { + font.pixelSize: 15 + text: volumeSlider.from + Layout.preferredWidth: volumeSlider.width/2 + color: Theme.palette.baseColor1 + } + + StatusBaseText { + font.pixelSize: 15 + text: volumeSlider.to + Layout.alignment: Qt.AlignRight + color: Theme.palette.baseColor1 + } + } + } + + StatusButton { + Layout.leftMargin: Style.current.padding + text: qsTr("Send a Test Notification") + onClicked: { + root.notificationsStore.sendTestNotification(notifNameAndMsg.notificationTitle, + notifNameAndMsg.notificationMessage) + } + } + + Separator { + Layout.preferredWidth: root.contentWidth + Layout.preferredHeight: Style.current.bigPadding + } + + StatusBaseText { + Layout.preferredWidth: root.contentWidth + Layout.leftMargin: Style.current.padding + text: qsTr("Exemptions") + font.pixelSize: Constants.settingsSection.subHeaderFontSize + color: Theme.palette.directColor1 + } + + SearchBox { + id: searchBox + Layout.preferredWidth: root.contentWidth - 2 * Style.current.padding + Layout.leftMargin: Style.current.padding + Layout.rightMargin: Style.current.padding + input.implicitHeight: 44 + input.placeholderText: qsTr("Search Communities, Group Chats and 1:1 Chats") + } + + StatusBaseText { + Layout.preferredWidth: root.contentWidth + Layout.leftMargin: Style.current.padding + text: qsTr("Most recent") + font.pixelSize: Constants.settingsSection.subHeaderFontSize + color: Theme.palette.baseColor1 + } + + ListView { + Layout.preferredWidth: root.contentWidth + Layout.preferredHeight: 400 + visible: root.notificationsStore.exemptionsModel.count > 0 + clip: true + + model: root.notificationsStore.exemptionsModel + delegate: exemptionDelegateComponent } } - } - - StatusBaseText { - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: Style.current.padding - anchors.rightMargin: Style.current.padding - //% "Restore default notification settings and unmute all chats and users" - text: qsTrId("restore-default-notification-settings-and-unmute-all-chats-and-users") - font.pixelSize: 15 - color: Theme.palette.baseColor1 - } - } - } + //} + // } +//} } diff --git a/ui/app/AppLayouts/Profile/views/SoundsView.qml b/ui/app/AppLayouts/Profile/views/SoundsView.qml deleted file mode 100644 index ddf0a5e04f..0000000000 --- a/ui/app/AppLayouts/Profile/views/SoundsView.qml +++ /dev/null @@ -1,46 +0,0 @@ -import QtQuick 2.13 -import QtQuick.Controls 2.13 -import QtQuick.Layouts 1.13 - -import utils 1.0 - -import StatusQ.Core 0.1 -import StatusQ.Core.Theme 0.1 -import StatusQ.Controls 0.1 - -SettingsContentBase { - id: root - - ColumnLayout { - spacing: Constants.settingsSection.itemSpacing - width: root.contentWidth - - StatusBaseText { - id: labelVolume - Layout.fillWidth: true - Layout.leftMargin: Style.current.padding - Layout.rightMargin: Style.current.padding - //% "Sound volume" - text: qsTrId("sound-volume") + " " + volume.value.toPrecision(1) - font.pixelSize: 15 - color: Theme.palette.directColor1 - } - - StatusSlider { - id: volume - Layout.fillWidth: true - Layout.leftMargin: Style.current.padding - Layout.rightMargin: Style.current.padding - from: 0.0 - to: 1.0 - stepSize: 0.1 - onValueChanged: { - localAccountSensitiveSettings.volume = volume.value * 10 - } - - Component.onCompleted: { - value = localAccountSensitiveSettings.volume * 0.1 - } - } - } -} diff --git a/ui/app/AppLayouts/stores/RootStore.qml b/ui/app/AppLayouts/stores/RootStore.qml index 1bc0885aa2..bae8cf15d2 100644 --- a/ui/app/AppLayouts/stores/RootStore.qml +++ b/ui/app/AppLayouts/stores/RootStore.qml @@ -47,7 +47,7 @@ QtObject { // property MessageStore messageStore: MessageStore { } - property real volume: !!localAccountSensitiveSettings ? localAccountSensitiveSettings.volume * 0.1 : 0.2 + property real volume: !!localAccountSensitiveSettings ? localAccountSensitiveSettings.volume * 0.01 : 0.5 property bool notificationSoundsEnabled: !!localAccountSensitiveSettings ? localAccountSensitiveSettings.notificationSoundsEnabled : false property var walletSectionTransactionsInst: walletSectionTransactions diff --git a/ui/imports/shared/status/StatusNotification.qml b/ui/imports/shared/status/StatusNotification.qml index f4f663d16b..c0e5bce8ba 100644 --- a/ui/imports/shared/status/StatusNotification.qml +++ b/ui/imports/shared/status/StatusNotification.qml @@ -26,7 +26,7 @@ Rectangle { Loader { id: identicon - sourceComponent: localAccountSensitiveSettings.notificationMessagePreviewSetting === Constants.notificationPreviewAnonymous ? statusIdenticon : userOrChannelIdenticon + sourceComponent: localAccountSensitiveSettings.notificationMessagePreviewSetting === Constants.settingsSection.notificationsBubble.previewAnonymous ? statusIdenticon : userOrChannelIdenticon anchors.left: parent.left anchors.leftMargin: Style.current.padding anchors.verticalCenter: parent.verticalCenter diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index 083e494256..fb4839f6f1 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -37,15 +37,14 @@ QtObject { property int wallet: 4 property int privacyAndSecurity: 5 property int appearance: 6 - property int sound: 7 - property int language: 8 - property int notifications: 9 - property int devicesSettings: 10 - property int browserSettings: 11 - property int advanced: 12 - property int needHelp: 13 - property int about: 14 - property int signout: 15 + property int language: 7 + property int notifications: 8 + property int devicesSettings: 9 + property int browserSettings: 10 + property int advanced: 11 + property int needHelp: 12 + property int about: 13 + property int signout: 14 } readonly property QtObject userStatus: QtObject{ @@ -151,7 +150,35 @@ QtObject { readonly property QtObject settingsSection: QtObject { readonly property int itemSpacing: 10 + readonly property int radius: 8 readonly property int mainHeaderFontSize: 28 + readonly property int subHeaderFontSize: 15 + readonly property int infoFontSize: 15 + readonly property int infoLineHeight: 22 + readonly property int infoSpacing: 5 + readonly property int itemHeight: 64 + readonly property int leftMargin: 64 + readonly property int rightMargin: 64 + readonly property int topMargin: 64 + readonly property int bottomMargin: 64 + + readonly property QtObject notificationsBubble: QtObject { + readonly property int previewAnonymous: 0 + readonly property int previewNameOnly: 1 + readonly property int previewNameAndMessage: 2 + } + + readonly property QtObject notifications: QtObject { + readonly property string sendAlertsValue: "sendAlerts" + readonly property string deliverQuietlyValue: "deliverQuietly" + readonly property string turnOffValue: "turnOff" + } + + readonly property QtObject exemptions: QtObject { + readonly property int community: 0 + readonly property int oneToOneChat: 1 + readonly property int groupChat: 2 + } } readonly property int communityImported: 0 @@ -194,9 +221,7 @@ QtObject { readonly property int notifyAllMessages: 0 readonly property int notifyJustMentions: 1 readonly property int notifyNone: 2 - readonly property int notificationPreviewAnonymous: 0 - readonly property int notificationPreviewNameOnly: 1 - readonly property int notificationPreviewNameAndMessage: 2 + readonly property string watchWalletType: "watch" readonly property string keyWalletType: "key"