fix(@desktop/general): clicking push notification does expand the app but does not open correct channel/chat

Logic for adding os notifications for Windows and  MacOs added.

Fixes: #2996
This commit is contained in:
Sale Djenic 2021-08-18 16:43:59 +02:00 committed by Iuri Matias
parent 6e0d8437d4
commit a591e91a2e
20 changed files with 283 additions and 67 deletions

View File

@ -31,6 +31,7 @@ include signal_handling
proc init*(self: ChatController) =
self.handleMailserverEvents()
self.handleChatEvents()
self.handleSystemEvents()
self.handleSignals()
let pubKey = self.status.settings.getSetting[:string](Setting.PublicKey, "0x0")

View File

@ -3,6 +3,7 @@ import # std libs
import # status-desktop libs
../../status/chat/chat as status_chat,
../../status/notifications/os_notifications,
../../status/tasks/marathon,
../../status/tasks/marathon/mailserver/worker,
./views/communities,
@ -206,3 +207,8 @@ proc handleMailserverEvents(self: ChatController) =
slot: "requestAllHistoricMessagesResult"
)
mailserverWorker.start(task)
proc handleSystemEvents(self: ChatController) =
self.status.events.on("osNotificationClicked") do(e:Args):
let arg = OsNotificationsArgs(e)
self.view.onOsNotificationClicked(arg.details)

View File

@ -9,14 +9,16 @@ import ../../status/contacts as status_contacts
import ../../status/ens as status_ens
import ../../status/chat/[chat, message]
import ../../status/profile/profile
import web3/[conversions, ethtypes]
import views/[channels_list, message_list, chat_item, reactions, stickers, groups, transactions, communities, community_list, community_item, format_input, ens, activity_notification_list, channel, messages, message_item, gif]
import ../utils/image_utils
import ../../status/tasks/[qt, task_runner_impl]
import ../../status/tasks/marathon/mailserver/worker
import ../../status/signals/types as signal_types
import ../../status/types
import ../../status/notifications/[os_notifications, os_notification_details]
import ../utils/image_utils
import web3/[conversions, ethtypes]
import views/message_search/[view_controller]
import views/[channels_list, message_list, chat_item, reactions, stickers, groups, transactions, communities, community_list, community_item, format_input, ens, activity_notification_list, channel, messages, message_item, gif]
# TODO: remove me
import ../../status/libstatus/chat as libstatus_chat
@ -503,22 +505,59 @@ QtObject:
echo self.status.mailservers.setMailserver("16Uiu2HAm4v86W3bmT1BiH6oSPzcsSr24iDQpSN5Qa992BCjjwgrD")
echo self.status.mailservers.requestAllHistoricMessages()
proc switchTo*(self: ChatsView, communityId: string, channelId: string,
messageId: string) =
## This method displays community with communityId as an active one (if
## communityId is empty, "Chat" section will be active), then displays
## channel/chat with channelId as an active one and finally display message
## with messageId as a central message in the message list.
if (communityId.len > 0):
self.communities.setActiveCommunity(communityId)
if (channelId.len > 0):
self.channelView.setActiveChannel(channelId)
if (messageId.len > 0):
self.messageView.switchToMessage(messageId)
else:
self.communities.activeCommunity.setActive(false)
if (channelId.len > 0):
self.channelView.setActiveChannel(channelId)
if (messageId.len > 0):
self.messageView.switchToMessage(messageId)
proc switchToSearchedItem*(self: ChatsView, itemId: string) {.slot.} =
let info = self.messageSearchViewController.getItemInfo(itemId)
if(info.isEmpty()):
return
if (info.communityId.len > 0):
self.communities.setActiveCommunity(info.communityId)
if (info.channelId.len > 0):
self.channelView.setActiveChannel(info.channelId)
if (info.messageId.len > 0):
self.messageView.switchToMessage(info.messageId)
else:
self.communities.activeCommunity.setActive(false)
if (info.channelId.len > 0):
self.channelView.setActiveChannel(info.channelId)
if (info.messageId.len > 0):
self.messageView.switchToMessage(info.messageId)
self.switchTo(info.communityId, info.channelId, info.messageId)
proc notificationClicked*(self:ChatsView, notificationType: int) {.signal.}
proc onOsNotificationClicked*(self: ChatsView, details: OsNotificationDetails) =
# A logic what should be done depends on details.notificationType and should be
# defined here in this method.
# So far if notificationType is:
# - NewContactRequest or AcceptedContactRequest we are switching to Chat section
# - JoinCommunityRequest or AcceptedIntoCommunity we are switching to that Community
# - RejectedByCommunity we are switching to Chat section
# - NewMessage we are switching to appropriate chat/channel and a message inside it
self.switchTo(details.communityId, details.channelId, details.messageId)
# Notify qml about the changes, cause changing section cannot be performed
# completely from the nim side.
self.notificationClicked(details.notificationType.int)
proc showOSNotification*(self: ChatsView, title: string, message: string,
notificationType: int, communityId: string, channelId: string,
messageId: string, useOSNotifications: bool) {.slot.} =
let details = OsNotificationDetails(
notificationType: notificationType.OsNotificationType,
communityId: communityId,
channelId: channelId,
messageId: messageId
)
self.status.osnotifications.showNotification(title, message, details,
useOSNotifications)

