parent
c2ffb4aee7
commit
cd44b8a606
|
@ -34,11 +34,10 @@ proc init*(self: ChatController) =
|
|||
self.handleSignals()
|
||||
|
||||
let pubKey = self.status.settings.getSetting[:string](Setting.PublicKey, "0x0")
|
||||
let messagesFromContactsOnly = self.status.settings.getSetting[:bool](Setting.MessagesFromContactsOnly, false, true)
|
||||
|
||||
# self.view.pubKey = pubKey
|
||||
self.view.setPubKey(pubKey)
|
||||
self.status.chat.init(pubKey, messagesFromContactsOnly)
|
||||
self.status.chat.init(pubKey)
|
||||
self.status.stickers.init()
|
||||
self.view.reactions.init()
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ type
|
|||
Read = UserRole + 7
|
||||
Dismissed = UserRole + 8
|
||||
Accepted = UserRole + 9
|
||||
Author = UserRole + 10
|
||||
|
||||
QtObject:
|
||||
type
|
||||
|
@ -71,6 +72,7 @@ QtObject:
|
|||
of NotifRoles.Id: result = newQVariant(acitivityNotificationItem.id)
|
||||
of NotifRoles.ChatId: result = newQVariant(acitivityNotificationItem.chatId)
|
||||
of NotifRoles.Name: result = newQVariant(acitivityNotificationItem.name)
|
||||
of NotifRoles.Author: result = newQVariant(acitivityNotificationItem.author)
|
||||
of NotifRoles.NotificationType: result = newQVariant(acitivityNotificationItem.notificationType.int)
|
||||
of NotifRoles.Message: result = newQVariant(acitivityNotificationItem.messageItem)
|
||||
of NotifRoles.Timestamp: result = newQVariant(acitivityNotificationItem.timestamp)
|
||||
|
@ -86,6 +88,7 @@ QtObject:
|
|||
of "id": result = notif.id
|
||||
of "chatId": result = notif.chatId
|
||||
of "name": result = notif.name
|
||||
of "author": result = notif.author
|
||||
of "notificationType": result = $(notif.notificationType.int)
|
||||
of "timestamp": result = $(notif.timestamp)
|
||||
of "read": result = $(notif.read)
|
||||
|
@ -98,6 +101,7 @@ QtObject:
|
|||
NotifRoles.Id.int:"id",
|
||||
NotifRoles.ChatId.int:"chatId",
|
||||
NotifRoles.Name.int: "name",
|
||||
NotifRoles.Author.int: "author",
|
||||
NotifRoles.NotificationType.int: "notificationType",
|
||||
NotifRoles.Message.int: "message",
|
||||
NotifRoles.Timestamp.int: "timestamp",
|
||||
|
@ -145,11 +149,54 @@ QtObject:
|
|||
let topLeft = self.createIndex(i, 0, nil)
|
||||
let bottomRight = self.createIndex(i, 0, nil)
|
||||
self.dataChanged(topLeft, bottomRight, @[NotifRoles.Read.int])
|
||||
break
|
||||
i = i + 1
|
||||
|
||||
proc markActivityCenterNotificationRead(self: ActivityNotificationList, id: string): string {.slot.} =
|
||||
self.markActivityCenterNotificationsRead(fmt"[""{id}""]")
|
||||
|
||||
proc removeNotifications(self: ActivityNotificationList, ids: seq[string]) =
|
||||
var i = 0
|
||||
var indexesToDelete: seq[int] = @[]
|
||||
for activityCenterNotification in self.activityCenterNotifications:
|
||||
for id in ids:
|
||||
if (activityCenterNotification.id == id):
|
||||
indexesToDelete.add(i)
|
||||
break
|
||||
i = i + 1
|
||||
|
||||
i = 0
|
||||
for index in indexesToDelete:
|
||||
let indexUpdated = index - i
|
||||
self.beginRemoveRows(newQModelIndex(), indexUpdated, indexUpdated)
|
||||
self.activityCenterNotifications.delete(indexUpdated)
|
||||
self.endRemoveRows()
|
||||
i = i + 1
|
||||
|
||||
proc acceptActivityCenterNotifications(self: ActivityNotificationList, idsJson: string): string {.slot.} =
|
||||
let ids = map(parseJson(idsJson).getElems(), proc(x:JsonNode):string = x.getStr())
|
||||
|
||||
let error = self.status.chat.acceptActivityCenterNotifications(ids)
|
||||
if (error != ""):
|
||||
return error
|
||||
|
||||
self.removeNotifications(ids)
|
||||
|
||||
proc acceptActivityCenterNotification(self: ActivityNotificationList, id: string): string {.slot.} =
|
||||
self.acceptActivityCenterNotifications(fmt"[""{id}""]")
|
||||
|
||||
proc dismissActivityCenterNotifications(self: ActivityNotificationList, idsJson: string): string {.slot.} =
|
||||
let ids = map(parseJson(idsJson).getElems(), proc(x:JsonNode):string = x.getStr())
|
||||
|
||||
let error = self.status.chat.dismissActivityCenterNotifications(ids)
|
||||
if (error != ""):
|
||||
return error
|
||||
|
||||
self.removeNotifications(ids)
|
||||
|
||||
proc dismissActivityCenterNotification(self: ActivityNotificationList, id: string): string {.slot.} =
|
||||
self.dismissActivityCenterNotifications(fmt"[""{id}""]")
|
||||
|
||||
proc toActivityCenterNotificationViewItem*(self: ActivityNotificationList, activityCenterNotification: ActivityCenterNotification): ActivityCenterNotificationViewItem =
|
||||
let communityId = self.status.chat.getCommunityIdForChat(activityCenterNotification.chatId)
|
||||
activityCenterNotification.message.communityId = communityId
|
||||
|
@ -158,6 +205,7 @@ QtObject:
|
|||
chatId: activityCenterNotification.chatId,
|
||||
name: activityCenterNotification.name,
|
||||
notificationType: activityCenterNotification.notificationType,
|
||||
author: activityCenterNotification.author,
|
||||
timestamp: activityCenterNotification.timestamp,
|
||||
read: activityCenterNotification.read,
|
||||
dismissed: activityCenterNotification.dismissed,
|
||||
|
|
|
@ -52,7 +52,6 @@ type
|
|||
|
||||
ChatModel* = ref object
|
||||
publicKey*: string
|
||||
messagesFromContactsOnly*: bool
|
||||
events*: EventEmitter
|
||||
communitiesToFetch*: seq[string]
|
||||
mailserverReady*: bool
|
||||
|
@ -72,7 +71,6 @@ include chat/utils
|
|||
|
||||
proc newChatModel*(events: EventEmitter): ChatModel =
|
||||
result = ChatModel()
|
||||
result.messagesFromContactsOnly = false
|
||||
result.events = events
|
||||
result.mailserverReady = false
|
||||
result.communitiesToFetch = @[]
|
||||
|
@ -86,37 +84,9 @@ proc newChatModel*(events: EventEmitter): ChatModel =
|
|||
proc delete*(self: ChatModel) =
|
||||
discard
|
||||
|
||||
proc cleanSpamChatGroups(self: ChatModel, chats: seq[Chat], contacts: seq[Profile]): seq[Chat] =
|
||||
for chat in chats:
|
||||
if not chat.isActive: continue
|
||||
if chat.chatType == ChatType.PrivateGroupChat:
|
||||
var isContact = false
|
||||
var joined = false
|
||||
for member in chat.members:
|
||||
if member.id == self.publicKey and member.joined:
|
||||
joined = true
|
||||
if member.admin and member.joined:
|
||||
for contact in contacts:
|
||||
if contact.address == member.id:
|
||||
isContact = true
|
||||
if not isContact and not joined:
|
||||
discard status_chat.deactivateChat(chat)
|
||||
else:
|
||||
result.add(chat)
|
||||
else:
|
||||
result.add(chat)
|
||||
|
||||
proc update*(self: ChatModel, chats: seq[Chat], messages: seq[Message], emojiReactions: seq[Reaction], communities: seq[Community], communityMembershipRequests: seq[CommunityMembershipRequest], pinnedMessages: seq[Message], activityCenterNotifications: seq[ActivityCenterNotification]) =
|
||||
var contacts = getAddedContacts()
|
||||
|
||||
var chatList = chats
|
||||
if (self.messagesFromContactsOnly):
|
||||
# Automatically decline chat group invitations if admin is not a contact
|
||||
chatList = self.cleanSpamChatGroups(chats, contacts)
|
||||
|
||||
for chat in chatList:
|
||||
if chat.isActive:
|
||||
self.channels[chat.id] = chat
|
||||
for chat in chats:
|
||||
self.channels[chat.id] = chat
|
||||
|
||||
for message in messages:
|
||||
let chatId = message.chatId
|
||||
|
@ -127,7 +97,7 @@ proc update*(self: ChatModel, chats: seq[Chat], messages: seq[Message], emojiRea
|
|||
if self.lastMessageTimestamps[chatId] > ts:
|
||||
self.lastMessageTimestamps[chatId] = ts
|
||||
|
||||
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages,chats: chatList, contacts: @[], emojiReactions: emojiReactions, communities: communities, communityMembershipRequests: communityMembershipRequests, pinnedMessages: pinnedMessages, activityCenterNotifications: activityCenterNotifications))
|
||||
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages,chats: chats, contacts: @[], emojiReactions: emojiReactions, communities: communities, communityMembershipRequests: communityMembershipRequests, pinnedMessages: pinnedMessages, activityCenterNotifications: activityCenterNotifications))
|
||||
|
||||
proc hasChannel*(self: ChatModel, chatId: string): bool =
|
||||
self.channels.hasKey(chatId)
|
||||
|
@ -178,16 +148,12 @@ proc requestMissingCommunityInfos*(self: ChatModel) =
|
|||
for communityId in self.communitiesToFetch:
|
||||
status_chat.requestCommunityInfo(communityId)
|
||||
|
||||
proc init*(self: ChatModel, pubKey: string, messagesFromContactsOnly: bool) =
|
||||
proc init*(self: ChatModel, pubKey: string) =
|
||||
self.publicKey = pubKey
|
||||
self.messagesFromContactsOnly = messagesFromContactsOnly
|
||||
|
||||
var contacts = getAddedContacts()
|
||||
var chatList = status_chat.loadChats()
|
||||
|
||||
if (messagesFromContactsOnly):
|
||||
chatList = self.cleanSpamChatGroups(chatList, contacts)
|
||||
|
||||
let profileUpdatesChatIds = chatList.filter(c => c.chatType == ChatType.Profile).map(c => c.id)
|
||||
|
||||
if chatList.filter(c => c.chatType == ChatType.Timeline).len == 0:
|
||||
|
@ -576,6 +542,25 @@ proc markActivityCenterNotificationsRead*(self: ChatModel, ids: seq[string]): st
|
|||
error "Error marking as read", msg = e.msg
|
||||
result = e.msg
|
||||
|
||||
proc acceptActivityCenterNotifications*(self: ChatModel, ids: seq[string]): string =
|
||||
try:
|
||||
let response = status_chat.acceptActivityCenterNotifications(ids)
|
||||
|
||||
let resultTuple = self.processChatUpdate(parseJson(response))
|
||||
let (chats, messages) = resultTuple
|
||||
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats))
|
||||
|
||||
except Exception as e:
|
||||
error "Error marking as accepted", msg = e.msg
|
||||
result = e.msg
|
||||
|
||||
proc dismissActivityCenterNotifications*(self: ChatModel, ids: seq[string]): string =
|
||||
try:
|
||||
discard status_chat.dismissActivityCenterNotifications(ids)
|
||||
except Exception as e:
|
||||
error "Error marking as dismissed", msg = e.msg
|
||||
result = e.msg
|
||||
|
||||
proc unreadActivityCenterNotificationsCount*(self: ChatModel): int =
|
||||
status_chat.unreadActivityCenterNotificationsCount()
|
||||
|
||||
|
|
|
@ -134,6 +134,7 @@ type ActivityCenterNotification* = ref object of RootObj
|
|||
id*: string # ID is the id of the chat, for public chats it is the name e.g. status, for one-to-one is the hex encoded public key and for group chats is a random uuid appended with the hex encoded pk of the creator of the chat
|
||||
chatId*: string
|
||||
name*: string
|
||||
author*: string
|
||||
notificationType*: ActivityCenterNotificationType
|
||||
message*: Message
|
||||
timestamp*: int64
|
||||
|
|
|
@ -604,7 +604,13 @@ proc markAllActivityCenterNotificationsRead*() =
|
|||
discard callPrivateRPC("markAllActivityCenterNotificationsRead".prefix, %*[])
|
||||
|
||||
proc markActivityCenterNotificationsRead*(ids: seq[string]) =
|
||||
let res = callPrivateRPC("markActivityCenterNotificationsRead".prefix, %*[ids])
|
||||
discard callPrivateRPC("markActivityCenterNotificationsRead".prefix, %*[ids])
|
||||
|
||||
proc acceptActivityCenterNotifications*(ids: seq[string]): string =
|
||||
result = callPrivateRPC("acceptActivityCenterNotifications".prefix, %*[ids])
|
||||
|
||||
proc dismissActivityCenterNotifications*(ids: seq[string]): string =
|
||||
result = callPrivateRPC("dismissActivityCenterNotifications".prefix, %*[ids])
|
||||
|
||||
proc unreadActivityCenterNotificationsCount*(): int =
|
||||
let rpcResult = callPrivateRPC("unreadActivityCenterNotificationsCount".prefix, %*[]).parseJson
|
||||
|
|
|
@ -385,6 +385,7 @@ proc toActivityCenterNotification*(jsonNotification: JsonNode, pk: string): Acti
|
|||
id: jsonNotification{"id"}.getStr,
|
||||
chatId: jsonNotification{"chatId"}.getStr,
|
||||
name: jsonNotification{"name"}.getStr,
|
||||
author: jsonNotification{"author"}.getStr,
|
||||
notificationType: activityCenterNotificationType,
|
||||
timestamp: jsonNotification{"timestamp"}.getInt,
|
||||
read: jsonNotification{"read"}.getBool,
|
||||
|
|
|
@ -104,6 +104,7 @@ Popup {
|
|||
|
||||
DelegateModelGeneralized {
|
||||
id: notifDelegateList
|
||||
|
||||
lessThan: [
|
||||
function(left, right) { return left.timestamp > right.timestamp }
|
||||
]
|
||||
|
@ -135,15 +136,30 @@ Popup {
|
|||
}
|
||||
|
||||
Loader {
|
||||
property int previousNotificationIndex: {
|
||||
if (notificationDelegate.idx === 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
// This is used in order to have access to the previous message and determine the timestamp
|
||||
// we can't rely on the index because the sequence of messages is not ordered on the nim side
|
||||
if (notificationDelegate.idx < notifDelegateList.items.count - 1) {
|
||||
return notifDelegateList.items.get(notificationDelegate.idx - 1).model.index
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
property string previousNotificationTimestamp: notificationDelegate.idx === 0 ? "" : chatsModel.activityNotificationList.getNotificationData(previousNotificationIndex, "timestamp")
|
||||
|
||||
|
||||
id: notifLoader
|
||||
anchors.top: parent.top
|
||||
active: !!sourceComponent
|
||||
width: parent.width
|
||||
height: active && item.visible ? item.height : 0
|
||||
sourceComponent: {
|
||||
switch (model.notificationType) {
|
||||
case Constants.activityCenterNotificationTypeMention:return messageNotificationComponent
|
||||
case Constants.activityCenterNotificationTypeReply: return messageNotificationComponent
|
||||
case Constants.activityCenterNotificationTypeGroupRequest: return groupRequestNotificationComponent
|
||||
default: return null
|
||||
}
|
||||
}
|
||||
|
@ -152,128 +168,13 @@ Popup {
|
|||
Component {
|
||||
id: messageNotificationComponent
|
||||
|
||||
Item {
|
||||
visible: {
|
||||
if (hideReadNotifications && model.read) {
|
||||
return false
|
||||
}
|
||||
ActivityCenterMessageComponent {}
|
||||
}
|
||||
|
||||
return activityCenter.currentFilter === ActivityCenter.Filter.All ||
|
||||
(model.notificationType === Constants.activityCenterNotificationTypeMention && activityCenter.currentFilter === ActivityCenter.Filter.Mentions) ||
|
||||
(model.notificationType === Constants.activityCenterNotificationTypeReply && activityCenter.currentFilter === ActivityCenter.Filter.Replies)
|
||||
}
|
||||
width: parent.width
|
||||
height: messageNotificationContent.height
|
||||
Component {
|
||||
id: groupRequestNotificationComponent
|
||||
|
||||
StatusIconButton {
|
||||
id: markReadBtn
|
||||
icon.name: "double-check"
|
||||
iconColor: Style.current.primary
|
||||
icon.width: 24
|
||||
icon.height: 24
|
||||
width: 32
|
||||
height: 32
|
||||
onClicked: chatsModel.activityNotificationList.markActivityCenterNotificationRead(model.id)
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 12
|
||||
anchors.verticalCenter: messageNotificationContent.verticalCenter
|
||||
z: 52
|
||||
|
||||
StatusToolTip {
|
||||
visible: markReadBtn.hovered
|
||||
text: qsTr("Mark as Read")
|
||||
orientation: "left"
|
||||
x: - width - Style.current.padding
|
||||
y: markReadBtn.height / 2 - height / 2 + 4
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: messageNotificationContent
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
Message {
|
||||
id: notificationMessage
|
||||
anchors.right: undefined
|
||||
fromAuthor: model.message.fromAuthor
|
||||
chatId: model.message.chatId
|
||||
userName: model.message.userName
|
||||
alias: model.message.alias
|
||||
localName: model.message.localName
|
||||
message: model.message.message
|
||||
plainText: model.message.plainText
|
||||
identicon: model.message.identicon
|
||||
isCurrentUser: model.message.isCurrentUser
|
||||
timestamp: model.message.timestamp
|
||||
sticker: model.message.sticker
|
||||
contentType: model.message.contentType
|
||||
outgoingStatus: model.message.outgoingStatus
|
||||
responseTo: model.message.responseTo
|
||||
imageClick: imagePopup.openPopup.bind(imagePopup)
|
||||
messageId: model.message.messageId
|
||||
linkUrls: model.message.linkUrls
|
||||
communityId: model.message.communityId
|
||||
hasMention: model.message.hasMention
|
||||
stickerPackId: model.message.stickerPackId
|
||||
pinnedBy: model.message.pinnedBy
|
||||
pinnedMessage: model.message.isPinned
|
||||
activityCenterMessage: true
|
||||
read: model.read
|
||||
clickMessage: function (isProfileClick) {
|
||||
if (isProfileClick) {
|
||||
const pk = model.message.fromAuthor
|
||||
const userProfileImage = appMain.getProfileImage(pk)
|
||||
return openProfilePopup(chatsModel.userNameOrAlias(pk), pk, userProfileImage || utilsModel.generateIdenticon(pk))
|
||||
}
|
||||
|
||||
activityCenter.close()
|
||||
|
||||
if (model.message.communityId) {
|
||||
chatsModel.communities.setActiveCommunity(model.message.communityId)
|
||||
}
|
||||
|
||||
chatsModel.channelView.setActiveChannel(model.message.chatId)
|
||||
positionAtMessage(model.message.messageId)
|
||||
}
|
||||
|
||||
prevMessageIndex: {
|
||||
if (notificationDelegate.idx === 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
// This is used in order to have access to the previous message and determine the timestamp
|
||||
// we can't rely on the index because the sequence of messages is not ordered on the nim side
|
||||
if (notificationDelegate.idx < notifDelegateList.items.count - 1) {
|
||||
return notifDelegateList.items.get(notificationDelegate.idx - 1).model.index
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
prevMsgTimestamp: notificationDelegate.idx === 0 ? "" : chatsModel.activityNotificationList.getNotificationData(prevMessageIndex, "timestamp")
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.top: notificationMessage.bottom
|
||||
anchors.bottom: badge.bottom
|
||||
anchors.bottomMargin: -Style.current.smallPadding
|
||||
width: parent.width
|
||||
color: model.read ? Style.current.transparent : Utils.setColorAlpha(Style.current.blue, 0.1)
|
||||
|
||||
}
|
||||
|
||||
ActivityChannelBadge {
|
||||
id: badge
|
||||
name: model.name
|
||||
chatId: model.chatId
|
||||
notificationType: model.notificationType
|
||||
responseTo: model.message.responseTo
|
||||
communityId: model.message.communityId
|
||||
anchors.top: notificationMessage.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 61 // TODO find a way to align with the text of the message
|
||||
}
|
||||
}
|
||||
}
|
||||
ActivityCenterGroupRequest {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
import QtQuick 2.13
|
||||
import QtGraphicalEffects 1.13
|
||||
import "../../../../../imports"
|
||||
import "../../../../../shared"
|
||||
import "../../../../../shared/status"
|
||||
import "../MessageComponents"
|
||||
import "../../components"
|
||||
import ".."
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: childrenRect.height + dateGroupLbl.anchors.topMargin
|
||||
|
||||
DateGroup {
|
||||
id: dateGroupLbl
|
||||
previousMessageIndex: previousNotificationIndex
|
||||
previousMessageTimestamp: previousNotificationTimestamp
|
||||
messageTimestamp: model.timestamp
|
||||
isActivityCenterMessage: true
|
||||
height: visible ? implicitHeight : 0
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: groupRequestContent
|
||||
property string timestamp: model.timestamp
|
||||
|
||||
visible: {
|
||||
if (hideReadNotifications && model.read) {
|
||||
return false
|
||||
}
|
||||
|
||||
return activityCenter.currentFilter === ActivityCenter.Filter.All
|
||||
}
|
||||
width: parent.width
|
||||
height: visible ? 60 : 0
|
||||
anchors.top: dateGroupLbl.bottom
|
||||
anchors.topMargin: dateGroupLbl.visible ? 4 : 0
|
||||
color: model.read ? Style.current.transparent : Utils.setColorAlpha(Style.current.blue, 0.1)
|
||||
|
||||
StatusIdenticon {
|
||||
id: channelIdenticon
|
||||
height: 40
|
||||
width: 40
|
||||
chatId: model.chatId
|
||||
chatName: model.name
|
||||
chatType: Constants.chatTypePrivateGroupChat
|
||||
identicon: ""
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
id: nameItem
|
||||
width: childrenRect.width
|
||||
height: chatName.name
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Style.current.halfPadding
|
||||
// TODO fix anchoring to center when there is no author
|
||||
// anchors.top: inviteText.visible ? parent.top: undefined
|
||||
// anchors.topMargin: inviteText.visible ? Style.current.halfPadding : 0
|
||||
// anchors.verticalCenter: inviteText.visible ? undefined : parent.verticalCenter
|
||||
anchors.left: channelIdenticon.right
|
||||
anchors.leftMargin: Style.current.halfPadding
|
||||
|
||||
SVGImage {
|
||||
id: groupImage
|
||||
width: 16
|
||||
height: 16
|
||||
anchors.verticalCenter: chatName.verticalCenter
|
||||
anchors.left: parent.left
|
||||
source: "../../../../img/channel-icon-group.svg"
|
||||
|
||||
ColorOverlay {
|
||||
anchors.fill: parent
|
||||
source: parent
|
||||
color: chatName.color
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: chatName
|
||||
text: model.name
|
||||
anchors.left: groupImage.right
|
||||
anchors.leftMargin: 4
|
||||
font.pixelSize: 15
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
ChatTime {
|
||||
anchors.verticalCenter: chatName.verticalCenter
|
||||
anchors.left: chatName.right
|
||||
anchors.leftMargin: 4
|
||||
font.pixelSize: 10
|
||||
visible: true
|
||||
color: Style.current.secondaryText
|
||||
}
|
||||
}
|
||||
|
||||
function openProfile() {
|
||||
const pk = model.author
|
||||
const userProfileImage = appMain.getProfileImage(pk)
|
||||
openProfilePopup(chatsModel.userNameOrAlias(pk), pk, userProfileImage || utilsModel.generateIdenticon(pk))
|
||||
}
|
||||
|
||||
StyledTextEdit {
|
||||
id: inviteText
|
||||
visible: !!model.author
|
||||
text: {
|
||||
if (!visible) {
|
||||
return ""
|
||||
}
|
||||
|
||||
let name = chatsModel.userNameOrAlias(model.author)
|
||||
if (name.length > 20) {
|
||||
name = name.substring(0, 9) + "..." + name.substring(name.length - 10)
|
||||
}
|
||||
|
||||
return qsTr("%1 invited you to join the group")
|
||||
.arg(`<style type="text/css">`+
|
||||
`a {`+
|
||||
`color: ${Style.current.primary};`+
|
||||
`text-decoration: none;` +
|
||||
`}`+
|
||||
`</style>`+
|
||||
`<a href="#">${name}</a>`)
|
||||
}
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Style.current.halfPadding
|
||||
anchors.left: nameItem.left
|
||||
anchors.right: buttons.left
|
||||
anchors.rightMargin: Style.current.halfPadding
|
||||
clip: true
|
||||
font.pixelSize: 15
|
||||
font.weight: Font.Medium
|
||||
readOnly: true
|
||||
selectByMouse: true
|
||||
textFormat: Text.RichText
|
||||
onLinkActivated: groupRequestContent.openProfile()
|
||||
onLinkHovered: {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
||||
|
||||
AcceptRejectOptionsButtons {
|
||||
id: buttons
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.halfPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onAcceptClicked: chatsModel.activityNotificationList.acceptActivityCenterNotification(model.id)
|
||||
onDeclineClicked: chatsModel.activityNotificationList.dismissActivityCenterNotification(model.id)
|
||||
onProfileClicked: groupRequestContent.openProfile()
|
||||
onBlockClicked: {
|
||||
const pk = model.author
|
||||
blockContactConfirmationDialog.contactName = chatsModel.userNameOrAlias(pk)
|
||||
blockContactConfirmationDialog.contactAddress = pk
|
||||
blockContactConfirmationDialog.open()
|
||||
}
|
||||
|
||||
BlockContactConfirmationDialog {
|
||||
id: blockContactConfirmationDialog
|
||||
onBlockButtonClicked: {
|
||||
profileModel.contacts.blockContact(blockContactConfirmationDialog.contactAddress)
|
||||
chatsModel.activityNotificationList.dismissActivityCenterNotification(model.id)
|
||||
blockContactConfirmationDialog.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
import QtQuick 2.13
|
||||
import "../../../../../imports"
|
||||
import "../../../../../shared"
|
||||
import "../../../../../shared/status"
|
||||
import ".."
|
||||
|
||||
|
||||
Item {
|
||||
visible: {
|
||||
if (hideReadNotifications && model.read) {
|
||||
return false
|
||||
}
|
||||
|
||||
return activityCenter.currentFilter === ActivityCenter.Filter.All ||
|
||||
(model.notificationType === Constants.activityCenterNotificationTypeMention && activityCenter.currentFilter === ActivityCenter.Filter.Mentions) ||
|
||||
(model.notificationType === Constants.activityCenterNotificationTypeReply && activityCenter.currentFilter === ActivityCenter.Filter.Replies)
|
||||
}
|
||||
width: parent.width
|
||||
height: visible ? messageNotificationContent.height : 0
|
||||
|
||||
StatusIconButton {
|
||||
id: markReadBtn
|
||||
icon.name: "double-check"
|
||||
iconColor: Style.current.primary
|
||||
icon.width: 24
|
||||
icon.height: 24
|
||||
width: 32
|
||||
height: 32
|
||||
onClicked: chatsModel.activityNotificationList.markActivityCenterNotificationRead(model.id)
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 12
|
||||
anchors.verticalCenter: messageNotificationContent.verticalCenter
|
||||
z: 52
|
||||
|
||||
StatusToolTip {
|
||||
visible: markReadBtn.hovered
|
||||
text: qsTr("Mark as Read")
|
||||
orientation: "left"
|
||||
x: - width - Style.current.padding
|
||||
y: markReadBtn.height / 2 - height / 2 + 4
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: messageNotificationContent
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
Message {
|
||||
id: notificationMessage
|
||||
anchors.right: undefined
|
||||
fromAuthor: model.message.fromAuthor
|
||||
chatId: model.message.chatId
|
||||
userName: model.message.userName
|
||||
alias: model.message.alias
|
||||
localName: model.message.localName
|
||||
message: model.message.message
|
||||
plainText: model.message.plainText
|
||||
identicon: model.message.identicon
|
||||
isCurrentUser: model.message.isCurrentUser
|
||||
timestamp: model.message.timestamp
|
||||
sticker: model.message.sticker
|
||||
contentType: model.message.contentType
|
||||
outgoingStatus: model.message.outgoingStatus
|
||||
responseTo: model.message.responseTo
|
||||
imageClick: imagePopup.openPopup.bind(imagePopup)
|
||||
messageId: model.message.messageId
|
||||
linkUrls: model.message.linkUrls
|
||||
communityId: model.message.communityId
|
||||
hasMention: model.message.hasMention
|
||||
stickerPackId: model.message.stickerPackId
|
||||
pinnedBy: model.message.pinnedBy
|
||||
pinnedMessage: model.message.isPinned
|
||||
activityCenterMessage: true
|
||||
read: model.read
|
||||
clickMessage: function (isProfileClick) {
|
||||
if (isProfileClick) {
|
||||
const pk = model.message.fromAuthor
|
||||
const userProfileImage = appMain.getProfileImage(pk)
|
||||
return openProfilePopup(chatsModel.userNameOrAlias(pk), pk, userProfileImage || utilsModel.generateIdenticon(pk))
|
||||
}
|
||||
|
||||
activityCenter.close()
|
||||
|
||||
if (model.message.communityId) {
|
||||
chatsModel.communities.setActiveCommunity(model.message.communityId)
|
||||
}
|
||||
|
||||
chatsModel.channelView.setActiveChannel(model.message.chatId)
|
||||
positionAtMessage(model.message.messageId)
|
||||
}
|
||||
|
||||
prevMessageIndex: previousNotificationIndex
|
||||
prevMsgTimestamp: previousNotificationTimestamp
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.top: notificationMessage.bottom
|
||||
anchors.bottom: badge.bottom
|
||||
anchors.bottomMargin: -Style.current.smallPadding
|
||||
width: parent.width
|
||||
color: model.read ? Style.current.transparent : Utils.setColorAlpha(Style.current.blue, 0.1)
|
||||
|
||||
}
|
||||
|
||||
ActivityChannelBadge {
|
||||
id: badge
|
||||
name: model.name
|
||||
chatId: model.chatId
|
||||
notificationType: model.notificationType
|
||||
responseTo: model.message.responseTo
|
||||
communityId: model.message.communityId
|
||||
anchors.top: notificationMessage.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 61 // TODO find a way to align with the text of the message
|
||||
}
|
||||
}
|
||||
}
|
|
@ -58,6 +58,7 @@ Item {
|
|||
previousMessageIndex: prevMessageIndex
|
||||
previousMessageTimestamp: prevMsgTimestamp
|
||||
messageTimestamp: timestamp
|
||||
isActivityCenterMessage: activityCenterMessage
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
|
|
@ -3,6 +3,7 @@ import "../../../../../shared"
|
|||
import "../../../../../imports"
|
||||
|
||||
StyledText {
|
||||
property bool isActivityCenterMessage: false
|
||||
property int previousMessageIndex: -1
|
||||
property string previousMessageTimestamp
|
||||
property string messageTimestamp
|
||||
|
@ -11,11 +12,11 @@ StyledText {
|
|||
font.pixelSize: 13
|
||||
color: Style.current.secondaryText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.horizontalCenter: activityCenterMessage ? undefined : parent.horizontalCenter
|
||||
anchors.horizontalCenter: isActivityCenterMessage ? undefined : parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: visible ? (activityCenterMessage ? Style.current.halfPadding : 20) : 0
|
||||
anchors.topMargin: visible ? (isActivityCenterMessage ? Style.current.halfPadding : 20) : 0
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: activityCenterMessage ? Style.current.padding : 0
|
||||
anchors.leftMargin: isActivityCenterMessage ? Style.current.padding : 0
|
||||
|
||||
text: {
|
||||
if (previousMessageIndex === -1) return ""; // identifier
|
||||
|
|
|
@ -21,6 +21,7 @@ Item {
|
|||
previousMessageIndex: prevMessageIndex
|
||||
previousMessageTimestamp: prevMsgTimestamp
|
||||
messageTimestamp: timestamp
|
||||
isActivityCenterMessage: activityCenterMessage
|
||||
}
|
||||
|
||||
UserImage {
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
import "../../../../shared/status"
|
||||
|
||||
Row {
|
||||
signal acceptClicked()
|
||||
signal declineClicked()
|
||||
signal blockClicked()
|
||||
signal profileClicked()
|
||||
|
||||
id: root
|
||||
height: acceptBtn.height
|
||||
spacing: Style.current.halfPadding
|
||||
|
||||
StatusIconButton {
|
||||
id: acceptBtn
|
||||
icon.name: "check-circle"
|
||||
onClicked: root.acceptClicked()
|
||||
width: 32
|
||||
height: 32
|
||||
padding: 6
|
||||
iconColor: Style.current.success
|
||||
hoveredIconColor: Style.current.success
|
||||
highlightedBackgroundColor: Utils.setColorAlpha(Style.current.success, 0.1)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StatusIconButton {
|
||||
id: declineBtn
|
||||
icon.name: "close"
|
||||
onClicked: root.declineClicked()
|
||||
width: 32
|
||||
height: 32
|
||||
padding: 6
|
||||
iconColor: Style.current.danger
|
||||
hoveredIconColor: Style.current.danger
|
||||
highlightedBackgroundColor: Utils.setColorAlpha(Style.current.danger, 0.1)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StatusContextMenuButton {
|
||||
property int iconSize: 14
|
||||
id: menuButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: {
|
||||
contactContextMenu.popup()
|
||||
}
|
||||
|
||||
PopupMenu {
|
||||
id: contactContextMenu
|
||||
hasArrow: false
|
||||
Action {
|
||||
icon.source: "../../../img/profileActive.svg"
|
||||
icon.width: menuButton.iconSize
|
||||
icon.height: menuButton.iconSize
|
||||
//% "View Profile"
|
||||
text: qsTrId("view-profile")
|
||||
onTriggered: root.profileClicked()
|
||||
enabled: true
|
||||
}
|
||||
Separator {}
|
||||
Action {
|
||||
icon.source: "../../../img/block-icon.svg"
|
||||
icon.width: menuButton.iconSize
|
||||
icon.height: menuButton.iconSize
|
||||
icon.color: Style.current.danger
|
||||
text: qsTr("Decline and block")
|
||||
onTriggered: root.blockClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -39,7 +39,7 @@ Rectangle {
|
|||
anchors.topMargin: Style.current.smallPadding
|
||||
anchors.left: accountImage.right
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.right: declineBtn.left
|
||||
anchors.right: buttons.left
|
||||
anchors.rightMargin: Style.current.padding
|
||||
}
|
||||
|
||||
|
@ -58,76 +58,17 @@ Rectangle {
|
|||
onHoveredChanged: container.isHovered = hovered
|
||||
}
|
||||
|
||||
StatusIconButton {
|
||||
id: declineBtn
|
||||
icon.name: "close"
|
||||
onClicked: profileModel.contacts.rejectContactRequest(container.address)
|
||||
width: 32
|
||||
height: 32
|
||||
padding: 6
|
||||
iconColor: Style.current.danger
|
||||
hoveredIconColor: Style.current.danger
|
||||
highlightedBackgroundColor: Utils.setColorAlpha(Style.current.danger, 0.1)
|
||||
anchors.right: acceptBtn.left
|
||||
anchors.rightMargin: Style.current.halfPadding
|
||||
AcceptRejectOptionsButtons {
|
||||
id: buttons
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StatusIconButton {
|
||||
id: acceptBtn
|
||||
icon.name: "check-circle"
|
||||
onClicked: {
|
||||
onAcceptClicked: {
|
||||
chatsModel.channelView.joinPrivateChat(container.address, "")
|
||||
profileModel.contacts.addContact(container.address)
|
||||
}
|
||||
width: 32
|
||||
height: 32
|
||||
padding: 6
|
||||
iconColor: Style.current.success
|
||||
hoveredIconColor: Style.current.success
|
||||
highlightedBackgroundColor: Utils.setColorAlpha(Style.current.success, 0.1)
|
||||
anchors.right: menuButton.left
|
||||
anchors.rightMargin: Style.current.halfPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StatusContextMenuButton {
|
||||
property int iconSize: 14
|
||||
id: menuButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: {
|
||||
contactContextMenu.popup()
|
||||
}
|
||||
|
||||
PopupMenu {
|
||||
id: contactContextMenu
|
||||
hasArrow: false
|
||||
Action {
|
||||
icon.source: "../../../img/profileActive.svg"
|
||||
icon.width: menuButton.iconSize
|
||||
icon.height: menuButton.iconSize
|
||||
//% "View Profile"
|
||||
text: qsTrId("view-profile")
|
||||
onTriggered: profileClick(true, name, address, identicon, "", localNickname)
|
||||
enabled: true
|
||||
}
|
||||
Separator {}
|
||||
Action {
|
||||
icon.source: "../../../img/block-icon.svg"
|
||||
icon.width: menuButton.iconSize
|
||||
icon.height: menuButton.iconSize
|
||||
icon.color: Style.current.danger
|
||||
text: qsTr("Decline and block")
|
||||
onTriggered: container.blockContactActionTriggered(name, address)
|
||||
}
|
||||
}
|
||||
}
|
||||
onDeclineClicked: profileModel.contacts.rejectContactRequest(container.address)
|
||||
onProfileClicked: profileClick(true, name, address, identicon, "", localNickname)
|
||||
onBlockClicked: container.blockContactActionTriggered(name, address)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ QtObject {
|
|||
readonly property int communityChatOnRequestAccess: 3
|
||||
|
||||
|
||||
readonly property int activityCenterNotificationTypeGroupRequest: 2
|
||||
readonly property int activityCenterNotificationTypeMention: 3
|
||||
readonly property int activityCenterNotificationTypeReply: 4
|
||||
|
||||
|
|
|
@ -97,6 +97,8 @@ DISTFILES += \
|
|||
app/AppLayouts/Browser/FavoritesList.qml \
|
||||
app/AppLayouts/Browser/components/BookmarkButton.qml \
|
||||
app/AppLayouts/Chat/ChatColumn/ActivityCenter.qml \
|
||||
app/AppLayouts/Chat/ChatColumn/ChatComponents/ActivityCenterGroupRequest.qml \
|
||||
app/AppLayouts/Chat/ChatColumn/ChatComponents/ActivityCenterMessageComponent.qml \
|
||||
app/AppLayouts/Chat/ChatColumn/ChatComponents/ActivityCenterTopBar.qml \
|
||||
app/AppLayouts/Chat/ChatColumn/ChatComponents/ActivityChannelBadge.qml \
|
||||
app/AppLayouts/Chat/ChatColumn/ChatComponents/AddToContactBanner.qml \
|
||||
|
@ -155,6 +157,7 @@ DISTFILES += \
|
|||
app/AppLayouts/Chat/CommunityComponents/TransferOwnershipPopup.qml \
|
||||
app/AppLayouts/Chat/ContactsColumn/AddChat.qml \
|
||||
app/AppLayouts/Chat/ContactsColumn/ClosedEmptyView.qml \
|
||||
app/AppLayouts/Chat/components/AcceptRejectOptionsButtons.qml \
|
||||
app/AppLayouts/Chat/components/ChooseBrowserPopup.qml \
|
||||
app/AppLayouts/Chat/ContactsColumn/CommunityButton.qml \
|
||||
app/AppLayouts/Chat/ContactsColumn/CommunityList.qml \
|
||||
|
|
Loading…
Reference in New Issue