2020-06-17 19:18:31 +00:00
|
|
|
import QtQuick 2.13
|
2021-08-06 15:31:42 +00:00
|
|
|
import Qt.labs.platform 1.1
|
2020-06-17 19:18:31 +00:00
|
|
|
import QtQuick.Controls 2.13
|
|
|
|
import QtQuick.Layouts 1.13
|
2021-04-28 15:30:41 +00:00
|
|
|
import QtGraphicalEffects 1.0
|
2021-07-06 09:42:51 +00:00
|
|
|
|
|
|
|
import StatusQ.Core.Theme 0.1
|
|
|
|
import StatusQ.Components 0.1
|
|
|
|
import StatusQ.Controls 0.1
|
|
|
|
|
2021-09-28 15:04:06 +00:00
|
|
|
import utils 1.0
|
2021-10-27 21:27:49 +00:00
|
|
|
import shared 1.0
|
|
|
|
import shared.popups 1.0
|
|
|
|
import shared.status 1.0
|
|
|
|
import shared.controls 1.0
|
2021-10-28 20:23:30 +00:00
|
|
|
import shared.views.chat 1.0
|
2020-05-25 20:34:26 +00:00
|
|
|
|
2021-10-01 15:58:36 +00:00
|
|
|
import "../helpers"
|
|
|
|
import "../controls"
|
|
|
|
import "../popups"
|
|
|
|
import "../panels"
|
|
|
|
import "../../Wallet"
|
2021-07-16 15:02:47 +00:00
|
|
|
|
|
|
|
Item {
|
2021-10-21 22:39:53 +00:00
|
|
|
id: root
|
2021-07-22 14:53:19 +00:00
|
|
|
anchors.fill: parent
|
2021-10-01 15:58:36 +00:00
|
|
|
property var rootStore
|
2021-05-25 19:38:18 +00:00
|
|
|
property alias pinnedMessagesPopupComponent: pinnedMessagesPopupComponent
|
2020-05-26 18:16:07 +00:00
|
|
|
property int chatGroupsListViewCount: 0
|
2020-07-09 17:47:36 +00:00
|
|
|
property bool isReply: false
|
2020-07-20 17:04:33 +00:00
|
|
|
property bool isImage: false
|
|
|
|
property bool isExtendedInput: isReply || isImage
|
2020-07-15 16:19:25 +00:00
|
|
|
property bool isConnected: false
|
2020-08-10 13:06:46 +00:00
|
|
|
property string contactToRemove: ""
|
2021-10-21 22:39:53 +00:00
|
|
|
property string activeChatId: root.rootStore.chatsModelInst.channelView.activeChannel.id
|
2021-11-15 15:15:21 +00:00
|
|
|
property bool isBlocked: root.rootStore.contactsModuleInst.model.isContactBlocked(activeChatId)
|
|
|
|
property bool isContact: root.rootStore.isContactAdded(activeChatId)
|
|
|
|
// property bool contactRequestReceived: root.rootStore.contactsModuleInst.model.contactRequestReceived(activeChatId)
|
2021-05-26 17:36:24 +00:00
|
|
|
property string currentNotificationChatId
|
|
|
|
property string currentNotificationCommunityId
|
2021-07-16 15:02:47 +00:00
|
|
|
property var currentTime: 0
|
|
|
|
property var idMap: ({})
|
|
|
|
property Timer timer: Timer { }
|
2021-07-22 14:53:19 +00:00
|
|
|
property var userList
|
2021-07-16 15:02:47 +00:00
|
|
|
property var onActivated: function () {
|
2021-09-21 12:26:32 +00:00
|
|
|
if(stackLayoutChatMessages.currentIndex >= 0 && stackLayoutChatMessages.currentIndex < stackLayoutChatMessages.children.length)
|
|
|
|
stackLayoutChatMessages.children[stackLayoutChatMessages.currentIndex].chatInput.textInput.forceActiveFocus(Qt.MouseFocusReason)
|
|
|
|
}
|
|
|
|
|
|
|
|
function hideChatInputExtendedArea () {
|
|
|
|
if(stackLayoutChatMessages.currentIndex >= 0 && stackLayoutChatMessages.currentIndex < stackLayoutChatMessages.children.length)
|
|
|
|
stackLayoutChatMessages.children[stackLayoutChatMessages.currentIndex].chatInput.hideExtendedArea()
|
2021-07-16 15:02:47 +00:00
|
|
|
}
|
2021-05-25 19:38:18 +00:00
|
|
|
|
2020-09-23 16:13:39 +00:00
|
|
|
function showReplyArea() {
|
2020-07-20 17:04:33 +00:00
|
|
|
isReply = true;
|
|
|
|
isImage = false;
|
2021-10-21 22:39:53 +00:00
|
|
|
let replyMessageIndex = root.rootStore.chatsModelInst.messageView.messageList.getMessageIndex(SelectedMessage.messageId);
|
2020-09-29 09:06:57 +00:00
|
|
|
if (replyMessageIndex === -1) return;
|
2021-10-21 22:39:53 +00:00
|
|
|
let userName = root.rootStore.chatsModelInst.messageView.messageList.getMessageData(replyMessageIndex, "userName")
|
|
|
|
let message = root.rootStore.chatsModelInst.messageView.messageList.getMessageData(replyMessageIndex, "message")
|
|
|
|
let identicon = root.rootStore.chatsModelInst.messageView.messageList.getMessageData(replyMessageIndex, "identicon")
|
|
|
|
let image = root.rootStore.chatsModelInst.messageView.messageList.getMessageData(replyMessageIndex, "image")
|
|
|
|
let sticker = root.rootStore.chatsModelInst.messageView.messageList.getMessageData(replyMessageIndex, "sticker")
|
|
|
|
let contentType = root.rootStore.chatsModelInst.messageView.messageList.getMessageData(replyMessageIndex, "contentType")
|
2020-09-29 09:06:57 +00:00
|
|
|
|
2021-09-21 12:26:32 +00:00
|
|
|
if(stackLayoutChatMessages.currentIndex >= 0 && stackLayoutChatMessages.currentIndex < stackLayoutChatMessages.children.length)
|
|
|
|
stackLayoutChatMessages.children[stackLayoutChatMessages.currentIndex].chatInput.showReplyArea(userName, message, identicon, contentType, image, sticker)
|
2020-07-20 17:04:33 +00:00
|
|
|
}
|
|
|
|
|
2020-09-29 09:06:57 +00:00
|
|
|
function requestAddressForTransaction(address, amount, tokenAddress, tokenDecimals = 18) {
|
2021-10-21 22:39:53 +00:00
|
|
|
amount = root.rootStore.utilsModelInst.eth2Wei(amount.toString(), tokenDecimals)
|
|
|
|
root.rootStore.chatsModelInst.transactions.requestAddress(activeChatId,
|
2020-12-06 15:24:28 +00:00
|
|
|
address,
|
|
|
|
amount,
|
|
|
|
tokenAddress)
|
2020-11-03 10:29:56 +00:00
|
|
|
txModalLoader.close()
|
2020-07-20 17:04:33 +00:00
|
|
|
}
|
2020-09-29 09:06:57 +00:00
|
|
|
function requestTransaction(address, amount, tokenAddress, tokenDecimals = 18) {
|
2021-10-21 22:39:53 +00:00
|
|
|
amount = root.rootStore.utilsModelInst.eth2Wei(amount.toString(), tokenDecimals)
|
|
|
|
root.rootStore.chatsModelInst.transactions.request(activeChatId,
|
2020-09-29 09:06:57 +00:00
|
|
|
address,
|
|
|
|
amount,
|
|
|
|
tokenAddress)
|
2020-11-03 10:29:56 +00:00
|
|
|
txModalLoader.close()
|
2020-07-20 17:04:33 +00:00
|
|
|
}
|
2020-09-29 09:06:57 +00:00
|
|
|
|
2021-05-26 17:36:24 +00:00
|
|
|
function clickOnNotification() {
|
2021-07-09 07:49:08 +00:00
|
|
|
// So far we're just showing this app as the top most window. Once we decide about the way
|
|
|
|
// how to notify the app what channle should be displayed within the app when user clicks
|
|
|
|
// notificaiton bubble this part should be updated accordingly.
|
|
|
|
//
|
|
|
|
// I removed part of this function which caused app crash.
|
2021-05-26 17:36:24 +00:00
|
|
|
applicationWindow.show()
|
|
|
|
applicationWindow.raise()
|
|
|
|
applicationWindow.requestActivate()
|
|
|
|
}
|
|
|
|
|
2021-09-24 14:04:49 +00:00
|
|
|
function positionAtMessage(messageId, isSearch = false) {
|
|
|
|
stackLayoutChatMessages.children[stackLayoutChatMessages.currentIndex].message.scrollToMessage(messageId, isSearch);
|
2021-05-26 17:36:24 +00:00
|
|
|
}
|
|
|
|
|
2021-04-07 18:26:03 +00:00
|
|
|
Timer {
|
2021-07-16 15:02:47 +00:00
|
|
|
interval: 60000; // 1 min
|
|
|
|
running: true
|
|
|
|
repeat: true
|
|
|
|
triggeredOnStart: true
|
|
|
|
onTriggered: {
|
2021-10-21 22:39:53 +00:00
|
|
|
root.currentTime = Date.now()
|
2021-07-16 15:02:47 +00:00
|
|
|
}
|
2021-04-07 18:26:03 +00:00
|
|
|
}
|
2021-06-10 19:20:43 +00:00
|
|
|
|
2021-10-21 00:41:54 +00:00
|
|
|
MessageContextMenuView {
|
2021-08-25 20:31:00 +00:00
|
|
|
id: contextmenu
|
2021-10-21 22:39:53 +00:00
|
|
|
store: root.rootStore
|
|
|
|
reactionModel: root.rootStore.emojiReactionsModel
|
2021-08-25 20:31:00 +00:00
|
|
|
}
|
|
|
|
|
2021-07-16 15:02:47 +00:00
|
|
|
StackLayout {
|
|
|
|
anchors.fill: parent
|
2021-10-21 22:39:53 +00:00
|
|
|
currentIndex: root.rootStore.chatsModelInst.channelView.activeChannelIndex > -1
|
|
|
|
&& chatGroupsListViewCount > 0 ? 0 : 1
|
2020-05-26 15:55:32 +00:00
|
|
|
|
2021-07-16 15:02:47 +00:00
|
|
|
StatusImageModal {
|
|
|
|
id: imagePopup
|
2021-08-16 09:11:43 +00:00
|
|
|
onClicked: {
|
|
|
|
if (button === Qt.LeftButton) {
|
|
|
|
imagePopup.close()
|
|
|
|
}
|
|
|
|
else if(button === Qt.RightButton) {
|
2021-08-25 20:31:00 +00:00
|
|
|
contextmenu.imageSource = imagePopup.imageSource
|
|
|
|
contextmenu.hideEmojiPicker = true
|
|
|
|
contextmenu.isRightClickOnImage = true;
|
|
|
|
contextmenu.show()
|
2021-08-16 09:11:43 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-16 15:02:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ColumnLayout {
|
|
|
|
spacing: 0
|
|
|
|
|
|
|
|
StatusChatToolBar {
|
|
|
|
id: topBar
|
|
|
|
Layout.fillWidth: true
|
2021-07-06 09:42:51 +00:00
|
|
|
|
2021-10-21 22:39:53 +00:00
|
|
|
property string chatId: root.rootStore.chatsModelInst.channelView.activeChannel.id
|
2021-07-16 15:02:47 +00:00
|
|
|
property string profileImage: appMain.getProfileImage(chatId) || ""
|
2021-07-06 09:42:51 +00:00
|
|
|
|
2021-10-21 22:39:53 +00:00
|
|
|
chatInfoButton.title: Utils.removeStatusEns(root.rootStore.chatsModelInst.channelView.activeChannel.name)
|
2021-07-16 15:02:47 +00:00
|
|
|
chatInfoButton.subTitle: {
|
2021-10-21 22:39:53 +00:00
|
|
|
switch (root.rootStore.chatsModelInst.channelView.activeChannel.chatType) {
|
2021-07-06 09:42:51 +00:00
|
|
|
case Constants.chatTypeOneToOne:
|
2021-11-15 15:15:21 +00:00
|
|
|
return (root.isContact ?
|
2021-07-20 14:22:32 +00:00
|
|
|
//% "Contact"
|
|
|
|
qsTrId("chat-is-a-contact") :
|
|
|
|
//% "Not a contact"
|
|
|
|
qsTrId("chat-is-not-a-contact"))
|
2021-07-06 09:42:51 +00:00
|
|
|
case Constants.chatTypePublic:
|
2021-07-16 20:22:50 +00:00
|
|
|
//% "Public chat"
|
|
|
|
return qsTrId("public-chat")
|
2021-07-06 09:42:51 +00:00
|
|
|
case Constants.chatTypePrivateGroupChat:
|
2021-10-21 22:39:53 +00:00
|
|
|
let cnt = root.rootStore.chatsModelInst.channelView.activeChannel.members.rowCount();
|
2021-07-16 20:22:50 +00:00
|
|
|
//% "%1 members"
|
|
|
|
if(cnt > 1) return qsTrId("-1-members").arg(cnt);
|
|
|
|
//% "1 member"
|
|
|
|
return qsTrId("1-member");
|
2021-07-06 09:42:51 +00:00
|
|
|
case Constants.chatTypeCommunity:
|
2021-10-21 22:39:53 +00:00
|
|
|
return Utils.linkifyAndXSS(root.rootStore.chatsModelInst.channelView.activeChannel.description).trim()
|
2021-07-06 09:42:51 +00:00
|
|
|
default:
|
|
|
|
return ""
|
2021-07-16 15:02:47 +00:00
|
|
|
}
|
2021-07-06 09:42:51 +00:00
|
|
|
}
|
2021-10-21 22:39:53 +00:00
|
|
|
chatInfoButton.image.source: profileImage || root.rootStore.chatsModelInst.channelView.activeChannel.identicon
|
|
|
|
chatInfoButton.image.isIdenticon: !!!profileImage && root.rootStore.chatsModelInst.channelView.activeChannel.identicon
|
|
|
|
chatInfoButton.icon.color: root.rootStore.chatsModelInst.channelView.activeChannel.color
|
|
|
|
chatInfoButton.type: root.rootStore.chatsModelInst.channelView.activeChannel.chatType
|
|
|
|
chatInfoButton.pinnedMessagesCount: root.rootStore.chatsModelInst.messageView.pinnedMessagesList.count
|
|
|
|
chatInfoButton.muted: root.rootStore.chatsModelInst.channelView.activeChannel.muted
|
2021-07-16 15:02:47 +00:00
|
|
|
|
|
|
|
chatInfoButton.onPinnedMessagesCountClicked: openPopup(pinnedMessagesPopupComponent)
|
2021-10-21 22:39:53 +00:00
|
|
|
chatInfoButton.onUnmute: root.rootStore.chatsModelInst.channelView.unmuteChatItem(chatsModel.channelView.activeChannel.id)
|
2021-07-16 15:02:47 +00:00
|
|
|
|
2021-10-21 22:39:53 +00:00
|
|
|
chatInfoButton.sensor.enabled: root.rootStore.chatsModelInst.channelView.activeChannel.chatType !== Constants.chatTypePublic &&
|
|
|
|
root.rootStore.chatsModelInst.channelView.activeChannel.chatType !== Constants.chatTypeCommunity
|
2021-07-16 15:02:47 +00:00
|
|
|
chatInfoButton.onClicked: {
|
2021-10-21 22:39:53 +00:00
|
|
|
switch (root.rootStore.chatsModelInst.channelView.activeChannel.chatType) {
|
2021-07-06 09:42:51 +00:00
|
|
|
case Constants.chatTypePrivateGroupChat:
|
2021-07-28 10:41:37 +00:00
|
|
|
openPopup(groupInfoPopupComponent, {
|
|
|
|
channelType: GroupInfoPopup.ChannelType.ActiveChannel,
|
2021-10-21 22:39:53 +00:00
|
|
|
channel: root.rootStore.chatsModelInst.channelView.activeChannel
|
2021-07-28 10:41:37 +00:00
|
|
|
})
|
2021-07-06 09:42:51 +00:00
|
|
|
break;
|
|
|
|
case Constants.chatTypeOneToOne:
|
2021-10-21 22:39:53 +00:00
|
|
|
openProfilePopup(root.rootStore.chatsModelInst.userNameOrAlias(chatsModel.channelView.activeChannel.id),
|
|
|
|
root.rootStore.chatsModelInst.channelView.activeChannel.id, profileImage
|
|
|
|
|| root.rootStore.chatsModelInst.channelView.activeChannel.identicon,
|
|
|
|
"", root.rootStore.chatsModelInst.channelView.activeChannel.nickname)
|
2021-07-06 09:42:51 +00:00
|
|
|
break;
|
2021-07-16 15:02:47 +00:00
|
|
|
}
|
2021-07-06 09:42:51 +00:00
|
|
|
}
|
|
|
|
|
2021-10-20 09:50:50 +00:00
|
|
|
membersButton.visible: (localAccountSensitiveSettings.showOnlineUsers || root.rootStore.chatsModelInst.communities.activeCommunity.active)
|
2021-10-21 22:39:53 +00:00
|
|
|
&& root.rootStore.chatsModelInst.channelView.activeChannel.chatType !== Constants.chatTypeOneToOne
|
2021-10-20 09:50:50 +00:00
|
|
|
membersButton.highlighted: localAccountSensitiveSettings.expandUsersList
|
|
|
|
notificationButton.visible: localAccountSensitiveSettings.isActivityCenterEnabled
|
|
|
|
notificationButton.tooltip.offset: localAccountSensitiveSettings.expandUsersList ? 0 : 14
|
2021-10-21 22:39:53 +00:00
|
|
|
notificationCount: root.rootStore.chatsModelInst.activityNotificationList.unreadCount
|
2021-07-06 09:42:51 +00:00
|
|
|
|
2021-07-16 15:02:47 +00:00
|
|
|
onSearchButtonClicked: searchPopup.open()
|
2021-07-12 17:42:40 +00:00
|
|
|
|
2021-10-20 09:50:50 +00:00
|
|
|
onMembersButtonClicked: localAccountSensitiveSettings.expandUsersList = !localAccountSensitiveSettings.expandUsersList
|
2021-07-16 15:02:47 +00:00
|
|
|
onNotificationButtonClicked: activityCenter.open()
|
2021-07-06 09:42:51 +00:00
|
|
|
|
2021-10-01 15:58:36 +00:00
|
|
|
popupMenu: ChatContextMenuView {
|
2021-10-21 22:39:53 +00:00
|
|
|
store: root.rootStore
|
2021-09-14 09:58:20 +00:00
|
|
|
onOpened: {
|
2021-10-21 22:39:53 +00:00
|
|
|
chatItem = root.rootStore.chatsModelInst.channelView.activeChannel
|
2021-07-16 15:02:47 +00:00
|
|
|
}
|
2021-07-06 09:42:51 +00:00
|
|
|
}
|
|
|
|
}
|
2020-07-15 16:19:25 +00:00
|
|
|
|
2021-07-16 15:02:47 +00:00
|
|
|
Rectangle {
|
|
|
|
id: connectedStatusRect
|
|
|
|
Layout.fillWidth: true
|
|
|
|
height: 40
|
|
|
|
Layout.alignment: Qt.AlignHCenter
|
|
|
|
z: 60
|
|
|
|
visible: false
|
|
|
|
color: isConnected ? Style.current.green : Style.current.darkGrey
|
|
|
|
Text {
|
|
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
color: Style.current.white
|
|
|
|
id: connectedStatusLbl
|
|
|
|
text: isConnected ?
|
|
|
|
//% "Connected"
|
|
|
|
qsTrId("connected") :
|
|
|
|
//% "Disconnected"
|
|
|
|
qsTrId("disconnected")
|
|
|
|
}
|
|
|
|
|
|
|
|
Connections {
|
2021-10-21 22:39:53 +00:00
|
|
|
target: root.rootStore.chatsModelInst
|
2021-07-16 15:02:47 +00:00
|
|
|
onOnlineStatusChanged: {
|
|
|
|
if (connected == isConnected) return;
|
|
|
|
isConnected = connected;
|
|
|
|
if(isConnected){
|
|
|
|
timer.setTimeout(function(){
|
|
|
|
connectedStatusRect.visible = false;
|
|
|
|
}, 5000);
|
|
|
|
} else {
|
|
|
|
connectedStatusRect.visible = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Component.onCompleted: {
|
2021-10-21 22:39:53 +00:00
|
|
|
isConnected = root.rootStore.chatsModelInst.isOnline
|
2021-07-16 15:02:47 +00:00
|
|
|
if(!isConnected){
|
|
|
|
connectedStatusRect.visible = true
|
|
|
|
}
|
2020-07-15 16:19:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-07 15:31:58 +00:00
|
|
|
// Blocked User Status Bar
|
|
|
|
StatusBanner {
|
|
|
|
id: blockedUsrBar
|
|
|
|
width: parent.width
|
2021-08-09 11:11:04 +00:00
|
|
|
visible: isBlocked
|
2022-01-07 15:31:58 +00:00
|
|
|
type: StatusBanner.Type.Danger
|
|
|
|
statusText: qsTr("Blocked")
|
2021-08-09 11:11:04 +00:00
|
|
|
}
|
|
|
|
|
2021-07-16 15:02:47 +00:00
|
|
|
StackLayout {
|
|
|
|
id: stackLayoutChatMessages
|
|
|
|
Layout.fillWidth: true
|
|
|
|
Layout.fillHeight: true
|
|
|
|
clip: true
|
2021-10-21 22:39:53 +00:00
|
|
|
currentIndex: root.rootStore.chatsModelInst.messageView.getMessageListIndex(root.rootStore.chatsModelInst.channelView.activeChannelIndex)
|
2021-07-16 15:02:47 +00:00
|
|
|
Repeater {
|
2021-10-21 22:39:53 +00:00
|
|
|
model: root.rootStore.chatsModelInst.messageView
|
2021-09-21 12:26:32 +00:00
|
|
|
ColumnLayout {
|
|
|
|
property alias chatInput: chatInput
|
2021-09-22 08:25:55 +00:00
|
|
|
property alias message: messageLoader.item
|
2021-09-21 12:26:32 +00:00
|
|
|
Loader {
|
2021-09-22 08:25:55 +00:00
|
|
|
id: messageLoader
|
2021-09-21 12:26:32 +00:00
|
|
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
|
|
|
Layout.fillWidth: true
|
|
|
|
Layout.fillHeight: true
|
|
|
|
Layout.preferredWidth: parent.width
|
|
|
|
|
|
|
|
active: stackLayoutChatMessages.currentIndex === index
|
2021-10-01 15:58:36 +00:00
|
|
|
sourceComponent: ChatMessagesView {
|
2021-09-21 12:26:32 +00:00
|
|
|
id: chatMessages
|
2021-10-21 22:39:53 +00:00
|
|
|
store: root.rootStore
|
2021-09-21 12:26:32 +00:00
|
|
|
messageList: messages
|
|
|
|
messageContextMenuInst: contextmenu
|
|
|
|
Component.onCompleted: {
|
2021-10-21 22:39:53 +00:00
|
|
|
root.userList = chatMessages.messageList.userList;
|
2021-09-21 12:26:32 +00:00
|
|
|
}
|
2021-07-22 14:53:19 +00:00
|
|
|
}
|
2021-07-16 15:02:47 +00:00
|
|
|
}
|
2021-09-21 12:26:32 +00:00
|
|
|
Item {
|
|
|
|
id: inputArea
|
|
|
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
|
|
|
Layout.fillWidth: true
|
|
|
|
Layout.preferredWidth: parent.width
|
|
|
|
height: chatInput.height
|
|
|
|
Layout.preferredHeight: height
|
|
|
|
|
|
|
|
Connections {
|
2021-10-21 22:39:53 +00:00
|
|
|
target: root.rootStore.chatsModelInst.messageView
|
2021-09-21 12:26:32 +00:00
|
|
|
onLoadingMessagesChanged:
|
|
|
|
if(value){
|
|
|
|
loadingMessagesIndicator.active = true
|
|
|
|
} else {
|
|
|
|
timer.setTimeout(function(){
|
|
|
|
loadingMessagesIndicator.active = false;
|
|
|
|
}, 5000);
|
|
|
|
}
|
|
|
|
}
|
2021-05-26 17:36:24 +00:00
|
|
|
|
2021-09-21 12:26:32 +00:00
|
|
|
Loader {
|
|
|
|
id: loadingMessagesIndicator
|
2021-10-21 22:39:53 +00:00
|
|
|
active: root.rootStore.chatsModelInst.messageView.loadingMessages
|
2021-09-21 12:26:32 +00:00
|
|
|
sourceComponent: loadingIndicator
|
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.bottom: chatInput.top
|
|
|
|
anchors.rightMargin: Style.current.padding
|
|
|
|
anchors.bottomMargin: Style.current.padding
|
|
|
|
}
|
2021-06-21 19:35:32 +00:00
|
|
|
|
2021-09-21 12:26:32 +00:00
|
|
|
Component {
|
|
|
|
id: loadingIndicator
|
|
|
|
LoadingAnimation { }
|
|
|
|
}
|
2021-07-16 15:02:47 +00:00
|
|
|
|
2021-09-21 12:26:32 +00:00
|
|
|
StatusChatInput {
|
|
|
|
id: chatInput
|
|
|
|
visible: {
|
2021-10-21 22:39:53 +00:00
|
|
|
if (root.rootStore.chatsModelInst.channelView.activeChannel.chatType === Constants.chatTypePrivateGroupChat) {
|
|
|
|
return root.rootStore.chatsModelInst.channelView.activeChannel.isMember
|
2021-09-21 12:26:32 +00:00
|
|
|
}
|
2021-10-21 22:39:53 +00:00
|
|
|
if (root.rootStore.chatsModelInst.channelView.activeChannel.chatType === Constants.chatTypeOneToOne) {
|
2021-09-21 12:26:32 +00:00
|
|
|
return isContact
|
|
|
|
}
|
2021-10-21 22:39:53 +00:00
|
|
|
const community = root.rootStore.chatsModelInst.communities.activeCommunity
|
2021-09-21 12:26:32 +00:00
|
|
|
return !community.active ||
|
|
|
|
community.access === Constants.communityChatPublicAccess ||
|
|
|
|
community.admin ||
|
2021-10-21 22:39:53 +00:00
|
|
|
root.rootStore.chatsModelInst.channelView.activeChannel.canPost
|
2021-09-21 12:26:32 +00:00
|
|
|
}
|
|
|
|
isContactBlocked: isBlocked
|
|
|
|
chatInputPlaceholder: isBlocked ?
|
|
|
|
//% "This user has been blocked."
|
|
|
|
qsTrId("this-user-has-been-blocked-") :
|
|
|
|
//% "Type a message."
|
|
|
|
qsTrId("type-a-message-")
|
|
|
|
anchors.bottom: parent.bottom
|
2021-10-21 22:39:53 +00:00
|
|
|
recentStickers: root.rootStore.chatsModelInst.stickers.recent
|
|
|
|
stickerPackList: root.rootStore.chatsModelInst.stickers.stickerPacks
|
|
|
|
chatType: root.rootStore.chatsModelInst.channelView.activeChannel.chatType
|
2021-09-21 12:26:32 +00:00
|
|
|
onSendTransactionCommandButtonClicked: {
|
2021-10-21 22:39:53 +00:00
|
|
|
if (root.rootStore.chatsModelInst.channelView.activeChannel.ensVerified) {
|
2021-09-21 12:26:32 +00:00
|
|
|
txModalLoader.sourceComponent = cmpSendTransactionWithEns
|
|
|
|
} else {
|
|
|
|
txModalLoader.sourceComponent = cmpSendTransactionNoEns
|
|
|
|
}
|
|
|
|
txModalLoader.item.open()
|
|
|
|
}
|
|
|
|
onReceiveTransactionCommandButtonClicked: {
|
|
|
|
txModalLoader.sourceComponent = cmpReceiveTransaction
|
|
|
|
txModalLoader.item.open()
|
|
|
|
}
|
|
|
|
onStickerSelected: {
|
2021-10-21 22:39:53 +00:00
|
|
|
root.rootStore.chatsModelInst.stickers.send(hashId, chatInput.isReply ? SelectedMessage.messageId : "", packId)
|
2021-09-21 12:26:32 +00:00
|
|
|
}
|
|
|
|
onSendMessage: {
|
|
|
|
if (chatInput.fileUrls.length > 0){
|
2021-10-21 22:39:53 +00:00
|
|
|
root.rootStore.chatsModelInst.sendImages(JSON.stringify(fileUrls));
|
2021-09-21 12:26:32 +00:00
|
|
|
}
|
2021-10-21 22:39:53 +00:00
|
|
|
let msg = root.rootStore.chatsModelInst.plainText(Emoji.deparse(chatInput.textInput.text))
|
2021-09-21 12:26:32 +00:00
|
|
|
if (msg.length > 0){
|
|
|
|
msg = chatInput.interpretMessage(msg)
|
2021-10-21 22:39:53 +00:00
|
|
|
root.rootStore.chatsModelInst.messageView.sendMessage(msg, chatInput.isReply ? SelectedMessage.messageId : "", Utils.isOnlyEmoji(msg) ? Constants.emojiType : Constants.messageType, false);
|
2021-09-21 12:26:32 +00:00
|
|
|
if(event) event.accepted = true
|
|
|
|
sendMessageSound.stop();
|
|
|
|
Qt.callLater(sendMessageSound.play);
|
|
|
|
|
|
|
|
chatInput.textInput.clear();
|
|
|
|
chatInput.textInput.textFormat = TextEdit.PlainText;
|
|
|
|
chatInput.textInput.textFormat = TextEdit.RichText;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-07-16 15:02:47 +00:00
|
|
|
}
|
2021-09-21 12:26:32 +00:00
|
|
|
Connections {
|
2021-10-21 22:39:53 +00:00
|
|
|
target: root.rootStore.chatsModelInst.channelView
|
2021-09-21 12:26:32 +00:00
|
|
|
onActiveChannelChanged: {
|
2021-11-15 15:15:21 +00:00
|
|
|
isBlocked = root.rootStore.contactsModuleInst.model.isContactBlocked(activeChatId);
|
2021-09-21 12:26:32 +00:00
|
|
|
chatInput.suggestions.hide();
|
|
|
|
if(stackLayoutChatMessages.currentIndex >= 0 && stackLayoutChatMessages.currentIndex < stackLayoutChatMessages.children.length)
|
|
|
|
stackLayoutChatMessages.children[stackLayoutChatMessages.currentIndex].chatInput.textInput.forceActiveFocus(Qt.MouseFocusReason)
|
|
|
|
}
|
2021-07-16 15:02:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-09-21 12:26:32 +00:00
|
|
|
}
|
2021-08-09 11:11:04 +00:00
|
|
|
|
2021-10-01 15:58:36 +00:00
|
|
|
ChatRequestMessagePanel {
|
2021-09-21 12:26:32 +00:00
|
|
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
|
|
|
Layout.fillWidth: true
|
|
|
|
Layout.bottomMargin: Style.current.bigPadding
|
2021-11-15 15:15:21 +00:00
|
|
|
isContact: root.isContact
|
|
|
|
visible: root.rootStore.chatsModelInst.channelView.activeChannel.chatType === Constants.chatTypeOneToOne
|
|
|
|
&& (!root.isContact /*|| !contactRequestReceived*/)
|
|
|
|
onAddContactClicked: {
|
|
|
|
root.rootStore.addContact(activeChatId);
|
|
|
|
}
|
2021-05-18 17:17:04 +00:00
|
|
|
}
|
2021-07-16 15:02:47 +00:00
|
|
|
}
|
2021-05-18 17:17:04 +00:00
|
|
|
|
2021-10-01 15:58:36 +00:00
|
|
|
EmptyChatPanel {
|
2021-10-29 18:06:18 +00:00
|
|
|
onShareChatKeyClicked: openProfilePopup(userProfile.username, userProfile.pubKey, userProfile.thumbnailImage);
|
2021-10-01 15:58:36 +00:00
|
|
|
}
|
2021-05-18 17:17:04 +00:00
|
|
|
|
2021-07-16 15:02:47 +00:00
|
|
|
Loader {
|
|
|
|
id: txModalLoader
|
|
|
|
function close() {
|
|
|
|
if (!this.item) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
this.item.close()
|
|
|
|
this.closed()
|
2021-05-18 17:17:04 +00:00
|
|
|
}
|
2021-07-16 15:02:47 +00:00
|
|
|
function closed() {
|
|
|
|
this.sourceComponent = undefined
|
|
|
|
}
|
|
|
|
}
|
2021-05-18 17:17:04 +00:00
|
|
|
|
2021-07-16 15:02:47 +00:00
|
|
|
Component {
|
|
|
|
id: cmpSendTransactionNoEns
|
|
|
|
ChatCommandModal {
|
|
|
|
id: sendTransactionNoEns
|
2021-10-22 20:49:47 +00:00
|
|
|
store: root.rootStore
|
2021-07-16 15:02:47 +00:00
|
|
|
onClosed: {
|
|
|
|
txModalLoader.closed()
|
2021-05-18 17:17:04 +00:00
|
|
|
}
|
2021-10-21 22:39:53 +00:00
|
|
|
sendChatCommand: root.requestAddressForTransaction
|
2021-07-16 15:02:47 +00:00
|
|
|
isRequested: false
|
|
|
|
//% "Send"
|
|
|
|
commandTitle: qsTrId("command-button-send")
|
2021-10-18 10:55:07 +00:00
|
|
|
header.title: commandTitle
|
2021-07-16 15:02:47 +00:00
|
|
|
//% "Request Address"
|
|
|
|
finalButtonLabel: qsTrId("request-address")
|
|
|
|
selectRecipient.selectedRecipient: {
|
|
|
|
return {
|
|
|
|
address: Constants.zeroAddress, // Setting as zero address since we don't have the address yet
|
2021-10-21 22:39:53 +00:00
|
|
|
alias: root.rootStore.chatsModelInst.channelView.activeChannel.alias,
|
|
|
|
identicon: root.rootStore.chatsModelInst.channelView.activeChannel.identicon,
|
|
|
|
name: root.rootStore.chatsModelInst.channelView.activeChannel.name,
|
2021-07-16 15:02:47 +00:00
|
|
|
type: RecipientSelector.Type.Contact
|
2021-05-18 17:17:04 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-16 15:02:47 +00:00
|
|
|
selectRecipient.selectedType: RecipientSelector.Type.Contact
|
|
|
|
selectRecipient.readOnly: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Component {
|
|
|
|
id: cmpReceiveTransaction
|
|
|
|
ChatCommandModal {
|
|
|
|
id: receiveTransaction
|
2021-10-22 20:49:47 +00:00
|
|
|
store: root.rootStore
|
2021-07-16 15:02:47 +00:00
|
|
|
onClosed: {
|
|
|
|
txModalLoader.closed()
|
2021-05-18 17:17:04 +00:00
|
|
|
}
|
2021-10-21 22:39:53 +00:00
|
|
|
sendChatCommand: root.requestTransaction
|
2021-07-16 15:02:47 +00:00
|
|
|
isRequested: true
|
|
|
|
//% "Request"
|
|
|
|
commandTitle: qsTrId("wallet-request")
|
2021-10-18 10:55:07 +00:00
|
|
|
header.title: commandTitle
|
2021-07-16 15:02:47 +00:00
|
|
|
//% "Request"
|
|
|
|
finalButtonLabel: qsTrId("wallet-request")
|
|
|
|
selectRecipient.selectedRecipient: {
|
|
|
|
return {
|
|
|
|
address: Constants.zeroAddress, // Setting as zero address since we don't have the address yet
|
2021-10-21 22:39:53 +00:00
|
|
|
alias: root.rootStore.chatsModelInst.channelView.activeChannel.alias,
|
|
|
|
identicon: root.rootStore.chatsModelInst.channelView.activeChannel.identicon,
|
|
|
|
name: root.rootStore.chatsModelInst.channelView.activeChannel.name,
|
2021-07-16 15:02:47 +00:00
|
|
|
type: RecipientSelector.Type.Contact
|
2021-05-18 17:17:04 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-16 15:02:47 +00:00
|
|
|
selectRecipient.selectedType: RecipientSelector.Type.Contact
|
|
|
|
selectRecipient.readOnly: true
|
2021-05-18 17:17:04 +00:00
|
|
|
}
|
2020-10-28 07:44:09 +00:00
|
|
|
}
|
|
|
|
|
2021-07-16 15:02:47 +00:00
|
|
|
Component {
|
|
|
|
id: cmpSendTransactionWithEns
|
|
|
|
SendModal {
|
|
|
|
id: sendTransactionWithEns
|
|
|
|
onOpened: {
|
2021-10-21 22:39:53 +00:00
|
|
|
root.rootStore.walletModelInst.gasView.getGasPrice()
|
2021-07-16 15:02:47 +00:00
|
|
|
}
|
|
|
|
onClosed: {
|
|
|
|
txModalLoader.closed()
|
|
|
|
}
|
|
|
|
selectRecipient.readOnly: true
|
|
|
|
selectRecipient.selectedRecipient: {
|
|
|
|
return {
|
|
|
|
address: "",
|
2021-10-21 22:39:53 +00:00
|
|
|
alias: root.rootStore.chatsModelInst.channelView.activeChannel.alias,
|
|
|
|
identicon: root.rootStore.chatsModelInst.channelView.activeChannel.identicon,
|
|
|
|
name: root.rootStore.chatsModelInst.channelView.activeChannel.name,
|
2021-07-16 15:02:47 +00:00
|
|
|
type: RecipientSelector.Type.Contact,
|
|
|
|
ensVerified: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
selectRecipient.selectedType: RecipientSelector.Type.Contact
|
2020-11-03 10:29:56 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-16 15:02:47 +00:00
|
|
|
|
2021-10-01 15:58:36 +00:00
|
|
|
ActivityCenterPopup {
|
2021-07-16 15:02:47 +00:00
|
|
|
id: activityCenter
|
2021-10-21 22:39:53 +00:00
|
|
|
height: root.height - (topBar.height * 2) // TODO get screen size
|
2021-07-16 15:02:47 +00:00
|
|
|
y: topBar.height
|
2021-10-21 22:39:53 +00:00
|
|
|
store: root.rootStore
|
2020-11-03 10:29:56 +00:00
|
|
|
}
|
2021-07-16 15:02:47 +00:00
|
|
|
|
|
|
|
Connections {
|
2021-11-15 15:15:21 +00:00
|
|
|
target: root.rootStore.contactsModuleInst.model
|
2021-07-16 15:02:47 +00:00
|
|
|
onContactListChanged: {
|
2021-11-15 15:15:21 +00:00
|
|
|
root.isBlocked = root.rootStore.contactsModuleInst.model.isContactBlocked(activeChatId);
|
|
|
|
root.isContact = root.rootStore.contactsModuleInst.model.isAdded(activeChatId);
|
2020-11-03 10:29:56 +00:00
|
|
|
}
|
2020-10-28 07:44:09 +00:00
|
|
|
}
|
2021-07-16 15:02:47 +00:00
|
|
|
|
|
|
|
Connections {
|
2021-10-21 22:39:53 +00:00
|
|
|
target: root.rootStore.chatsModelInst.channelView
|
2021-07-16 15:02:47 +00:00
|
|
|
onActiveChannelChanged: {
|
2021-10-21 22:39:53 +00:00
|
|
|
root.rootStore.chatsModelInst.messageView.hideLoadingIndicator()
|
2021-07-16 15:02:47 +00:00
|
|
|
SelectedMessage.reset();
|
|
|
|
chatColumn.isReply = false;
|
2020-10-28 07:44:09 +00:00
|
|
|
}
|
2020-05-25 20:34:26 +00:00
|
|
|
}
|
2021-07-16 15:02:47 +00:00
|
|
|
|
|
|
|
Connections {
|
|
|
|
target: systemTray
|
|
|
|
onMessageClicked: function () {
|
|
|
|
clickOnNotification()
|
2020-10-28 07:44:09 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-06 09:42:51 +00:00
|
|
|
|
2021-07-16 15:02:47 +00:00
|
|
|
Component {
|
|
|
|
id: pinnedMessagesPopupComponent
|
|
|
|
PinnedMessagesPopup {
|
|
|
|
id: pinnedMessagesPopup
|
2021-10-21 22:39:53 +00:00
|
|
|
rootStore: root.rootStore
|
|
|
|
messageStore: root.rootStore.messageStore
|
2021-07-16 15:02:47 +00:00
|
|
|
onClosed: destroy()
|
|
|
|
}
|
|
|
|
}
|
2021-07-14 12:35:37 +00:00
|
|
|
|
2021-07-16 15:02:47 +00:00
|
|
|
Connections {
|
2021-10-21 22:39:53 +00:00
|
|
|
target: root.rootStore.chatsModelInst.messageView
|
2021-07-14 12:35:37 +00:00
|
|
|
|
2021-07-26 08:08:03 +00:00
|
|
|
onSearchedMessageLoaded: {
|
2021-09-24 14:04:49 +00:00
|
|
|
positionAtMessage(messageId, true);
|
2021-07-26 08:08:03 +00:00
|
|
|
}
|
|
|
|
|
2021-08-18 14:43:59 +00:00
|
|
|
onMessageNotificationPushed: function(messageId, communityId, chatId, msg, contentType, chatType, timestamp, identicon, username, hasMention, isAddedContact, channelName) {
|
2021-10-20 09:50:50 +00:00
|
|
|
if (localAccountSensitiveSettings.notificationSetting == Constants.notifyAllMessages ||
|
|
|
|
(localAccountSensitiveSettings.notificationSetting == Constants.notifyJustMentions && hasMention)) {
|
2021-10-21 22:39:53 +00:00
|
|
|
if (chatId === root.rootStore.chatsModelInst.channelView.activeChannel.id && applicationWindow.active === true) {
|
2021-07-16 15:02:47 +00:00
|
|
|
// Do not show the notif if we are in the channel already and the window is active and focused
|
|
|
|
return
|
|
|
|
}
|
2021-07-14 12:35:37 +00:00
|
|
|
|
2021-10-21 22:39:53 +00:00
|
|
|
root.currentNotificationChatId = chatId
|
|
|
|
root.currentNotificationCommunityId = null
|
2021-07-14 12:35:37 +00:00
|
|
|
|
2021-07-16 15:02:47 +00:00
|
|
|
let name;
|
2021-10-20 09:50:50 +00:00
|
|
|
if (localAccountSensitiveSettings.notificationMessagePreviewSetting === Constants.notificationPreviewAnonymous) {
|
2021-07-16 15:02:47 +00:00
|
|
|
name = "Status"
|
|
|
|
} else if (chatType === Constants.chatTypePublic) {
|
|
|
|
name = chatId
|
|
|
|
} else {
|
|
|
|
name = chatType === Constants.chatTypePrivateGroupChat ? Utils.filterXSS(channelName) : Utils.removeStatusEns(username)
|
|
|
|
}
|
2021-07-14 12:35:37 +00:00
|
|
|
|
2021-07-16 15:02:47 +00:00
|
|
|
let message;
|
2021-10-20 09:50:50 +00:00
|
|
|
if (localAccountSensitiveSettings.notificationMessagePreviewSetting > Constants.notificationPreviewNameOnly) {
|
2021-07-16 15:02:47 +00:00
|
|
|
switch(contentType){
|
|
|
|
//% "Image"
|
|
|
|
case Constants.imageType: message = qsTrId("image"); break
|
|
|
|
//% "Sticker"
|
|
|
|
case Constants.stickerType: message = qsTrId("sticker"); break
|
|
|
|
default: message = msg // don't parse emojis here as it emits HTML
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//% "You have a new message"
|
|
|
|
message = qsTrId("you-have-a-new-message")
|
2021-07-14 12:35:37 +00:00
|
|
|
}
|
|
|
|
|
2021-07-16 15:02:47 +00:00
|
|
|
currentlyHasANotification = true
|
2021-08-18 14:43:59 +00:00
|
|
|
|
2021-11-30 17:52:34 +00:00
|
|
|
if (Qt.platform.os === "linux") {
|
|
|
|
// Linux Notifications are not implemented in Nim/C++ yet
|
|
|
|
return systemTray.showMessage(name, message, systemTray.icon.source, 4000)
|
|
|
|
}
|
|
|
|
|
2021-08-18 14:43:59 +00:00
|
|
|
// 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.
|
2021-10-21 22:39:53 +00:00
|
|
|
root.rootStore.chatsModelInst.showOSNotification(name,
|
2021-08-18 14:43:59 +00:00
|
|
|
message,
|
|
|
|
Constants.osNotificationType.newMessage,
|
|
|
|
communityId,
|
|
|
|
chatId,
|
|
|
|
messageId,
|
2021-10-20 09:50:50 +00:00
|
|
|
localAccountSensitiveSettings.useOSNotifications)
|
2021-07-14 12:35:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-07-16 15:02:47 +00:00
|
|
|
|
|
|
|
Component.onCompleted: {
|
2021-09-21 12:26:32 +00:00
|
|
|
if(stackLayoutChatMessages.currentIndex >= 0 && stackLayoutChatMessages.currentIndex < stackLayoutChatMessages.children.length)
|
|
|
|
stackLayoutChatMessages.children[stackLayoutChatMessages.currentIndex].chatInput.textInput.forceActiveFocus(Qt.MouseFocusReason)
|
2021-07-16 15:02:47 +00:00
|
|
|
}
|
2021-07-27 14:04:05 +00:00
|
|
|
|
|
|
|
Connections {
|
2021-10-21 22:39:53 +00:00
|
|
|
target: root.rootStore.chatsModelInst.stickers
|
2021-07-27 14:04:05 +00:00
|
|
|
onTransactionWasSent: {
|
2021-07-05 12:34:56 +00:00
|
|
|
//% "Transaction pending..."
|
|
|
|
toastMessage.title = qsTr("Transaction pending...")
|
2021-09-28 15:04:06 +00:00
|
|
|
toastMessage.source = Style.svg("loading")
|
2021-07-27 14:04:05 +00:00
|
|
|
toastMessage.iconColor = Style.current.primary
|
|
|
|
toastMessage.iconRotates = true
|
|
|
|
toastMessage.link = `${walletModel.utilsView.etherscanLink}/${txResult}`
|
|
|
|
toastMessage.open()
|
|
|
|
}
|
|
|
|
onTransactionCompleted: {
|
2021-08-19 13:04:52 +00:00
|
|
|
toastMessage.title = !success ?
|
2021-07-27 14:04:05 +00:00
|
|
|
//% "Could not buy Stickerpack"
|
|
|
|
qsTrId("could-not-buy-stickerpack")
|
|
|
|
:
|
|
|
|
//% "Stickerpack bought successfully"
|
|
|
|
qsTrId("stickerpack-bought-successfully");
|
|
|
|
if (success) {
|
2021-09-28 15:04:06 +00:00
|
|
|
toastMessage.source = Style.svg("check-circle")
|
2021-07-27 14:04:05 +00:00
|
|
|
toastMessage.iconColor = Style.current.success
|
|
|
|
} else {
|
2021-09-28 15:04:06 +00:00
|
|
|
toastMessage.source = Style.svg("block-icon")
|
2021-07-27 14:04:05 +00:00
|
|
|
toastMessage.iconColor = Style.current.danger
|
|
|
|
}
|
|
|
|
|
|
|
|
toastMessage.link = `${walletModel.utilsView.etherscanLink}/${txHash}`
|
|
|
|
toastMessage.open()
|
|
|
|
}
|
|
|
|
}
|
2021-07-14 12:35:37 +00:00
|
|
|
}
|
2020-05-25 20:34:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*##^##
|
|
|
|
Designer {
|
2020-05-27 17:26:21 +00:00
|
|
|
D{i:0;formeditorColor:"#ffffff";height:770;width:800}
|
2020-05-25 20:34:26 +00:00
|
|
|
}
|
2021-05-26 18:56:04 +00:00
|
|
|
##^##*/
|