View File

@ -171,7 +171,10 @@ QtObject:
proc isAddedContact*(self: MessageView, id: string): bool {.slot.} =
result = self.status.contacts.isAdded(id)
proc messageNotificationPushed*(self: MessageView, chatId: string, text: string, contentType: int, chatType: int, timestamp: string, identicon: string, username: string, hasMention: bool, isAddedContact: bool, channelName: string) {.signal.}
proc messageNotificationPushed*(self: MessageView, messageId: string,
communityId: string, chatId: string, text: string, contentType: int,
chatType: int, timestamp: string, identicon: string, username: string,
hasMention: bool, isAddedContact: bool, channelName: string) {.signal.}
proc pushMembers*(self:MessageView, chats: seq[Chat]) =
for chat in chats:
@ -214,7 +217,7 @@ QtObject:
let isEdit = msg.editedAt != "0" or msg.contentType == ContentType.Edit
if not channel.muted and not isEdit and not isGroupSelf:
let isAddedContact = channel.chatType.isOneToOne and self.isAddedContact(channel.id)
self.messageNotificationPushed(msg.chatId, escape_html(msg.text), msg.contentType.int, channel.chatType.int, msg.timestamp, msg.identicon, msg.userName, msg.hasMention, isAddedContact, channel.name)
self.messageNotificationPushed(msg.id, channel.communityId, msg.chatId, escape_html(msg.text), msg.contentType.int, channel.chatType.int, msg.timestamp, msg.identicon, msg.userName, msg.hasMention, isAddedContact, channel.name)
self.channelOpenTime[msg.chatId] = now().toTime.toUnix * 1000

View File

@ -11,6 +11,7 @@ import ../../status/ens as status_ens
import ../../status/chat/chat
import ../../status/types
import ../../status/constants as accountConstants
import ../../status/notifications/[os_notifications, os_notification_details]
import qrcode/qrcode
import ../utils/image_utils
@ -192,3 +193,13 @@ QtObject:
return
self.profile.setSendUserStatus(sendUserStatus)
self.status.saveSetting(Setting.SendUserStatus, sendUserStatus)
proc showOSNotification*(self: ProfileView, title: string, message: string,
notificationType: int, useOSNotifications: bool) {.slot.} =
let details = OsNotificationDetails(
notificationType: notificationType.OsNotificationType
)
self.status.osnotifications.showNotification(title, message, details,
useOSNotifications)

View File

@ -0,0 +1,38 @@
import json
type
OsNotificationType* {.pure.} = enum
NewContactRequest = 1,
AcceptedContactRequest,
JoinCommunityRequest,
AcceptedIntoCommunity,
RejectedByCommunity,
NewMessage
OsNotificationDetails* = object
notificationType*: OsNotificationType
communityId*: string
channelId*: string
messageId*: string
proc toOsNotificationDetails*(json: JsonNode): OsNotificationDetails =
if (not (json.contains("notificationType") and
json.contains("communityId") and
json.contains("channelId") and
json.contains("messageId"))):
return OsNotificationDetails()
return OsNotificationDetails(
notificationType: json{"notificationType"}.getInt.OsNotificationType,
communityId: json{"communityId"}.getStr,
channelId: json{"channelId"}.getStr,
messageId: json{"messageId"}.getStr
)
proc toJsonNode*(self: OsNotificationDetails): JsonNode =
result = %* {
"notificationType": self.notificationType.int,
"communityId": self.communityId,
"channelId": self.channelId,
"messageId": self.messageId
}

View File

@ -0,0 +1,49 @@
import NimQml, json
import ../../eventemitter
import os_notification_details
type
OsNotificationsArgs* = ref object of Args
details*: OsNotificationDetails
QtObject:
type OsNotifications* = ref object of QObject
events: EventEmitter
notification: StatusOSNotificationObject
proc setup(self: OsNotifications, events: EventEmitter) =
self.QObject.setup
self.events = events
self.notification = newStatusOSNotificationObject()
signalConnect(self.notification, "notificationClicked(QString)", self,
"onNotificationClicked(QString)", 2)
proc delete*(self: OsNotifications) =
self.notification.delete
self.QObject.delete
proc newOsNotifications*(events: EventEmitter): OsNotifications =
new(result, delete)
result.setup(events)
proc showNotification*(self: OsNotifications, title: string,
message: string, details: OsNotificationDetails, useOSNotifications: bool) =
## This method will add new notification to the Notification center. Param
## "details" is used to uniquely define a notification bubble.
# Whether we need to use OS notifications or not should be checked only here,
# but because we don't have settings class on the nim side yet, we're using
# useOSNotifications param sent from the qml side. Once we are able to check
# that here, we will remove useOSNotifications param from this method.
if(not useOSNotifications):
return
let identifier = $(details.toJsonNode())
self.notification.showNotification(title, message, identifier)
proc onNotificationClicked*(self: OsNotifications, identifier: string) {.slot.} =
## This slot is called once user clicks a notificaiton bubble, "identifier"
## contains data which uniquely define that notification.
let details = toOsNotificationDetails(parseJson(identifier))
self.events.emit("osNotificationClicked",
OsNotificationsArgs(details: details))

View File

@ -3,6 +3,7 @@ import libstatus/core as libstatus_core
import libstatus/settings as libstatus_settings
import types as libstatus_types
import chat, accounts, wallet, node, network, messages, contacts, profile, stickers, permissions, fleet, settings, mailservers, browser, tokens, provider
import notifications/os_notifications
import ../eventemitter
import ./tasks/task_runner_impl
import bitops, stew/byteutils, chronicles
@ -28,6 +29,7 @@ type Status* = ref object
browser*: BrowserModel
tokens*: TokensModel
provider*: ProviderModel
osnotifications*: OsNotifications
proc newStatusInstance*(fleetConfig: string): Status =
result = Status()
@ -50,6 +52,7 @@ proc newStatusInstance*(fleetConfig: string): Status =
result.browser = browser.newBrowserModel(result.events)
result.tokens = tokens.newTokensModel(result.events)
result.provider = provider.newProviderModel(result.events, result.permissions)
result.osnotifications = newOsNotifications(result.events)
proc initNode*(self: Status) =
self.tasks.init()

@ -1 +1 @@
Subproject commit 73c77c29c2db7918c6fd1654ecd8949c7ce65cbe
Subproject commit a4178bd6dc8a385504a52fc20535ae36966205bf

View File

@ -565,7 +565,7 @@ Item {
positionAtMessage(messageId)
}
onMessageNotificationPushed: function(chatId, msg, contentType, chatType, timestamp, identicon, username, hasMention, isAddedContact, channelName) {
onMessageNotificationPushed: function(messageId, communityId, chatId, msg, contentType, chatType, timestamp, identicon, username, hasMention, isAddedContact, channelName) {
if (appSettings.notificationSetting == Constants.notifyAllMessages ||
(appSettings.notificationSetting == Constants.notifyJustMentions && hasMention)) {
if (chatId === chatsModel.channelView.activeChannel.id && applicationWindow.active === true) {
@ -600,14 +600,17 @@ Item {
}
currentlyHasANotification = true
if (appSettings.useOSNotifications && systemTray.supportsMessages) {
systemTray.showMessage(name,
message,
SystemTrayIcon.NoIcon,
Constants.notificationPopupTTL)
} else {
notificationWindow.notifyUser(chatId, name, message, chatType, identicon, chatColumnLayout.clickOnNotification)
}
// Note:
// Show notification should be moved to the nim side.
// Left here only cause we don't have a way to deal with translations on the nim side.
chatsModel.showOSNotification(name,
message,
Constants.osNotificationType.newMessage,
communityId,
chatId,
messageId,
appSettings.useOSNotifications)
}
}
}

View File

@ -199,27 +199,38 @@ Item {
Connections {
target: chatsModel.communities
// Note:
// Whole this Connection object (both slots) should be moved to the nim side.
// Left here only cause we don't have a way to deal with translations on the nim side.
onMembershipRequestChanged: function (communityId, communityName, accepted) {
chatColumnLayout.currentNotificationChatId = null
chatColumnLayout.currentNotificationCommunityId = communityId
systemTray.showMessage("Status",
//% "You have been accepted into the %1 community"
accepted ? qsTrId("you-have-been-accepted-into-the---1--community").arg(communityName) :
//% "Your request to join the %1 community was declined"
qsTrId("your-request-to-join-the---1--community-was-declined").arg(communityName),
SystemTrayIcon.NoIcon,
Constants.notificationPopupTTL)
chatsModel.showOSNotification("Status",
//% "You have been accepted into the %1 community"
accepted ? qsTrId("you-have-been-accepted-into-the---1--community").arg(communityName) :
//% "Your request to join the %1 community was declined"
qsTrId("your-request-to-join-the---1--community-was-declined").arg(communityName),
accepted? Constants.osNotificationType.acceptedIntoCommunity :
Constants.osNotificationType.rejectedByCommunity,
communityId,
"",
"",
appSettings.useOSNotifications)
}
onMembershipRequestPushed: function (communityId, communityName, pubKey) {
chatColumnLayout.currentNotificationChatId = null
chatColumnLayout.currentNotificationCommunityId = communityId
//% "New membership request"
systemTray.showMessage(qsTrId("new-membership-request"),
//% "%1 asks to join %2"
qsTrId("-1-asks-to-join---2-").arg(Utils.getDisplayName(pubKey)).arg(communityName),
SystemTrayIcon.NoIcon,
Constants.notificationPopupTTL)
chatsModel.showOSNotification(qsTrId("new-membership-request"),
//% "%1 asks to join %2"
qsTrId("-1-asks-to-join---2-").arg(Utils.getDisplayName(pubKey)).arg(communityName),
Constants.osNotificationType.joinCommunityRequest,
communityId,
"",
"",
appSettings.useOSNotifications)
}
}

View File

@ -23,6 +23,12 @@ Item {
property int chatGroupsListViewCount: channelList.chatListItems.count
signal openProfileClicked()
Component.onCompleted: {
appMain.openContactsPopup.connect(function(){
openPopup(contactRequestsPopup)
})
}
MouseArea {
anchors.fill: parent
onClicked: {

View File

@ -33,6 +33,7 @@ Item {
property bool networkGuarded: profileModel.network.current === Constants.networkMainnet || (profileModel.network.current === Constants.networkRopsten && appSettings.stickersEnsRopsten)
signal settingsLoaded()
signal openContactsPopup()
function changeAppSection(section) {
chatsModel.communities.activeCommunity.active = false
@ -453,21 +454,25 @@ Item {
anchors.fill: parent
currentIndex: 0
onCurrentIndexChanged: {
if (typeof this.children[currentIndex].onActivated === "function") {
var obj = this.children[currentIndex];
if(!obj)
return
if (obj.onActivated && typeof obj.onActivated === "function") {
this.children[currentIndex].onActivated()
}
if(this.children[currentIndex] === browserLayoutContainer && browserLayoutContainer.active == false){
if(obj === browserLayoutContainer && browserLayoutContainer.active == false){
browserLayoutContainer.active = true;
}
timelineLayoutContainer.active = this.children[currentIndex] === timelineLayoutContainer
timelineLayoutContainer.active = obj === timelineLayoutContainer
if(this.children[currentIndex] === walletLayoutContainer){
if(obj === walletLayoutContainer){
walletLayoutContainer.showSigningPhrasePopup();
}
if(this.children[currentIndex] === walletV2LayoutContainer){
if(obj === walletV2LayoutContainer){
walletV2LayoutContainer.showSigningPhrasePopup();
}
}
@ -612,6 +617,31 @@ Item {
}
}
Connections {
target: chatsModel
onNotificationClicked: {
applicationWindow.makeStatusAppActive()
switch(notificationType){
case Constants.osNotificationType.newContactRequest:
appView.currentIndex = Utils.getAppSectionIndex(Constants.chat)
appMain.openContactsPopup()
break
case Constants.osNotificationType.acceptedContactRequest:
appView.currentIndex = Utils.getAppSectionIndex(Constants.chat)
break
case Constants.osNotificationType.joinCommunityRequest:
case Constants.osNotificationType.acceptedIntoCommunity:
case Constants.osNotificationType.rejectedByCommunity:
appView.currentIndex = Utils.getAppSectionIndex(Constants.community)
break
case Constants.osNotificationType.newMessage:
appView.currentIndex = Utils.getAppSectionIndex(Constants.chat)
break
}
}
}
Connections {
target: profileModel
ignoreUnknownSignals: true
@ -627,17 +657,24 @@ Item {
if (!appSettings.notifyOnNewRequests) {
return
}
const isContact = profileModel.contacts.isAdded(address)
// Note:
// Whole this Connection object should be moved to the nim side.
// Left here only cause we don't have a way to deal with translations on the nim side.
//% "Contact request accepted"
systemTray.showMessage(isContact ? qsTrId("contact-request-accepted") :
//% "New contact request"
qsTrId("new-contact-request"),
//% "You can now chat with %1"
isContact ? qsTrId("you-can-now-chat-with--1").arg(Utils.removeStatusEns(name)) :
//% "%1 requests to become contacts"
qsTrId("-1-requests-to-become-contacts").arg(Utils.removeStatusEns(name)),
SystemTrayIcon.NoIcon,
Constants.notificationPopupTTL)
profileModel.showOSNotification(isContact ? qsTrId("contact-request-accepted") :
//% "New contact request"
qsTrId("new-contact-request"),
//% "You can now chat with %1"
isContact ? qsTrId("you-can-now-chat-with--1").arg(Utils.removeStatusEns(name)) :
//% "%1 requests to become contacts"
qsTrId("-1-requests-to-become-contacts").arg(Utils.removeStatusEns(name)),
isContact? Constants.osNotificationType.acceptedContactRequest :
Constants.osNotificationType.newContactRequest,
appSettings.useOSNotifications)
}
}
@ -798,4 +835,4 @@ Item {
Designer {
D{i:0;formeditorZoom:1.75;height:770;width:1232}
}
##^##*/
##^##*/

View File

@ -182,4 +182,13 @@ QtObject {
//% "Continuing will require a transaction to connect the username with your current chat key."
"connected-different-key": qsTrId("ens-username-connected-with-different-key"),
}
readonly property QtObject osNotificationType: QtObject{
readonly property int newContactRequest: 1
readonly property int acceptedContactRequest: 2
readonly property int joinCommunityRequest: 3
readonly property int acceptedIntoCommunity: 4
readonly property int rejectedByCommunity: 5
readonly property int newMessage: 6
}
}

View File

@ -147,6 +147,12 @@ StatusWindow {
property bool currentlyHasANotification: false
function makeStatusAppActive() {
applicationWindow.show()
applicationWindow.raise()
applicationWindow.requestActivate()
}
SystemTrayIcon {
id: systemTray
visible: true
@ -164,18 +170,12 @@ StatusWindow {
}
}
function openStatusWindow() {
applicationWindow.show()
applicationWindow.raise()
applicationWindow.requestActivate()
}
menu: Menu {
MenuItem {
//% "Open Status"
text: qsTrId("open-status")
onTriggered: {
systemTray.openStatusWindow()
applicationWindow.makeStatusAppActive()
}
}
@ -191,7 +191,7 @@ StatusWindow {
onActivated: {
if (reason !== SystemTrayIcon.Context) {
openStatusWindow()
applicationWindow.makeStatusAppActive()
}
}
}

2
vendor/DOtherSide vendored

@ -1 +1 @@
Subproject commit caafbf9430e1957d7f8c47363ef031c6c7972718
Subproject commit 5ff11d3eb34cd36a5765b983dab360a8daff4de7

@ -1 +1 @@
Subproject commit b60faf96449cb9ba9c6494abf7a3fcbaa7849c17
Subproject commit 2b6e50491786ae0d61a97f99edda27b70364838a

@ -1 +1 @@
Subproject commit 6589cedffa2eb083f838d15c70c45ce000596859
Subproject commit e7694f16ceccd7c98ecb6870263025018b7d37b3

2
vendor/nimqml vendored

@ -1 +1 @@
Subproject commit 536401f0c1aa28e7e406ef9547c93821b641260b
Subproject commit f2f8f3a9e921e93b7f5d1961600919611b300066

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit 192ca6e3190714a888a8d585a1113eaffe0b7d9c
Subproject commit b8959e3f66159b142fb6706b89f4ad8295944ee4