parent
dc14bbe9ec
commit
12a7d7c067
|
@ -2,9 +2,11 @@ import QtQuick 2.13
|
|||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import "../../../shared"
|
||||
import "../../../shared/status"
|
||||
import "../../../imports"
|
||||
import "./components"
|
||||
import "./ChatColumn"
|
||||
import "./ChatColumn/ChatComponents"
|
||||
import "./data"
|
||||
|
||||
StackLayout {
|
||||
|
@ -27,7 +29,6 @@ StackLayout {
|
|||
chatInput.textInput.forceActiveFocus(Qt.MouseFocusReason)
|
||||
}
|
||||
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumWidth: 300
|
||||
|
@ -37,21 +38,33 @@ StackLayout {
|
|||
function showReplyArea() {
|
||||
isReply = true;
|
||||
isImage = false;
|
||||
replyAreaContainer.setup()
|
||||
let replyMessageIndex = chatsModel.messageList.getMessageIndex(SelectedMessage.messageId);
|
||||
if (replyMessageIndex === -1) return;
|
||||
|
||||
let userName = chatsModel.messageList.getMessageData(replyMessageIndex, "userName")
|
||||
let message = chatsModel.messageList.getMessageData(replyMessageIndex, "message")
|
||||
let identicon = chatsModel.messageList.getMessageData(replyMessageIndex, "identicon")
|
||||
|
||||
chatInput.showReplyArea(userName, message, identicon)
|
||||
}
|
||||
|
||||
function showImageArea(imagePath) {
|
||||
isImage = true;
|
||||
isReply = false;
|
||||
sendImageArea.image = imagePath[0];
|
||||
function requestAddressForTransaction(address, amount, tokenAddress, tokenDecimals = 18) {
|
||||
amount = walletModel.eth2Wei(amount.toString(), tokenDecimals)
|
||||
chatsModel.requestAddressForTransaction(chatsModel.activeChannel.id,
|
||||
address,
|
||||
amount,
|
||||
tokenAddress)
|
||||
chatCommandModal.close()
|
||||
}
|
||||
function requestTransaction(address, amount, tokenAddress, tokenDecimals = 18) {
|
||||
amount = walletModel.eth2Wei(amount.toString(), tokenDecimals)
|
||||
chatsModel.requestTransaction(chatsModel.activeChannel.id,
|
||||
address,
|
||||
amount,
|
||||
tokenAddress)
|
||||
chatCommandModal.close()
|
||||
}
|
||||
|
||||
function hideExtendedArea() {
|
||||
isImage = false;
|
||||
isReply = false;
|
||||
replyAreaContainer.setup();
|
||||
sendImageArea.image = "";
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
|
@ -164,14 +177,12 @@ StackLayout {
|
|||
|
||||
Rectangle {
|
||||
id: inputArea
|
||||
color: Style.current.background
|
||||
border.width: 1
|
||||
border.color: Style.current.border
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: parent.width
|
||||
height: (!isExtendedInput ? 70 : 140) * chatInput.extraHeightFactor
|
||||
height: chatInput.height
|
||||
Layout.preferredHeight: height
|
||||
color: "transparent"
|
||||
|
||||
SuggestionBox {
|
||||
id: suggestionsBox
|
||||
|
@ -209,16 +220,6 @@ StackLayout {
|
|||
}
|
||||
}
|
||||
|
||||
ReplyArea {
|
||||
id: replyAreaContainer
|
||||
visible: isReply
|
||||
}
|
||||
|
||||
SendImageArea {
|
||||
id: sendImageArea
|
||||
visible: isImage
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: chatsModel.loadingMessages
|
||||
sourceComponent: loadingIndicator
|
||||
|
@ -247,31 +248,56 @@ StackLayout {
|
|||
}
|
||||
}
|
||||
|
||||
ChatInput {
|
||||
StatusChatInput {
|
||||
id: chatInput
|
||||
height: 40
|
||||
anchors.top: {
|
||||
if(!isExtendedInput){
|
||||
return inputArea.top;
|
||||
}
|
||||
|
||||
if(isReply){
|
||||
return replyAreaContainer.bottom;
|
||||
}
|
||||
|
||||
if(isImage){
|
||||
return sendImageArea.bottom;
|
||||
}
|
||||
}
|
||||
anchors.topMargin: 4
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
recentStickers: chatsModel.recentStickers
|
||||
stickerPackList: chatsModel.stickerPacks
|
||||
chatType: chatsModel.activeChannel.chatType
|
||||
onSendTransactionCommandButtonClicked: {
|
||||
chatCommandModal.sendChatCommand = chatColumnLayout.requestAddressForTransaction
|
||||
chatCommandModal.isRequested = false
|
||||
//% "Send"
|
||||
chatCommandModal.commandTitle = qsTrId("command-button-send")
|
||||
chatCommandModal.title = chatCommandModal.commandTitle
|
||||
//% "Request Address"
|
||||
chatCommandModal.finalButtonLabel = qsTrId("request-address")
|
||||
chatCommandModal.selectedRecipient = {
|
||||
address: Constants.zeroAddress, // Setting as zero address since we don't have the address yet
|
||||
identicon: chatsModel.activeChannel.identicon,
|
||||
name: chatsModel.activeChannel.name,
|
||||
type: RecipientSelector.Type.Contact
|
||||
}
|
||||
chatCommandModal.open()
|
||||
}
|
||||
onReceiveTransactionCommandButtonClicked: {
|
||||
chatCommandModal.sendChatCommand = root.requestTransaction
|
||||
chatCommandModal.isRequested = true
|
||||
//% "Request"
|
||||
chatCommandModal.commandTitle = qsTrId("wallet-request")
|
||||
chatCommandModal.title = chatCommandModal.commandTitle
|
||||
//% "Request"
|
||||
chatCommandModal.finalButtonLabel = qsTrId("wallet-request")
|
||||
chatCommandModal.selectedRecipient = {
|
||||
address: Constants.zeroAddress, // Setting as zero address since we don't have the address yet
|
||||
identicon: chatsModel.activeChannel.identicon,
|
||||
name: chatsModel.activeChannel.name,
|
||||
type: RecipientSelector.Type.Contact
|
||||
}
|
||||
chatCommandModal.open()
|
||||
}
|
||||
onStickerSelected: {
|
||||
chatsModel.sendSticker(hashId, packId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EmptyChat {}
|
||||
|
||||
ChatCommandModal {
|
||||
id: chatCommandModal
|
||||
}
|
||||
}
|
||||
|
||||
/*##^##
|
||||
|
|
|
@ -1,127 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtGraphicalEffects 1.13
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
import "../components"
|
||||
import "./ChatComponents"
|
||||
|
||||
Row {
|
||||
property int iconPadding: 6
|
||||
property var addToChat: function () {}
|
||||
property var onSend: function () {}
|
||||
|
||||
id: chatButtonsContainer
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding
|
||||
spacing: 0
|
||||
|
||||
// ChildrenRect doesn't work with the width being able to change
|
||||
width: chatSendBtn.width + emojiIconButton.width +
|
||||
stickerIconButton.width + imageIconButton.width + commandIconButton.width
|
||||
|
||||
Button {
|
||||
id: chatSendBtn
|
||||
visible: txtData.length > 0 || chatColumn.isImage
|
||||
width: this.visible ? 30 : 0
|
||||
height: this.width
|
||||
text: ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: {
|
||||
onSend();
|
||||
}
|
||||
background: Rectangle {
|
||||
color: parent.enabled ? Style.current.blue : Style.current.grey
|
||||
radius: 50
|
||||
}
|
||||
SVGImage {
|
||||
source: "../../../img/arrowUp.svg"
|
||||
width: 13
|
||||
height: 17
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
ChatInputButton {
|
||||
id: emojiIconButton
|
||||
source: "../../../img/emojiBtn.svg"
|
||||
opened: emojiPopup.opened
|
||||
close: function () {
|
||||
emojiPopup.close()
|
||||
}
|
||||
open: function () {
|
||||
emojiPopup.open()
|
||||
}
|
||||
}
|
||||
|
||||
ChatInputButton {
|
||||
id: stickerIconButton
|
||||
visible: !chatColumn.isExtendedInput && txtData.length == 0
|
||||
source: "../../../img/stickers_icon.svg"
|
||||
opened: stickersPopup.opened
|
||||
close: function () {
|
||||
stickersPopup.close()
|
||||
}
|
||||
open: function () {
|
||||
stickersPopup.open()
|
||||
}
|
||||
}
|
||||
|
||||
ChatInputButton {
|
||||
id: imageIconButton
|
||||
visible: !chatColumn.isExtendedInput && (chatsModel.activeChannel.chatType === Constants.chatTypePrivateGroupChat || chatsModel.activeChannel.chatType === Constants.chatTypeOneToOne)
|
||||
source: "../../../img/images_icon.svg"
|
||||
opened: imageDialog.visible
|
||||
close: function () {
|
||||
imageDialog.close()
|
||||
}
|
||||
open: function () {
|
||||
imageDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
ChatInputButton {
|
||||
id: commandIconButton
|
||||
visible: !chatColumn.isExtendedInput && chatsModel.activeChannel.chatType === Constants.chatTypeOneToOne
|
||||
source: "../../../img/chat-commands.svg"
|
||||
opened: chatCommandsPopup.opened
|
||||
close: function () {
|
||||
chatCommandsPopup.close()
|
||||
}
|
||||
open: function () {
|
||||
chatCommandsPopup.open()
|
||||
}
|
||||
}
|
||||
|
||||
StickersPopup {
|
||||
id: stickersPopup
|
||||
width: 360
|
||||
height: 440
|
||||
x: parent.width - width - Style.current.halfPadding
|
||||
y: parent.height - sendBtns.height - height - Style.current.halfPadding
|
||||
recentStickers: chatsModel.recentStickers
|
||||
stickerPackList: chatsModel.stickerPacks
|
||||
}
|
||||
|
||||
EmojiPopup {
|
||||
id: emojiPopup
|
||||
width: 360
|
||||
height: 440
|
||||
x: parent.width - width - Style.current.halfPadding
|
||||
y: parent.height - sendBtns.height - height - Style.current.halfPadding
|
||||
addToChat: chatButtonsContainer.addToChat
|
||||
}
|
||||
|
||||
ChatCommandsPopup {
|
||||
id: chatCommandsPopup
|
||||
x: parent.width - width - Style.current.halfPadding
|
||||
y: parent.height - sendBtns.height - height - Style.current.halfPadding
|
||||
}
|
||||
}
|
||||
/*##^##
|
||||
Designer {
|
||||
D{i:0;formeditorColor:"#ffffff";formeditorZoom:1.75}
|
||||
}
|
||||
##^##*/
|
|
@ -1,453 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import QtMultimedia 5.13
|
||||
import QtQuick.Dialogs 1.3
|
||||
import "../components"
|
||||
import "../../../../shared"
|
||||
import "../../../../imports"
|
||||
|
||||
|
||||
import "../components/emojiList.js" as EmojiJSON
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property alias textInput: txtData
|
||||
border.width: 0
|
||||
height: 52
|
||||
color: Style.current.transparent
|
||||
|
||||
visible: chatsModel.activeChannel.chatType !== Constants.chatTypePrivateGroupChat || chatsModel.activeChannel.isMember
|
||||
|
||||
property bool emojiEvent: false;
|
||||
property bool paste: false;
|
||||
property bool isColonPressed: false;
|
||||
|
||||
property int extraHeightFactor: calculateExtraHeightFactor()
|
||||
property int messageLimit: 2000
|
||||
property int messageLimitVisible: 200
|
||||
|
||||
Audio {
|
||||
id: sendMessageSound
|
||||
source: "../../../../sounds/send_message.wav"
|
||||
volume: appSettings.volume
|
||||
}
|
||||
|
||||
function calculateExtraHeightFactor() {
|
||||
const factor = (txtData.length / 500) + 1;
|
||||
return (factor > 5) ? 5 : factor;
|
||||
}
|
||||
|
||||
function insertInTextInput(start, text) {
|
||||
// Repace new lines with entities because `insert` gets rid of them
|
||||
txtData.insert(start, text.replace(/\n/g, "<br/>"));
|
||||
}
|
||||
|
||||
function interpretMessage(msg) {
|
||||
if (msg === "/shrug") {
|
||||
return "¯\\\\\\_(ツ)\\_/¯"
|
||||
}
|
||||
if (msg === "/tableflip") {
|
||||
return "(╯°□°)╯︵ ┻━┻"
|
||||
}
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
function sendMsg(event){
|
||||
if(chatColumn.isImage){
|
||||
const error = chatsModel.sendImage(sendImageArea.image);
|
||||
if (error) {
|
||||
toastMessage.title = error
|
||||
toastMessage.source = "../../../img/block-icon.svg"
|
||||
toastMessage.iconColor = Style.current.danger
|
||||
toastMessage.linkText = ""
|
||||
toastMessage.open()
|
||||
}
|
||||
}
|
||||
var msg = chatsModel.plainText(Emoji.deparse(txtData.text).trim()).trim()
|
||||
if(msg.length > 0){
|
||||
msg = interpretMessage(msg)
|
||||
chatsModel.sendMessage(msg, chatColumn.isReply ? SelectedMessage.messageId : "", Utils.isOnlyEmoji(msg) ? Constants.emojiType : Constants.messageType);
|
||||
txtData.text = "";
|
||||
if(event) event.accepted = true
|
||||
sendMessageSound.stop()
|
||||
Qt.callLater(sendMessageSound.play);
|
||||
}
|
||||
chatColumn.hideExtendedArea();
|
||||
}
|
||||
|
||||
function onEnter(event){
|
||||
if (event.modifiers === Qt.NoModifier && (event.key === Qt.Key_Enter || event.key === Qt.Key_Return)) {
|
||||
if (emojiSuggestions.visible) {
|
||||
emojiSuggestions.addEmoji();
|
||||
event.accepted = true;
|
||||
return
|
||||
}
|
||||
if (txtData.length < messageLimit) {
|
||||
sendMsg(event);
|
||||
return;
|
||||
}
|
||||
if(event) event.accepted = true
|
||||
messageTooLongDialog.open()
|
||||
}
|
||||
|
||||
if ((event.key === Qt.Key_V) && (event.modifiers & Qt.ControlModifier)) {
|
||||
paste = true;
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_Down) {
|
||||
return emojiList.incrementCurrentIndex()
|
||||
}
|
||||
if (event.key === Qt.Key_Up) {
|
||||
return emojiList.decrementCurrentIndex()
|
||||
}
|
||||
|
||||
isColonPressed = (event.key === Qt.Key_Colon) && (event.modifiers & Qt.ShiftModifier);
|
||||
}
|
||||
|
||||
function onRelease(event) {
|
||||
// the text doesn't get registered to the textarea fast enough
|
||||
// we can only get it in the `released` event
|
||||
if (paste) {
|
||||
paste = false;
|
||||
interrogateMessage();
|
||||
}
|
||||
|
||||
emojiEvent = emojiHandler(event);
|
||||
if (!emojiEvent) {
|
||||
emojiSuggestions.close()
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseClicked() {
|
||||
emojiEvent = emojiHandler({key: null});
|
||||
}
|
||||
|
||||
function interrogateMessage() {
|
||||
const text = chatsModel.plainText(Emoji.deparse(txtData.text));
|
||||
var words = text.split(' ');
|
||||
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
var transform = true;
|
||||
if (words[i].charAt(0) === ':') {
|
||||
for (var j = 0; j < words[i].length; j++) {
|
||||
if (Utils.isSpace(words[i].charAt(j)) === true || Utils.isPunct(words[i].charAt(j)) === true) {
|
||||
transform = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (transform) {
|
||||
const codePoint = Emoji.getEmojiUnicode(words[i]);
|
||||
words[i] = words[i].replace(words[i], (codePoint !== undefined) ? Emoji.fromCodePoint(codePoint) : words[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
txtData.remove(0, txtData.length);
|
||||
insertInTextInput(0, Emoji.parse(words.join(' '), '26x26'));
|
||||
}
|
||||
|
||||
function replaceWithEmoji(message, shortname, codePoint) {
|
||||
const encodedCodePoint = Emoji.getEmojiCodepoint(codePoint)
|
||||
const newMessage = message.data
|
||||
.replace(shortname, encodedCodePoint)
|
||||
.replace(/ /g, " ");
|
||||
txtData.remove(0, txtData.cursorPosition);
|
||||
insertInTextInput(0, Emoji.parse(newMessage, '26x26'));
|
||||
emojiSuggestions.close()
|
||||
emojiEvent = false
|
||||
}
|
||||
|
||||
function emojiHandler(event) {
|
||||
let message = extrapolateCursorPosition();
|
||||
pollEmojiEvent(message);
|
||||
|
||||
// state machine to handle different forms of the emoji event state
|
||||
if (!emojiEvent && isColonPressed) {
|
||||
return (message.data.length <= 1 || Utils.isSpace(message.data.charAt(message.cursor - 1))) ? true : false;
|
||||
} else if (emojiEvent && isColonPressed) {
|
||||
const index = message.data.lastIndexOf(':', message.cursor - 2);
|
||||
if (index >= 0 && message.cursor > 0) {
|
||||
const shortname = message.data.substr(index, message.cursor);
|
||||
const codePoint = Emoji.getEmojiUnicode(shortname);
|
||||
if (codePoint !== undefined) {
|
||||
replaceWithEmoji(message, shortname, codePoint);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (emojiEvent && isKeyValid(event.key) && !isColonPressed) {
|
||||
// popup
|
||||
const index2 = message.data.lastIndexOf(':', message.cursor - 1);
|
||||
if (index2 >= 0 && message.cursor > 0) {
|
||||
const emojiPart = message.data.substr(index2, message.cursor);
|
||||
if (emojiPart.length > 2) {
|
||||
const emojis = EmojiJSON.emoji_json.filter(function (emoji) {
|
||||
return emoji.name.includes(emojiPart) ||
|
||||
emoji.shortname.includes(emojiPart) ||
|
||||
emoji.aliases.some(a => a.includes(emojiPart))
|
||||
})
|
||||
|
||||
emojiSuggestions.openPopup(emojis, emojiPart)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else if (emojiEvent && !isKeyValid(event.key) && !isColonPressed) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// since emoji length is not 1 we need to match that position that TextArea returns
|
||||
// to the actual position in the string.
|
||||
function extrapolateCursorPosition() {
|
||||
// we need only the message part to be html
|
||||
const text = chatsModel.plainText(Emoji.deparse(txtData.text));
|
||||
const plainText = Emoji.parse(text, '26x26');
|
||||
|
||||
var bracketEvent = false;
|
||||
var length = 0;
|
||||
|
||||
for (var i = 0; i < plainText.length;) {
|
||||
if (length >= txtData.cursorPosition) break;
|
||||
|
||||
if (!bracketEvent && plainText.charAt(i) !== '<') {
|
||||
i++;
|
||||
length++;
|
||||
} else if (!bracketEvent && plainText.charAt(i) === '<') {
|
||||
bracketEvent = true;
|
||||
i++;
|
||||
} else if (bracketEvent && plainText.charAt(i) !== '>') {
|
||||
i++;
|
||||
} else if (bracketEvent && plainText.charAt(i) === '>') {
|
||||
bracketEvent = false;
|
||||
i++;
|
||||
length++;
|
||||
}
|
||||
}
|
||||
|
||||
let textBeforeCursor = Emoji.deparseFromParse(plainText.substr(0, i));
|
||||
return {
|
||||
cursor: countEmojiLengths(plainText.substr(0, i)) + txtData.cursorPosition,
|
||||
data: Emoji.deparseFromParse(textBeforeCursor),
|
||||
};
|
||||
}
|
||||
|
||||
function countEmojiLengths(value) {
|
||||
const match = Emoji.getEmojis(value);
|
||||
var length = 0;
|
||||
|
||||
if (match && match.length > 0) {
|
||||
for (var i = 0; i < match.length; i++) {
|
||||
length += Emoji.deparseFromParse(match[i]).length;
|
||||
}
|
||||
length = length - match.length;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
// check if user has placed cursor near valid emoji colon token
|
||||
function pollEmojiEvent(message) {
|
||||
const index = message.data.lastIndexOf(':', message.cursor);
|
||||
if (index >= 0) {
|
||||
emojiEvent = validSubstr(message.data.substr(index, message.cursor - index));
|
||||
}
|
||||
}
|
||||
|
||||
function validSubstr(substr) {
|
||||
for(var i = 0; i < substr.length; i++) {
|
||||
var c = substr.charAt(i);
|
||||
if (c !== '_' && (Utils.isSpace(c) === true || Utils.isPunct(c) === true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function isKeyValid(key) {
|
||||
if (key !== Qt.Key_Underscore &&
|
||||
(key === Qt.Key_Space || key === Qt.Key_Tab ||
|
||||
(key >= Qt.Key_Exclam && key <= Qt.Key_Slash) ||
|
||||
(key >= Qt.Key_Semicolon && key <= Qt.Key_Question) ||
|
||||
(key >= Qt.Key_BracketLeft && key <= Qt.Key_hyphen)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: imageDialog
|
||||
//% "Please choose an image"
|
||||
title: qsTrId("please-choose-an-image")
|
||||
folder: shortcuts.pictures
|
||||
nameFilters: [
|
||||
//% "Image files (*.jpg *.jpeg *.png)"
|
||||
qsTrId("image-files----jpg---jpeg---png-")
|
||||
]
|
||||
onAccepted: {
|
||||
chatColumn.showImageArea(imageDialog.fileUrls);
|
||||
txtData.forceActiveFocus();
|
||||
}
|
||||
onRejected: {
|
||||
chatColumn.hideExtendedArea();
|
||||
}
|
||||
}
|
||||
|
||||
Popup {
|
||||
property var emojis
|
||||
property string shortname
|
||||
|
||||
function openPopup(emojisParam, shortnameParam) {
|
||||
emojis = emojisParam
|
||||
shortname = shortnameParam
|
||||
emojiSuggestions.open()
|
||||
}
|
||||
|
||||
function addEmoji(index) {
|
||||
if (index === undefined) {
|
||||
index = emojiList.currentIndex
|
||||
}
|
||||
|
||||
const message = extrapolateCursorPosition();
|
||||
const unicode = emojiSuggestions.emojis[index].unicode_alternates || emojiSuggestions.emojis[index].unicode
|
||||
replaceWithEmoji(message, emojiSuggestions.shortname, unicode)
|
||||
}
|
||||
|
||||
id: emojiSuggestions
|
||||
width: parent.width - Style.current.padding * 2
|
||||
height: Math.min(400, emojiList.contentHeight + Style.current.smallPadding * 2)
|
||||
x : Style.current.padding / 2
|
||||
y: -height - Style.current.smallPadding
|
||||
background: Rectangle {
|
||||
visible: !!emojiSuggestions.emojis && emojiSuggestions.emojis.length > 0
|
||||
color: Style.current.secondaryBackground
|
||||
border.width: 1
|
||||
border.color: Style.current.borderSecondary
|
||||
radius: 8
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: emojiList
|
||||
model: emojiSuggestions.emojis || []
|
||||
keyNavigationEnabled: true
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
|
||||
delegate: Rectangle {
|
||||
id: rectangle
|
||||
color: emojiList.currentIndex === index ? Style.current.inputBorderFocus : Style.current.transparent
|
||||
border.width: 0
|
||||
width: parent.width
|
||||
height: 42
|
||||
radius: 8
|
||||
|
||||
SVGImage {
|
||||
id: emojiImage
|
||||
source: `../../../../imports/twemoji/26x26/${modelData.unicode}.png`
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.smallPadding
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: modelData.shortname
|
||||
color: emojiList.currentIndex === index ? Style.current.currentUserTextColor : Style.current.textColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: emojiImage.right
|
||||
anchors.leftMargin: Style.current.smallPadding
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
emojiList.currentIndex = index
|
||||
}
|
||||
onClicked: {
|
||||
emojiSuggestions.addEmoji(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: scrollView
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: sendBtns.left
|
||||
anchors.rightMargin: 0
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
topPadding: Style.current.padding
|
||||
|
||||
StyledTArea {
|
||||
textFormat: Text.RichText
|
||||
id: txtData
|
||||
text: ""
|
||||
selectByMouse: true
|
||||
wrapMode: TextArea.Wrap
|
||||
font.pixelSize: 15
|
||||
//% "Type a message..."
|
||||
placeholderText: qsTrId("type-a-message")
|
||||
Keys.onPressed: onEnter(event)
|
||||
Keys.onReleased: onRelease(event) // gives much more up to date cursorPosition
|
||||
background: Rectangle {
|
||||
color: Style.current.transparent
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
id: mousearea
|
||||
onTapped: onMouseClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: messageLengthLimit
|
||||
property int remainingChars: messageLimit - txtData.length
|
||||
text: remainingChars.toString()
|
||||
visible: remainingChars <= root.messageLimitVisible
|
||||
color: (remainingChars <= 0) ? Style.current.danger : Style.current.textColor
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: sendBtns.top
|
||||
anchors.rightMargin: Style.current.padding
|
||||
leftPadding: Style.current.halfPadding
|
||||
rightPadding: Style.current.halfPadding
|
||||
}
|
||||
|
||||
ChatButtons {
|
||||
id: sendBtns
|
||||
height: 36
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding
|
||||
anchors.bottom: parent.bottom
|
||||
addToChat: function (text, atCursor) {
|
||||
insertInTextInput(atCursor ? txtData.cursorPosition :txtData.length, text)
|
||||
}
|
||||
onSend: function(){
|
||||
if (txtData.length < messageLimit) {
|
||||
sendMsg(false);
|
||||
return;
|
||||
}
|
||||
messageTooLongDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
MessageDialog {
|
||||
id: messageTooLongDialog
|
||||
//% "Your message is too long."
|
||||
title: qsTrId("your-message-is-too-long.")
|
||||
icon: StandardIcon.Critical
|
||||
//% "Please make your message shorter. We have set the limit to 2000 characters to be courteous of others."
|
||||
text: qsTrId("please-make-your-message-shorter.-we-have-set-the-limit-to-2000-characters-to-be-courteous-of-others.")
|
||||
standardButtons: StandardButton.Ok
|
||||
}
|
||||
}
|
||||
/*##^##
|
||||
Designer {
|
||||
D{i:0;formeditorColor:"#ffffff"}
|
||||
}
|
||||
##^##*/
|
|
@ -1,93 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import QtGraphicalEffects 1.13
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
import "../../../../shared/status"
|
||||
import "./"
|
||||
|
||||
Rectangle {
|
||||
property string userName: "Joseph Joestar"
|
||||
property string message: "Your next line is: this is a Jojo reference"
|
||||
property string identicon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII="
|
||||
|
||||
id: replyArea
|
||||
height: 70
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
color: "#00000000"
|
||||
|
||||
function setup(){
|
||||
let replyMessageIndex = chatsModel.messageList.getMessageIndex(SelectedMessage.messageId);
|
||||
if (replyMessageIndex == -1) return;
|
||||
|
||||
userName = chatsModel.messageList.getMessageData(replyMessageIndex, "userName")
|
||||
message = chatsModel.messageList.getMessageData(replyMessageIndex, "message")
|
||||
identicon = chatsModel.messageList.getMessageData(replyMessageIndex, "identicon")
|
||||
}
|
||||
|
||||
function reset(){
|
||||
userName = "";
|
||||
message= "";
|
||||
identicon = "";
|
||||
}
|
||||
|
||||
StatusIconButton {
|
||||
id: closeButton
|
||||
type: "secondary"
|
||||
icon.name: "close"
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Style.current.padding
|
||||
anchors.rightMargin: Style.current.padding
|
||||
anchors.right: parent.right
|
||||
onClicked: {
|
||||
chatColumn.hideExtendedArea()
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: chatImage
|
||||
width: 36
|
||||
height: 36
|
||||
anchors.topMargin: 20
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.top: parent.top
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: identicon
|
||||
mipmap: true
|
||||
smooth: false
|
||||
antialiasing: true
|
||||
}
|
||||
|
||||
StyledTextEdit {
|
||||
id: replyToUsername
|
||||
text: userName
|
||||
font.bold: true
|
||||
font.pixelSize: 14
|
||||
anchors.leftMargin: 20
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 0
|
||||
anchors.left: chatImage.right
|
||||
readOnly: true
|
||||
wrapMode: Text.WordWrap
|
||||
selectByMouse: true
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: replyText
|
||||
text: Emoji.parse(message, "26x26")
|
||||
anchors.left: replyToUsername.left
|
||||
anchors.top: replyToUsername.bottom
|
||||
anchors.topMargin: 8
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding * 2 + closeButton.width
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.Wrap
|
||||
font.pixelSize: 15
|
||||
textFormat: Text.RichText
|
||||
}
|
||||
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
import "./"
|
||||
|
||||
Rectangle {
|
||||
id: sendImageArea
|
||||
height: 70
|
||||
|
||||
property string image: ""
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
color: "#00000000"
|
||||
|
||||
Rectangle {
|
||||
id: closeButton
|
||||
height: 32
|
||||
width: 32
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Style.current.padding
|
||||
anchors.rightMargin: Style.current.padding
|
||||
anchors.right: parent.right
|
||||
radius: 8
|
||||
|
||||
SVGImage {
|
||||
id: closeModalImg
|
||||
source: "../../../../shared/img/close.svg"
|
||||
width: 11
|
||||
height: 11
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: closeImageArea
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onExited: {
|
||||
closeButton.color = Style.current.white
|
||||
}
|
||||
onEntered: {
|
||||
closeButton.color = Style.current.grey
|
||||
}
|
||||
onClicked: {
|
||||
chatColumn.hideExtendedArea();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: chatImage
|
||||
width: 36
|
||||
height: 36
|
||||
anchors.topMargin: 20
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.top: parent.top
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: image
|
||||
mipmap: true
|
||||
smooth: false
|
||||
antialiasing: true
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
|
||||
Rectangle {
|
||||
property bool active: false
|
||||
property var changeCategory: function () {}
|
||||
property url source: "../../../img/emojiCategories/recent.svg"
|
||||
|
||||
id: categoryButton
|
||||
width: 40
|
||||
height: 40
|
||||
|
||||
SVGImage {
|
||||
width: 20
|
||||
height: 20
|
||||
fillMode: Image.PreserveAspectFit
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
source: categoryButton.source
|
||||
|
||||
ColorOverlay {
|
||||
anchors.fill: parent
|
||||
source: parent
|
||||
color: categoryButton.active ? Style.current.blue : Style.current.transparent
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: categoryButton.active
|
||||
width: parent.width
|
||||
height: 2
|
||||
radius: 1
|
||||
color: Style.current.blue
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: -Style.current.smallPadding
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
onClicked: function () {
|
||||
categoryButton.changeCategory()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*##^##
|
||||
Designer {
|
||||
D{i:0;formeditorColor:"#ffffff";height:440;width:360}
|
||||
}
|
||||
##^##*/
|
|
@ -1,228 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
import "../ChatColumn/samples"
|
||||
|
||||
import "./emojiList.js" as EmojiJSON
|
||||
|
||||
Popup {
|
||||
property var addToChat: function () {}
|
||||
property var categories: []
|
||||
property string searchString: searchBox.text
|
||||
|
||||
id: popup
|
||||
modal: false
|
||||
width: 360
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
||||
background: Rectangle {
|
||||
radius: Style.current.radius
|
||||
color: Style.current.background
|
||||
border.color: Style.current.border
|
||||
layer.enabled: true
|
||||
layer.effect: DropShadow{
|
||||
verticalOffset: 3
|
||||
radius: 8
|
||||
samples: 15
|
||||
fast: true
|
||||
cached: true
|
||||
color: "#22000000"
|
||||
}
|
||||
}
|
||||
|
||||
function addEmoji(emoji) {
|
||||
const MAX_EMOJI_NUMBER = 36
|
||||
const extenstionIndex = emoji.filename.lastIndexOf('.');
|
||||
let iconCodePoint = emoji.filename
|
||||
if (extenstionIndex > -1) {
|
||||
iconCodePoint = iconCodePoint.substring(0, extenstionIndex)
|
||||
}
|
||||
|
||||
const encodedIcon = Emoji.getEmojiCodepoint(iconCodePoint)
|
||||
|
||||
// Add at the start of the list
|
||||
let recentEmojis = appSettings.recentEmojis
|
||||
recentEmojis.unshift(emoji)
|
||||
// Remove duplicates
|
||||
recentEmojis = recentEmojis.filter(function (e, index) {
|
||||
return !recentEmojis.some(function (e2, index2) {
|
||||
return index2 < index && e2.filename === e.filename
|
||||
})
|
||||
})
|
||||
if (recentEmojis.length > MAX_EMOJI_NUMBER) {
|
||||
// remove last one
|
||||
recentEmojis.splice(MAX_EMOJI_NUMBER - 1)
|
||||
}
|
||||
emojiSectionsRepeater.itemAt(0).allEmojis = recentEmojis
|
||||
appSettings.recentEmojis = recentEmojis
|
||||
|
||||
popup.addToChat(Emoji.parse(encodedIcon, "26x26") + ' ', true) // Adding a space because otherwise, some emojis would fuse since emoji is just a string
|
||||
popup.close()
|
||||
chatInput.textInput.forceActiveFocus()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
var categoryNames = {"recent": 0}
|
||||
var newCategories = [[]]
|
||||
|
||||
EmojiJSON.emoji_json.forEach(function (emoji) {
|
||||
if (!categoryNames[emoji.category] && categoryNames[emoji.category] !== 0) {
|
||||
categoryNames[emoji.category] = newCategories.length
|
||||
newCategories.push([])
|
||||
}
|
||||
newCategories[categoryNames[emoji.category]].push(Object.assign({}, emoji, {filename: emoji.unicode + '.png'}))
|
||||
})
|
||||
|
||||
if (newCategories[categoryNames.recent].length === 0) {
|
||||
newCategories[categoryNames.recent].push({
|
||||
category: "recent",
|
||||
empty: true
|
||||
})
|
||||
}
|
||||
|
||||
categories = newCategories
|
||||
}
|
||||
Connections {
|
||||
target: applicationWindow
|
||||
onSettingsLoaded: {
|
||||
// Add recent
|
||||
if (!appSettings.recentEmojis || !appSettings.recentEmojis.length) {
|
||||
return
|
||||
}
|
||||
emojiSectionsRepeater.itemAt(0).allEmojis = appSettings.recentEmojis
|
||||
}
|
||||
}
|
||||
|
||||
onOpened: {
|
||||
searchBox.forceActiveFocus(Qt.MouseFocusReason)
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
property int headerMargin: 8
|
||||
|
||||
id: emojiHeader
|
||||
Layout.fillWidth: true
|
||||
height: searchBox.height + emojiHeader.headerMargin
|
||||
|
||||
SearchBox {
|
||||
id: searchBox
|
||||
anchors.right: skinToneEmoji.left
|
||||
anchors.rightMargin: emojiHeader.headerMargin
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: emojiHeader.headerMargin
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: emojiHeader.headerMargin
|
||||
}
|
||||
|
||||
SVGImage {
|
||||
id: skinToneEmoji
|
||||
width: 22
|
||||
height: 22
|
||||
anchors.verticalCenter: searchBox.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: emojiHeader.headerMargin
|
||||
source: "../../../../imports/twemoji/26x26/1f590.png"
|
||||
|
||||
MouseArea {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
onClicked: function () {
|
||||
console.log('Change skin tone')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
property ScrollBar vScrollBar: ScrollBar.vertical
|
||||
property var categrorySectionHeightRatios: []
|
||||
property int activeCategory: 0
|
||||
|
||||
id: scrollView
|
||||
topPadding: Style.current.smallPadding
|
||||
leftPadding: Style.current.smallPadding
|
||||
rightPadding: Style.current.smallPadding / 2
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: Style.current.smallPadding / 2
|
||||
Layout.topMargin: Style.current.smallPadding
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||
Layout.preferredHeight: 400 - Style.current.smallPadding - emojiHeader.height
|
||||
clip: true
|
||||
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
|
||||
ScrollBar.vertical.onPositionChanged: function () {
|
||||
if (vScrollBar.position < categrorySectionHeightRatios[scrollView.activeCategory - 1]) {
|
||||
scrollView.activeCategory--
|
||||
} else if (vScrollBar.position > categrorySectionHeightRatios[scrollView.activeCategory]) {
|
||||
scrollView.activeCategory++
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToCategory(category) {
|
||||
if (category === 0) {
|
||||
return vScrollBar.setPosition(0)
|
||||
}
|
||||
vScrollBar.setPosition(categrorySectionHeightRatios[category - 1])
|
||||
}
|
||||
|
||||
contentHeight: {
|
||||
var totalHeight = 0
|
||||
var categoryHeights = []
|
||||
for (let i = 0; i < emojiSectionsRepeater.count; i++) {
|
||||
totalHeight += emojiSectionsRepeater.itemAt(i).height + Style.current.padding
|
||||
categoryHeights.push(totalHeight)
|
||||
}
|
||||
var ratios = []
|
||||
categoryHeights.forEach(function (catHeight) {
|
||||
ratios.push(catHeight / totalHeight)
|
||||
})
|
||||
|
||||
categrorySectionHeightRatios = ratios
|
||||
return totalHeight + Style.current.padding
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: emojiSectionsRepeater
|
||||
model: popup.categories
|
||||
|
||||
EmojiSection {
|
||||
searchString: popup.searchString
|
||||
addEmoji: popup.addEmoji
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
Layout.fillWidth: true
|
||||
height: 40
|
||||
leftPadding: Style.current.smallPadding / 2
|
||||
rightPadding: Style.current.smallPadding / 2
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: EmojiJSON.emojiCategories
|
||||
|
||||
EmojiCategoryButton {
|
||||
source: `../../../img/emojiCategories/${modelData}.svg`
|
||||
active: index === scrollView.activeCategory
|
||||
changeCategory: function () {
|
||||
scrollView.activeCategory = index
|
||||
scrollView.scrollToCategory(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*##^##
|
||||
Designer {
|
||||
D{i:0;formeditorColor:"#ffffff";height:440;width:360}
|
||||
}
|
||||
##^##*/
|
|
@ -1,108 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Layouts 1.3
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
|
||||
|
||||
Item {
|
||||
property string searchString: ""
|
||||
property string searchStringLowercase: searchString.toLowerCase()
|
||||
property int imageWidth: 26
|
||||
property int imageMargin: 4
|
||||
property var emojis: []
|
||||
property var allEmojis: modelData
|
||||
property var addEmoji: function () {}
|
||||
|
||||
id: emojiSection
|
||||
visible: emojis.length > 0 || !!(modelData && modelData.length && modelData[0].empty && searchString === "")
|
||||
|
||||
anchors.top: index === 0 ? parent.top : parent.children[index - 1].bottom
|
||||
anchors.topMargin: index === 0 ? 0 : Style.current.padding
|
||||
|
||||
width: parent.width
|
||||
// childrenRect caused a binding loop here
|
||||
height: this.visible ? emojiGrid.height + categoryText.height + noRecentText.height + Style.current.padding : 0
|
||||
|
||||
StyledText {
|
||||
id: categoryText
|
||||
text: modelData && modelData.length ? modelData[0].category.toUpperCase() : ""
|
||||
color: Style.current.darkGrey
|
||||
font.pixelSize: 13
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: noRecentText
|
||||
visible: !!(allEmojis && allEmojis.length && allEmojis[0].empty)
|
||||
//% "No recent emojis"
|
||||
text: qsTrId("no-recent-emojis")
|
||||
color: Style.current.darkGrey
|
||||
font.pixelSize: 10
|
||||
anchors.top: categoryText.bottom
|
||||
anchors.topMargin: Style.current.smallPadding
|
||||
}
|
||||
|
||||
onSearchStringLowercaseChanged: {
|
||||
if (emojiSection.searchStringLowercase === "") {
|
||||
this.emojis = modelData
|
||||
return
|
||||
}
|
||||
this.emojis = modelData.filter(function (emoji) {
|
||||
return emoji.name.includes(emojiSection.searchStringLowercase) ||
|
||||
emoji.shortname.includes(emojiSection.searchStringLowercase) ||
|
||||
emoji.aliases.some(a => a.includes(emojiSection.searchStringLowercase))
|
||||
})
|
||||
}
|
||||
|
||||
onAllEmojisChanged: {
|
||||
if (this.allEmojis[0].empty) {
|
||||
return
|
||||
}
|
||||
this.emojis = this.allEmojis
|
||||
}
|
||||
|
||||
GridView {
|
||||
id: emojiGrid
|
||||
anchors.top: categoryText.bottom
|
||||
anchors.topMargin: Style.current.smallPadding
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
visible: count > 0
|
||||
cellWidth: emojiSection.imageWidth + emojiSection.imageMargin * 2
|
||||
cellHeight: emojiSection.imageWidth + emojiSection.imageMargin * 2
|
||||
model: emojiSection.emojis
|
||||
focus: true
|
||||
clip: true
|
||||
interactive: false
|
||||
|
||||
delegate: Item {
|
||||
id: emojiContainer
|
||||
width: emojiGrid.cellWidth
|
||||
height: emojiGrid.cellHeight
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: emojiSection.imageMargin
|
||||
anchors.leftMargin: emojiSection.imageMargin
|
||||
|
||||
SVGImage {
|
||||
width: emojiSection.imageWidth
|
||||
height: emojiSection.imageWidth
|
||||
source: "../../../../imports/twemoji/26x26/" + modelData.filename
|
||||
|
||||
MouseArea {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
emojiSection.addEmoji(modelData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*##^##
|
||||
Designer {
|
||||
D{i:0;formeditorColor:"#ffffff";height:440;width:360}
|
||||
}
|
||||
##^##*/
|
|
@ -1,269 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
enum StyleType {
|
||||
Default,
|
||||
LargeNoIcon
|
||||
}
|
||||
property int style: StickerButton.StyleType.Default
|
||||
property int packPrice: 0
|
||||
property bool isBought: false
|
||||
property bool isPending: false
|
||||
property bool isInstalled: false
|
||||
property bool hasUpdate: false
|
||||
property bool isTimedOut: false
|
||||
property bool hasInsufficientFunds: false
|
||||
property bool enabled: true
|
||||
property var icon: new Object({
|
||||
path: "../../../img/status-logo-no-bg",
|
||||
rotation: 0,
|
||||
runAnimation: false
|
||||
})
|
||||
//% "Buy for %1 SNT"
|
||||
property string text: root.style === StickerButton.StyleType.Default ? packPrice : qsTrId("buy-for--1-snt").arg(packPrice )
|
||||
property color textColor: style === StickerButton.StyleType.Default ? Style.current.pillButtonTextColor : Style.current.buttonForegroundColor
|
||||
property color bgColor: style === StickerButton.StyleType.Default ? Style.current.blue : Style.current.secondaryBackground
|
||||
signal uninstallClicked()
|
||||
signal installClicked()
|
||||
signal cancelClicked()
|
||||
signal updateClicked()
|
||||
signal buyClicked()
|
||||
width: pill.width
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "installed"
|
||||
when: root.isInstalled
|
||||
PropertyChanges {
|
||||
target: root;
|
||||
//% "Uninstall"
|
||||
text: root.style === StickerButton.StyleType.Default ? "" : qsTrId("uninstall");
|
||||
textColor: root.style === StickerButton.StyleType.Default ? Style.current.pillButtonTextColor : Style.current.red;
|
||||
bgColor: root.style === StickerButton.StyleType.Default ? Style.current.green : Style.current.lightRed;
|
||||
icon: new Object({
|
||||
path: "../../../img/check.svg",
|
||||
rotation: 0,
|
||||
runAnimation: false
|
||||
})
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "bought"
|
||||
when: root.isBought;
|
||||
PropertyChanges {
|
||||
target: root;
|
||||
//% "Install"
|
||||
text: qsTrId("install");
|
||||
icon: new Object({
|
||||
path: "../../../img/arrowUp.svg",
|
||||
rotation: 180,
|
||||
runAnimation: false
|
||||
})
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "free"
|
||||
when: root.packPrice === 0;
|
||||
extend: "bought"
|
||||
PropertyChanges {
|
||||
target: root;
|
||||
//% "Free"
|
||||
text: qsTrId("free");
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "insufficientFunds"
|
||||
when: root.hasInsufficientFunds
|
||||
PropertyChanges {
|
||||
target: root;
|
||||
text: root.style === StickerButton.StyleType.Default ? packPrice : packPrice + " SNT";
|
||||
textColor: root.style === StickerButton.StyleType.Default ? Style.current.pillButtonTextColor : Style.current.darkGrey
|
||||
bgColor: root.style === StickerButton.StyleType.Default ? Style.current.darkGrey : Style.current.buttonDisabledBackgroundColor;
|
||||
enabled: false;
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "pending"
|
||||
when: root.isPending
|
||||
PropertyChanges {
|
||||
target: root;
|
||||
//% "Pending..."
|
||||
text: qsTrId("pending---");
|
||||
textColor: root.style === StickerButton.StyleType.Default ? Style.current.pillButtonTextColor : Style.current.darkGrey
|
||||
bgColor: root.style === StickerButton.StyleType.Default ? Style.current.darkGrey : Style.current.grey;
|
||||
enabled: false;
|
||||
icon: new Object({
|
||||
path: "../../../img/loading.png",
|
||||
rotation: 0,
|
||||
runAnimation: true
|
||||
})
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "timedOut"
|
||||
when: root.isTimedOut
|
||||
extend: "pending"
|
||||
PropertyChanges {
|
||||
target: root;
|
||||
//% "Cancel"
|
||||
text: qsTrId("browsing-cancel");
|
||||
textColor: root.style === StickerButton.StyleType.Default ? Style.current.pillButtonTextColor : Style.current.red;
|
||||
bgColor: root.style === StickerButton.StyleType.Default ? Style.current.red : Style.current.lightRed;
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "hasUpdate"
|
||||
when: root.hasUpdate
|
||||
extend: "bought"
|
||||
PropertyChanges {
|
||||
target: root;
|
||||
//% "Update"
|
||||
text: qsTrId("update");
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
TextMetrics {
|
||||
id: textMetrics
|
||||
font.weight: Font.Medium
|
||||
font.family: Style.current.fontBold.name
|
||||
font.pixelSize: 15
|
||||
text: root.text
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: pill
|
||||
anchors.right: parent.right
|
||||
width: textMetrics.width + roundedIconImage.width + (Style.current.smallPadding * 2) + 6.7
|
||||
height: 26
|
||||
color: root.bgColor
|
||||
radius: root.style === StickerButton.StyleType.Default ? (width / 2) : 8
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "installed"
|
||||
when: root.isInstalled && root.style === StickerButton.StyleType.Default
|
||||
PropertyChanges {
|
||||
target: pill;
|
||||
width: 28;
|
||||
height: 28
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "large"
|
||||
when: root.style === StickerButton.StyleType.LargeNoIcon
|
||||
PropertyChanges {
|
||||
target: pill;
|
||||
width: textMetrics.width + (Style.current.padding * 4);
|
||||
height: 44
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
SVGImage {
|
||||
id: roundedIconImage
|
||||
width: 12
|
||||
height: 12
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.smallPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: icon.path
|
||||
rotation: icon.rotation
|
||||
RotationAnimator {
|
||||
target: roundedIconImage;
|
||||
from: 0;
|
||||
to: 360;
|
||||
duration: 1200
|
||||
running: root.icon.runAnimation
|
||||
loops: Animation.Infinite
|
||||
}
|
||||
ColorOverlay {
|
||||
anchors.fill: roundedIconImage
|
||||
source: roundedIconImage
|
||||
color: Style.current.pillButtonTextColor
|
||||
antialiasing: true
|
||||
}
|
||||
states: [
|
||||
State {
|
||||
name: "installed"
|
||||
when: root.isInstalled && root.style === StickerButton.StyleType.Default
|
||||
PropertyChanges {
|
||||
target: roundedIconImage;
|
||||
anchors.leftMargin: 9
|
||||
width: 11;
|
||||
height: 8
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "large"
|
||||
when: root.style === StickerButton.StyleType.LargeNoIcon
|
||||
PropertyChanges {
|
||||
target: roundedIconImage;
|
||||
visible: false;
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Text {
|
||||
id: content
|
||||
color: root.textColor
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.smallPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.text
|
||||
font.weight: Font.Medium
|
||||
font.family: Style.current.fontBold.name
|
||||
font.pixelSize: 15
|
||||
states: [
|
||||
State {
|
||||
name: "installed"
|
||||
when: root.isInstalled && root.style === StickerButton.StyleType.Default
|
||||
PropertyChanges {
|
||||
target: content;
|
||||
anchors.rightMargin: 9;
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "large"
|
||||
when: root.style === StickerButton.StyleType.LargeNoIcon
|
||||
PropertyChanges {
|
||||
target: content;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
anchors.leftMargin: Style.current.padding * 2
|
||||
anchors.rightMargin: Style.current.padding * 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
enabled: !root.isPending
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (root.isPending) return;
|
||||
if (root.isInstalled) return root.uninstallClicked();
|
||||
if (root.packPrice === 0 || root.isBought) return root.installClicked()
|
||||
if (root.isTimedOut) return root.cancelClicked()
|
||||
if (root.hasUpdate) return root.updateClicked()
|
||||
return root.buyClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*##^##
|
||||
Designer {
|
||||
D{i:0;autoSize:true;height:480;width:640}
|
||||
}
|
||||
##^##*/
|
|
@ -1,35 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
|
||||
GridView {
|
||||
id: root
|
||||
visible: count > 0
|
||||
anchors.fill: parent
|
||||
cellWidth: 88
|
||||
cellHeight: 88
|
||||
model: stickerList
|
||||
focus: true
|
||||
clip: true
|
||||
signal stickerClicked(string hash, int packId)
|
||||
delegate: Item {
|
||||
width: stickerGrid.cellWidth
|
||||
height: stickerGrid.cellHeight
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 4
|
||||
anchors.leftMargin: 4
|
||||
ImageLoader {
|
||||
width: 80
|
||||
height: 80
|
||||
source: "https://ipfs.infura.io/ipfs/" + url
|
||||
onClicked: {
|
||||
root.stickerClicked(hash, packId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,164 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
import "../ChatColumn/samples"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property var stickerPacks: StickerPackData {}
|
||||
signal backClicked
|
||||
signal uninstallClicked(int packId)
|
||||
signal installClicked(var stickers, int packId, int index)
|
||||
signal cancelClicked(int packId)
|
||||
signal updateClicked(int packId)
|
||||
signal buyClicked(int packId)
|
||||
|
||||
Component.onCompleted: {
|
||||
walletModel.getGasPricePredictions()
|
||||
}
|
||||
|
||||
GridView {
|
||||
id: availableStickerPacks
|
||||
width: parent.width
|
||||
height: 380
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Style.current.padding
|
||||
cellWidth: parent.width - (Style.current.padding * 2)
|
||||
cellHeight: height - 72
|
||||
model: stickerPacks
|
||||
focus: true
|
||||
clip: true
|
||||
delegate: Item {
|
||||
width: availableStickerPacks.cellWidth
|
||||
height: availableStickerPacks.cellHeight
|
||||
RoundedImage {
|
||||
id: imgPreview
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 220
|
||||
width: parent.width
|
||||
radius: 12
|
||||
source: "https://ipfs.infura.io/ipfs/" + preview
|
||||
onClicked: {
|
||||
stickerPackDetailsPopup.open()
|
||||
}
|
||||
}
|
||||
ModalPopup {
|
||||
id: stickerPackDetailsPopup
|
||||
height: 472
|
||||
header: StickerPackDetails {
|
||||
height: 46
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.topMargin: Style.current.padding
|
||||
width: parent.width - (Style.current.padding * 2)
|
||||
packThumb: thumbnail
|
||||
packName: name
|
||||
packAuthor: author
|
||||
packNameFontSize: 17
|
||||
spacing: Style.current.padding / 2
|
||||
}
|
||||
footer: StickerButton {
|
||||
height: 76
|
||||
anchors.right: parent.right
|
||||
style: StickerButton.StyleType.LargeNoIcon
|
||||
packPrice: price
|
||||
isInstalled: installed
|
||||
isBought: bought
|
||||
isPending: pending
|
||||
onInstallClicked: root.installClicked(stickers, packId, index)
|
||||
onUninstallClicked: root.uninstallClicked(packId)
|
||||
onCancelClicked: root.cancelClicked(packId)
|
||||
onUpdateClicked: root.updateClicked(packId)
|
||||
onBuyClicked: {
|
||||
stickerPackPurchaseModal.open()
|
||||
root.buyClicked(packId)
|
||||
}
|
||||
}
|
||||
contentWrapper.anchors.topMargin: 0
|
||||
contentWrapper.anchors.bottomMargin: 0
|
||||
StickerList {
|
||||
id: stickerGridInPopup
|
||||
model: stickers
|
||||
height: 350
|
||||
}
|
||||
}
|
||||
StickerPackPurchaseModal {
|
||||
id: stickerPackPurchaseModal
|
||||
stickerPackId: packId
|
||||
packPrice: price
|
||||
width: stickerPackDetailsPopup.width
|
||||
height: stickerPackDetailsPopup.height
|
||||
showBackBtn: stickerPackDetailsPopup.opened
|
||||
}
|
||||
StickerPackDetails {
|
||||
id: stickerPackDetails
|
||||
height: 64 - (Style.current.smallPadding * 2)
|
||||
width: parent.width - (Style.current.padding * 2)
|
||||
anchors.top: imgPreview.bottom
|
||||
anchors.topMargin: Style.current.smallPadding
|
||||
anchors.bottomMargin: Style.current.smallPadding
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
packThumb: thumbnail
|
||||
packName: name
|
||||
packAuthor: author
|
||||
|
||||
StickerButton {
|
||||
anchors.right: parent.right
|
||||
packPrice: price
|
||||
width: 75 // only needed for Qt Creator
|
||||
isInstalled: installed
|
||||
isBought: bought
|
||||
isPending: pending
|
||||
onInstallClicked: root.installClicked(stickers, packId, index)
|
||||
onUninstallClicked: root.uninstallClicked(packId)
|
||||
onCancelClicked: root.cancelClicked(packId)
|
||||
onUpdateClicked: root.updateClicked(packId)
|
||||
onBuyClicked: {
|
||||
stickerPackPurchaseModal.open()
|
||||
root.buyClicked(packId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: footer
|
||||
height: 44 - Style.current.padding
|
||||
anchors.top: availableStickerPacks.bottom
|
||||
|
||||
RoundedIcon {
|
||||
id: btnBack
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Style.current.padding / 2
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding / 2
|
||||
width: 28
|
||||
height: 28
|
||||
iconWidth: 17.5
|
||||
iconHeight: 13.5
|
||||
iconColor: Style.current.pillButtonTextColor
|
||||
source: "../../../img/arrowUp.svg"
|
||||
rotation: 270
|
||||
onClicked: {
|
||||
root.backClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*##^##
|
||||
Designer {
|
||||
D{i:0;height:440;width:360}
|
||||
}
|
||||
##^##*/
|
|
@ -1,47 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.3
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
default property alias content: rest.children
|
||||
property string packThumb: "QmfZrHmLR5VvkXSDbArDR3TX6j4FgpDcrvNz2fHSJk1VvG"
|
||||
property string packName: "Status Cat"
|
||||
property string packAuthor: "cryptoworld1373"
|
||||
property int packNameFontSize: 15
|
||||
property int spacing: Style.current.padding
|
||||
|
||||
RoundedImage {
|
||||
id: imgThumb
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 40
|
||||
height: 40
|
||||
source: "https://ipfs.infura.io/ipfs/" + packThumb
|
||||
}
|
||||
Column {
|
||||
anchors.left: imgThumb.right
|
||||
anchors.leftMargin: root.spacing
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Text {
|
||||
id: txtPackName
|
||||
text: packName
|
||||
color: Style.current.textColor
|
||||
font.family: Style.current.fontBold.name
|
||||
font.weight: Font.Bold
|
||||
font.pixelSize: packNameFontSize
|
||||
}
|
||||
Text {
|
||||
color: Style.current.darkGrey
|
||||
text: packAuthor
|
||||
font.family: Style.current.fontRegular.name
|
||||
font.pixelSize: 15
|
||||
}
|
||||
}
|
||||
Item {
|
||||
anchors.right: parent.right
|
||||
id: rest
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property bool selected: false
|
||||
property bool useIconInsteadOfImage: false
|
||||
property url source: "../../../img/history_icon.svg"
|
||||
signal clicked
|
||||
height: 24
|
||||
width: 24
|
||||
|
||||
RoundedImage {
|
||||
visible: !useIconInsteadOfImage
|
||||
id: iconImage
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
source: root.source
|
||||
onClicked: {
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
RoundedIcon {
|
||||
id: iconIcon
|
||||
visible: useIconInsteadOfImage
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
iconWidth: 6
|
||||
color: Style.current.darkGrey
|
||||
source: root.source
|
||||
onClicked: {
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
id: packIndicator
|
||||
visible: root.selected
|
||||
border.color: Style.current.blue
|
||||
border.width: 1
|
||||
height: 2
|
||||
width: 16
|
||||
x: 4
|
||||
y: root.y + root.height + 6
|
||||
}
|
||||
}
|
|
@ -1,266 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import QtQuick.Dialogs 1.3
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
import "../../../../shared/status"
|
||||
|
||||
ModalPopup {
|
||||
id: root
|
||||
readonly property var asset: JSON.parse(walletModel.getStatusToken())
|
||||
property int stickerPackId: -1
|
||||
property string packPrice
|
||||
property bool showBackBtn: false
|
||||
//% "Authorize %1 %2"
|
||||
title: qsTrId("authorize--1--2").arg(Utils.stripTrailingZeros(packPrice)).arg(asset.symbol)
|
||||
|
||||
property MessageDialog sendingError: MessageDialog {
|
||||
id: sendingError
|
||||
//% "Error sending the transaction"
|
||||
title: qsTrId("error-sending-the-transaction")
|
||||
icon: StandardIcon.Critical
|
||||
standardButtons: StandardButton.Ok
|
||||
}
|
||||
|
||||
onClosed: {
|
||||
stack.reset()
|
||||
}
|
||||
|
||||
function sendTransaction() {
|
||||
let responseStr = chatsModel.buyStickerPack(root.stickerPackId,
|
||||
selectFromAccount.selectedAccount.address,
|
||||
root.packPrice,
|
||||
gasSelector.selectedGasLimit,
|
||||
gasSelector.selectedGasPrice,
|
||||
transactionSigner.enteredPassword)
|
||||
let response = JSON.parse(responseStr)
|
||||
|
||||
if (!response.success) {
|
||||
if (response.result.includes("could not decrypt key with given password")){
|
||||
//% "Wrong password"
|
||||
transactionSigner.validationError = qsTrId("wrong-password")
|
||||
return
|
||||
}
|
||||
sendingError.text = response.result
|
||||
return sendingError.open()
|
||||
}
|
||||
root.close()
|
||||
}
|
||||
|
||||
TransactionStackView {
|
||||
id: stack
|
||||
height: parent.height
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.rightMargin: Style.current.padding
|
||||
onGroupActivated: {
|
||||
root.title = group.headerText
|
||||
btnNext.text = group.footerText
|
||||
}
|
||||
TransactionFormGroup {
|
||||
id: group1
|
||||
//% "Authorize %1 %2"
|
||||
headerText: qsTrId("authorize--1--2").arg(Utils.stripTrailingZeros(root.packPrice)).arg(root.asset.symbol)
|
||||
//% "Continue"
|
||||
footerText: qsTrId("continue")
|
||||
|
||||
StackView.onActivated: {
|
||||
btnBack.visible = root.showBackBtn
|
||||
}
|
||||
|
||||
AccountSelector {
|
||||
id: selectFromAccount
|
||||
accounts: walletModel.accounts
|
||||
selectedAccount: walletModel.currentAccount
|
||||
currency: walletModel.defaultCurrency
|
||||
width: stack.width
|
||||
//% "Choose account"
|
||||
label: qsTrId("choose-account")
|
||||
showBalanceForAssetSymbol: root.asset.symbol
|
||||
minRequiredAssetBalance: root.packPrice
|
||||
reset: function() {
|
||||
accounts = Qt.binding(function() { return walletModel.accounts })
|
||||
selectedAccount = Qt.binding(function() { return walletModel.currentAccount })
|
||||
showBalanceForAssetSymbol = Qt.binding(function() { return root.asset.symbol })
|
||||
minRequiredAssetBalance = Qt.binding(function() { return root.packPrice })
|
||||
}
|
||||
onSelectedAccountChanged: gasSelector.estimateGas()
|
||||
}
|
||||
RecipientSelector {
|
||||
id: selectRecipient
|
||||
visible: false
|
||||
accounts: walletModel.accounts
|
||||
contacts: profileModel.addedContacts
|
||||
selectedRecipient: { "address": utilsModel.stickerMarketAddress, "type": RecipientSelector.Type.Address }
|
||||
readOnly: true
|
||||
onSelectedRecipientChanged: gasSelector.estimateGas()
|
||||
}
|
||||
GasSelector {
|
||||
id: gasSelector
|
||||
visible: false
|
||||
slowestGasPrice: parseFloat(walletModel.safeLowGasPrice)
|
||||
fastestGasPrice: parseFloat(walletModel.fastestGasPrice)
|
||||
getGasEthValue: walletModel.getGasEthValue
|
||||
getFiatValue: walletModel.getFiatValue
|
||||
defaultCurrency: walletModel.defaultCurrency
|
||||
reset: function() {
|
||||
slowestGasPrice = Qt.binding(function(){ return parseFloat(walletModel.safeLowGasPrice) })
|
||||
fastestGasPrice = Qt.binding(function(){ return parseFloat(walletModel.fastestGasPrice) })
|
||||
}
|
||||
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
|
||||
if (!(root.stickerPackId > -1 && selectFromAccount.selectedAccount && root.packPrice && parseFloat(root.packPrice) > 0)) {
|
||||
selectedGasLimit = 325000
|
||||
return
|
||||
}
|
||||
selectedGasLimit = chatsModel.buyPackGasEstimate(root.stickerPackId, selectFromAccount.selectedAccount.address, root.packPrice)
|
||||
})
|
||||
}
|
||||
GasValidator {
|
||||
id: gasValidator
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 8
|
||||
selectedAccount: selectFromAccount.selectedAccount
|
||||
selectedAsset: root.asset
|
||||
selectedAmount: parseFloat(packPrice)
|
||||
selectedGasEthValue: gasSelector.selectedGasEthValue
|
||||
reset: function() {
|
||||
selectedAccount = Qt.binding(function() { return selectFromAccount.selectedAccount })
|
||||
selectedAsset = Qt.binding(function() { return root.asset })
|
||||
selectedAmount = Qt.binding(function() { return parseFloat(packPrice) })
|
||||
selectedGasEthValue = Qt.binding(function() { return gasSelector.selectedGasEthValue })
|
||||
}
|
||||
}
|
||||
}
|
||||
TransactionFormGroup {
|
||||
id: group3
|
||||
//% "Authorize %1 %2"
|
||||
headerText: qsTrId("authorize--1--2").arg(Utils.stripTrailingZeros(root.packPrice)).arg(root.asset.symbol)
|
||||
//% "Sign with password"
|
||||
footerText: qsTrId("sign-with-password")
|
||||
|
||||
StackView.onActivated: {
|
||||
btnBack.visible = true
|
||||
}
|
||||
|
||||
TransactionPreview {
|
||||
id: pvwTransaction
|
||||
width: stack.width
|
||||
fromAccount: selectFromAccount.selectedAccount
|
||||
gas: {
|
||||
"value": gasSelector.selectedGasEthValue,
|
||||
"symbol": "ETH",
|
||||
"fiatValue": gasSelector.selectedGasFiatValue
|
||||
}
|
||||
toAccount: selectRecipient.selectedRecipient
|
||||
asset: root.asset
|
||||
currency: walletModel.defaultCurrency
|
||||
amount: {
|
||||
const fiatValue = walletModel.getFiatValue(root.packPrice || 0, root.asset.symbol, currency)
|
||||
return { "value": root.packPrice, "fiatValue": fiatValue }
|
||||
}
|
||||
reset: function() {
|
||||
fromAccount = Qt.binding(function() { return selectFromAccount.selectedAccount })
|
||||
toAccount = Qt.binding(function() { return selectRecipient.selectedRecipient })
|
||||
asset = Qt.binding(function() { return root.asset })
|
||||
amount = Qt.binding(function() { return { "value": root.packPrice, "fiatValue": walletModel.getFiatValue(root.packPrice, root.asset.symbol, currency) } })
|
||||
gas = Qt.binding(function() {
|
||||
return {
|
||||
"value": gasSelector.selectedGasEthValue,
|
||||
"symbol": "ETH",
|
||||
"fiatValue": gasSelector.selectedGasFiatValue
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
TransactionFormGroup {
|
||||
id: group4
|
||||
//% "Send %1 %2"
|
||||
headerText: qsTrId("send--1--2").arg(Utils.stripTrailingZeros(root.packPrice)).arg(root.asset.symbol)
|
||||
//% "Sign with password"
|
||||
footerText: qsTrId("sign-with-password")
|
||||
|
||||
TransactionSigner {
|
||||
id: transactionSigner
|
||||
width: stack.width
|
||||
signingPhrase: walletModel.signingPhrase
|
||||
reset: function() {
|
||||
signingPhrase = Qt.binding(function() { return walletModel.signingPhrase })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: Item {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
StyledButton {
|
||||
id: btnBack
|
||||
anchors.left: parent.left
|
||||
//% "Back"
|
||||
label: qsTrId("back")
|
||||
onClicked: {
|
||||
if (stack.isFirstGroup) {
|
||||
return root.close()
|
||||
}
|
||||
stack.back()
|
||||
}
|
||||
}
|
||||
StatusButton {
|
||||
id: btnNext
|
||||
anchors.right: parent.right
|
||||
//% "Next"
|
||||
text: qsTrId("next")
|
||||
enabled: stack.currentGroup.isValid && !stack.currentGroup.isPending
|
||||
onClicked: {
|
||||
const validity = stack.currentGroup.validate()
|
||||
if (validity.isValid && !validity.isPending) {
|
||||
if (stack.isLastGroup) {
|
||||
return root.sendTransaction()
|
||||
}
|
||||
stack.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: chatsModel
|
||||
onTransactionWasSent: {
|
||||
//% "Transaction pending..."
|
||||
toastMessage.title = qsTrId("ens-transaction-pending")
|
||||
toastMessage.source = "../../../img/loading.svg"
|
||||
toastMessage.iconColor = Style.current.primary
|
||||
toastMessage.iconRotates = true
|
||||
toastMessage.link = `${walletModel.etherscanLink}/${txResult}`
|
||||
toastMessage.open()
|
||||
}
|
||||
onTransactionCompleted: {
|
||||
toastMessage.title = !success ?
|
||||
//% "Could not buy Stickerpack"
|
||||
qsTrId("could-not-buy-stickerpack")
|
||||
:
|
||||
//% "Stickerpack bought successfully"
|
||||
qsTrId("stickerpack-bought-successfully");
|
||||
if (success) {
|
||||
toastMessage.source = "../../../img/check-circle.svg"
|
||||
toastMessage.iconColor = Style.current.success
|
||||
} else {
|
||||
toastMessage.source = "../../../img/block-icon.svg"
|
||||
toastMessage.iconColor = Style.current.danger
|
||||
}
|
||||
|
||||
toastMessage.link = `${walletModel.etherscanLink}/${txHash}`
|
||||
toastMessage.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*##^##
|
||||
Designer {
|
||||
D{i:0;autoSize:true;height:480;width:640}
|
||||
}
|
||||
##^##*/
|
||||
|
|
@ -1,259 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
import "../../../../shared/status"
|
||||
import "../ChatColumn/samples"
|
||||
|
||||
Popup {
|
||||
id: root
|
||||
property var recentStickers: StickerData {}
|
||||
property var stickerPackList: StickerPackData {}
|
||||
property int installedPacksCount: chatsModel.numInstalledStickerPacks
|
||||
property bool stickerPacksLoaded: false
|
||||
modal: false
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
||||
background: Rectangle {
|
||||
radius: Style.current.radius
|
||||
color: Style.current.background
|
||||
border.color: Style.current.border
|
||||
layer.enabled: true
|
||||
layer.effect: DropShadow{
|
||||
verticalOffset: 3
|
||||
radius: 8
|
||||
samples: 15
|
||||
fast: true
|
||||
cached: true
|
||||
color: "#22000000"
|
||||
}
|
||||
}
|
||||
onClosed: {
|
||||
stickerMarket.visible = false
|
||||
footerContent.visible = true
|
||||
stickersContainer.visible = true
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
StickerMarket {
|
||||
id: stickerMarket
|
||||
visible: false
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
stickerPacks: stickerPackList
|
||||
onInstallClicked: {
|
||||
chatsModel.installStickerPack(packId)
|
||||
stickerGrid.model = stickers
|
||||
stickerPackListView.itemAt(index).clicked()
|
||||
}
|
||||
onUninstallClicked: {
|
||||
chatsModel.uninstallStickerPack(packId)
|
||||
stickerGrid.model = recentStickers
|
||||
btnHistory.clicked()
|
||||
}
|
||||
onBackClicked: {
|
||||
stickerMarket.visible = false
|
||||
footerContent.visible = true
|
||||
stickersContainer.visible = true
|
||||
}
|
||||
}
|
||||
Item {
|
||||
id: stickersContainer
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 4
|
||||
Layout.rightMargin: 4
|
||||
Layout.topMargin: 4
|
||||
Layout.bottomMargin: 0
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||
Layout.preferredHeight: 400 - 4
|
||||
|
||||
Item {
|
||||
id: noStickerPacks
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
|
||||
Image {
|
||||
id: imgNoStickers
|
||||
visible: lblNoStickersYet.visible || lblNoRecentStickers.visible
|
||||
width: 56
|
||||
height: 56
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 134
|
||||
source: "../../../img/stickers_sad_icon.svg"
|
||||
}
|
||||
|
||||
Item {
|
||||
id: noStickersContainer
|
||||
width: parent.width
|
||||
height: 22
|
||||
anchors.top: imgNoStickers.bottom
|
||||
anchors.topMargin: 8
|
||||
|
||||
StyledText {
|
||||
id: lblNoStickersYet
|
||||
visible: root.installedPacksCount === 0
|
||||
anchors.fill: parent
|
||||
font.pixelSize: 15
|
||||
//% "You don't have any stickers yet"
|
||||
text: qsTrId("you-don't-have-any-stickers-yet")
|
||||
lineHeight: 22
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: lblNoRecentStickers
|
||||
visible: stickerPackListView.selectedPackId === -1 && chatsModel.recentStickers.rowCount() === 0 && !lblNoStickersYet.visible
|
||||
anchors.fill: parent
|
||||
font.pixelSize: 15
|
||||
//% "Recently used stickers will appear here"
|
||||
text: qsTrId("recently-used-stickers")
|
||||
lineHeight: 22
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
StyledButton {
|
||||
visible: lblNoStickersYet.visible
|
||||
//% "Get Stickers"
|
||||
label: qsTrId("get-stickers")
|
||||
anchors.top: noStickersContainer.bottom
|
||||
anchors.topMargin: Style.current.padding
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
onClicked: {
|
||||
stickersContainer.visible = false
|
||||
stickerMarket.visible = true
|
||||
footerContent.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
StickerList {
|
||||
id: stickerGrid
|
||||
model: recentStickers
|
||||
onStickerClicked: {
|
||||
chatsModel.sendSticker(hash, packId)
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
StickerList {
|
||||
id: loadingGrid
|
||||
visible: chatsModel.recentStickers.rowCount() === 0
|
||||
interactive: false
|
||||
model: new Array(20)
|
||||
delegate: Item {
|
||||
width: stickerGrid.cellWidth
|
||||
height: stickerGrid.cellHeight
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 4
|
||||
anchors.leftMargin: 4
|
||||
Rectangle {
|
||||
width: 80
|
||||
height: 80
|
||||
radius: width / 2
|
||||
color: Style.current.backgroundHover
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: footerContent
|
||||
Layout.leftMargin: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 40 - 8 * 2
|
||||
Layout.topMargin: 8
|
||||
Layout.rightMargin: 8
|
||||
Layout.bottomMargin: 8
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||
|
||||
StatusRoundButton {
|
||||
id: btnAddStickerPack
|
||||
size: "medium"
|
||||
icon.name: "plusSign"
|
||||
implicitWidth: 24
|
||||
implicitHeight: 24
|
||||
state: root.stickerPacksLoaded ? "default" : "pending"
|
||||
onClicked: {
|
||||
stickersContainer.visible = false
|
||||
stickerMarket.visible = true
|
||||
footerContent.visible = false
|
||||
}
|
||||
}
|
||||
StickerPackIconWithIndicator {
|
||||
id: btnHistory
|
||||
width: 24
|
||||
height: 24
|
||||
selected: true
|
||||
useIconInsteadOfImage: true
|
||||
source: "../../../img/history_icon.svg"
|
||||
anchors.left: btnAddStickerPack.right
|
||||
anchors.leftMargin: Style.current.padding
|
||||
onClicked: {
|
||||
btnHistory.selected = true
|
||||
stickerPackListView.selectedPackId = -1
|
||||
stickerGrid.model = recentStickers
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Style.current.padding
|
||||
anchors.top: parent.top
|
||||
anchors.left: btnHistory.right
|
||||
anchors.leftMargin: Style.current.padding
|
||||
|
||||
Repeater {
|
||||
id: stickerPackListView
|
||||
property int selectedPackId: -1
|
||||
model: stickerPackList
|
||||
|
||||
delegate: StickerPackIconWithIndicator {
|
||||
id: packIconWithIndicator
|
||||
visible: installed
|
||||
width: 24
|
||||
height: 24
|
||||
selected: stickerPackListView.selectedPackId === packId
|
||||
source: "https://ipfs.infura.io/ipfs/" + thumbnail
|
||||
Layout.preferredHeight: height
|
||||
Layout.preferredWidth: width
|
||||
onClicked: {
|
||||
btnHistory.selected = false
|
||||
stickerPackListView.selectedPackId = packId
|
||||
stickerGrid.model = stickers
|
||||
}
|
||||
}
|
||||
}
|
||||
Repeater {
|
||||
id: loadingStickerPackListView
|
||||
model: new Array(7)
|
||||
|
||||
delegate: Rectangle {
|
||||
width: 24
|
||||
height: 24
|
||||
Layout.preferredHeight: height
|
||||
Layout.preferredWidth: width
|
||||
radius: width / 2
|
||||
color: Style.current.backgroundHover
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: chatsModel
|
||||
onStickerPacksLoaded: {
|
||||
root.stickerPacksLoaded = true
|
||||
stickerPackListView.visible = true
|
||||
loadingGrid.visible = false
|
||||
loadingStickerPackListView.model = []
|
||||
noStickerPacks.visible = installedPacksCount === 0 || chatsModel.recentStickers.rowCount() === 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
<svg width="20" height="20" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 5.25C10.4142 5.25 10.75 5.58579 10.75 6V8.75C10.75 9.02614 10.9739 9.25 11.25 9.25H14C14.4142 9.25 14.75 9.58579 14.75 10C14.75 10.4142 14.4142 10.75 14 10.75H11.25C10.9739 10.75 10.75 10.9739 10.75 11.25V14C10.75 14.4142 10.4142 14.75 10 14.75C9.58579 14.75 9.25 14.4142 9.25 14V11.25C9.25 10.9739 9.02614 10.75 8.75 10.75H6C5.58579 10.75 5.25 10.4142 5.25 10C5.25 9.58579 5.58579 9.25 6 9.25H8.75C9.02614 9.25 9.25 9.02614 9.25 8.75V6C9.25 5.58579 9.58579 5.25 10 5.25Z" fill="#4360DF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 10C0 4.47715 4.47715 0 10 0C15.5228 0 20 4.47715 20 10V16.6667C20 18.5076 18.5076 20 16.6667 20H10C4.47715 20 0 15.5228 0 10ZM18.5 10V16.6667C18.5 17.6792 17.6792 18.5 16.6667 18.5H10C5.30558 18.5 1.5 14.6944 1.5 10C1.5 5.30558 5.30558 1.5 10 1.5C14.6944 1.5 18.5 5.30558 18.5 10Z" fill="#4360DF"/>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 5.25C10.4142 5.25 10.75 5.58579 10.75 6V8.75C10.75 9.02614 10.9739 9.25 11.25 9.25H14C14.4142 9.25 14.75 9.58579 14.75 10C14.75 10.4142 14.4142 10.75 14 10.75H11.25C10.9739 10.75 10.75 10.9739 10.75 11.25V14C10.75 14.4142 10.4142 14.75 10 14.75C9.58579 14.75 9.25 14.4142 9.25 14V11.25C9.25 10.9739 9.02614 10.75 8.75 10.75H6C5.58579 10.75 5.25 10.4142 5.25 10C5.25 9.58579 5.58579 9.25 6 9.25H8.75C9.02614 9.25 9.25 9.02614 9.25 8.75V6C9.25 5.58579 9.58579 5.25 10 5.25Z" fill="#939BA1"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 10C0 4.47715 4.47715 0 10 0C15.5228 0 20 4.47715 20 10V16.6667C20 18.5076 18.5076 20 16.6667 20H10C4.47715 20 0 15.5228 0 10ZM18.5 10V16.6667C18.5 17.6792 17.6792 18.5 16.6667 18.5H10C5.30558 18.5 1.5 14.6944 1.5 10C1.5 5.30558 5.30558 1.5 10 1.5C14.6944 1.5 18.5 5.30558 18.5 10Z" fill="#939BA1"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 957 B After Width: | Height: | Size: 957 B |
|
@ -0,0 +1,5 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="1" y="1" width="22" height="22" rx="11" fill="black"/>
|
||||
<path d="M15.7728 8.22709C15.47 7.9243 14.9791 7.9243 14.6763 8.22709L12.3655 10.538C12.1636 10.7398 11.8364 10.7398 11.6345 10.538L9.32369 8.22709C9.02091 7.9243 8.53001 7.9243 8.22723 8.22709C7.92445 8.52987 7.92445 9.02078 8.22723 9.32357L10.5381 11.6344C10.7399 11.8363 10.7399 12.1636 10.5381 12.3654L8.22708 14.6764C7.92431 14.9792 7.92431 15.4701 8.22708 15.7729C8.52986 16.0757 9.02076 16.0757 9.32354 15.7729L11.6345 13.4619C11.8364 13.26 12.1636 13.26 12.3655 13.4619L14.6765 15.7729C14.9792 16.0757 15.4701 16.0757 15.7729 15.7729C16.0757 15.4701 16.0757 14.9792 15.7729 14.6764L13.4619 12.3654C13.2601 12.1636 13.2601 11.8363 13.4619 11.6344L15.7728 9.32357C16.0755 9.02078 16.0755 8.52987 15.7728 8.22709Z" fill="white"/>
|
||||
<rect x="1" y="1" width="22" height="22" rx="11" stroke="#EEF2F5" stroke-width="2"/>
|
||||
</svg>
|
After Width: | Height: | Size: 987 B |
|
@ -0,0 +1,5 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="1" y="1" width="22" height="22" rx="11" fill="#939BA1"/>
|
||||
<path d="M15.7728 8.22709C15.47 7.9243 14.9791 7.9243 14.6763 8.22709L12.3655 10.538C12.1636 10.7398 11.8364 10.7398 11.6345 10.538L9.32369 8.22709C9.02091 7.9243 8.53001 7.9243 8.22723 8.22709C7.92445 8.52987 7.92445 9.02078 8.22723 9.32357L10.5381 11.6344C10.7399 11.8363 10.7399 12.1636 10.5381 12.3654L8.22708 14.6764C7.92431 14.9792 7.92431 15.4701 8.22708 15.7729C8.52986 16.0757 9.02076 16.0757 9.32354 15.7729L11.6345 13.4619C11.8364 13.26 12.1636 13.26 12.3655 13.4619L14.6765 15.7729C14.9792 16.0757 15.4701 16.0757 15.7729 15.7729C16.0757 15.4701 16.0757 14.9792 15.7729 14.6764L13.4619 12.3654C13.2601 12.1636 13.2601 11.8363 13.4619 11.6344L15.7728 9.32357C16.0755 9.02078 16.0755 8.52987 15.7728 8.22709Z" fill="white"/>
|
||||
<rect x="1" y="1" width="22" height="22" rx="11" stroke="#EEF2F5" stroke-width="2"/>
|
||||
</svg>
|
After Width: | Height: | Size: 989 B |
|
@ -0,0 +1,4 @@
|
|||
<svg width="12" height="20" viewBox="0 0 12 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.7977 15.9103C5.05303 15.9488 5.25 16.1628 5.25 16.421V19C5.25 19.4142 5.58579 19.75 6 19.75C6.41421 19.75 6.75 19.4142 6.75 19V16.4209C6.75 16.1627 6.94696 15.9487 7.20227 15.9102C8.67126 15.6888 10.0094 15.0679 11.1024 14.1617C11.4213 13.8974 11.4192 13.4194 11.1263 13.1265C10.8334 12.8336 10.3609 12.8376 10.0364 13.095C8.92766 13.9746 7.52509 14.5 5.99981 14.5C4.47453 14.5 3.07195 13.9746 1.96317 13.095C1.63867 12.8376 1.16618 12.8336 0.873283 13.1265C0.58039 13.4194 0.578337 13.8974 0.897218 14.1617C1.99034 15.068 3.32857 15.6889 4.7977 15.9103Z" fill="#939BA1"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6 0C3.79086 0 2 1.79086 2 4V8C2 10.2091 3.79086 12 6 12C8.20914 12 10 10.2091 10 8V4C10 1.79086 8.20914 0 6 0ZM8.5 8V4C8.5 2.61929 7.38071 1.5 6 1.5C4.61929 1.5 3.5 2.61929 3.5 4V8C3.5 9.38071 4.61929 10.5 6 10.5C7.38071 10.5 8.5 9.38071 8.5 8Z" fill="#939BA1"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1001 B |
|
@ -2,7 +2,7 @@ pragma Singleton
|
|||
|
||||
import QtQuick 2.13
|
||||
import "./twemoji/twemoji.js" as Twemoji
|
||||
import "../app/AppLayouts/Chat/components/emojiList.js" as EmojiJSON
|
||||
import "../shared/status/emojiList.js" as EmojiJSON
|
||||
|
||||
QtObject {
|
||||
property string base: Qt.resolvedUrl("twemoji/")
|
||||
|
|
|
@ -7,6 +7,7 @@ Theme {
|
|||
property color black: "#000000"
|
||||
property color almostBlack: "#141414"
|
||||
property color grey: "#EEF2F5"
|
||||
property color lightGrey: "#ccd0d4"
|
||||
property color lightBlue: "#ECEFFC"
|
||||
property color cyan: "#00FFFF"
|
||||
property color blue: "#758EF0"
|
||||
|
|
|
@ -6,6 +6,7 @@ Theme {
|
|||
property color white2: "#FCFCFC"
|
||||
property color black: "#000000"
|
||||
property color grey: "#EEF2F5"
|
||||
property color lightGrey: "#ccd0d4"
|
||||
property color lightBlue: "#ECEFFC"
|
||||
property color cyan: "#00FFFF"
|
||||
property color blue: "#4360DF"
|
||||
|
|
|
@ -0,0 +1,691 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtGraphicalEffects 1.12
|
||||
import QtQuick.Layouts 1.13
|
||||
import QtMultimedia 5.13
|
||||
import QtQuick.Dialogs 1.3
|
||||
import "../../imports"
|
||||
import "../../shared"
|
||||
import "../../app/AppLayouts/Chat/ChatColumn/samples"
|
||||
|
||||
import "./emojiList.js" as EmojiJSON
|
||||
|
||||
Rectangle {
|
||||
id: control
|
||||
signal sendTransactionCommandButtonClicked()
|
||||
signal receiveTransactionCommandButtonClicked()
|
||||
signal stickerSelected(string hashId, string packId)
|
||||
|
||||
property bool emojiEvent: false;
|
||||
property bool paste: false;
|
||||
property bool isColonPressed: false;
|
||||
property bool isReply: false
|
||||
property bool isImage: false
|
||||
|
||||
property var recentStickers: StickerData {}
|
||||
property var stickerPackList: StickerPackData {}
|
||||
|
||||
property int extraHeightFactor: calculateExtraHeightFactor()
|
||||
property int messageLimit: 2000
|
||||
property int messageLimitVisible: 200
|
||||
|
||||
property int chatType
|
||||
|
||||
property alias textInput: messageInputField
|
||||
|
||||
height: {
|
||||
if (extendedArea.visible) {
|
||||
return messageInput.height + extendedArea.height + Style.current.bigPadding
|
||||
}
|
||||
if (messageInput.height > messageInput.defaultInputFieldHeight) {
|
||||
if (messageInput.height >= messageInput.maxInputFieldHeight) {
|
||||
return messageInput.maxInputFieldHeight + Style.current.bigPadding
|
||||
}
|
||||
return messageInput.height + Style.current.bigPadding
|
||||
}
|
||||
return 64
|
||||
}
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
color: Style.current.background
|
||||
|
||||
Audio {
|
||||
id: sendMessageSound
|
||||
source: "../../sounds/send_message.wav"
|
||||
volume: appSettings.volume
|
||||
}
|
||||
|
||||
function calculateExtraHeightFactor() {
|
||||
const factor = (messageInputField.length / 500) + 1;
|
||||
return (factor > 5) ? 5 : factor;
|
||||
}
|
||||
|
||||
function insertInTextInput(start, text) {
|
||||
// Repace new lines with entities because `insert` gets rid of them
|
||||
messageInputField.insert(start, text.replace(/\n/g, "<br/>"));
|
||||
}
|
||||
|
||||
function interpretMessage(msg) {
|
||||
if (msg === "/shrug") {
|
||||
return "¯\\\\\\_(ツ)\\_/¯"
|
||||
}
|
||||
if (msg === "/tableflip") {
|
||||
return "(╯°□°)╯︵ ┻━┻"
|
||||
}
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
function onEnter(event){
|
||||
if (event.modifiers === Qt.NoModifier && (event.key === Qt.Key_Enter || event.key === Qt.Key_Return)) {
|
||||
if (emojiSuggestions.visible) {
|
||||
emojiSuggestions.addEmoji();
|
||||
event.accepted = true;
|
||||
return
|
||||
}
|
||||
if (messageInputField.length < messageLimit) {
|
||||
sendMsg(event);
|
||||
return;
|
||||
}
|
||||
if(event) event.accepted = true
|
||||
messageTooLongDialog.open()
|
||||
}
|
||||
|
||||
if ((event.key === Qt.Key_V) && (event.modifiers & Qt.ControlModifier)) {
|
||||
paste = true;
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_Down) {
|
||||
return emojiList.incrementCurrentIndex()
|
||||
}
|
||||
if (event.key === Qt.Key_Up) {
|
||||
return emojiList.decrementCurrentIndex()
|
||||
}
|
||||
|
||||
isColonPressed = (event.key === Qt.Key_Colon) && (event.modifiers & Qt.ShiftModifier);
|
||||
}
|
||||
|
||||
|
||||
function onRelease(event) {
|
||||
// the text doesn't get registered to the textarea fast enough
|
||||
// we can only get it in the `released` event
|
||||
if (paste) {
|
||||
paste = false;
|
||||
interrogateMessage();
|
||||
}
|
||||
|
||||
emojiEvent = emojiHandler(event);
|
||||
if (!emojiEvent) {
|
||||
emojiSuggestions.close()
|
||||
}
|
||||
}
|
||||
|
||||
function interrogateMessage() {
|
||||
const text = chatsModel.plainText(Emoji.deparse(messageInputField.text));
|
||||
var words = text.split(' ');
|
||||
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
var transform = true;
|
||||
if (words[i].charAt(0) === ':') {
|
||||
for (var j = 0; j < words[i].length; j++) {
|
||||
if (Utils.isSpace(words[i].charAt(j)) === true || Utils.isPunct(words[i].charAt(j)) === true) {
|
||||
transform = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (transform) {
|
||||
const codePoint = Emoji.getEmojiUnicode(words[i]);
|
||||
words[i] = words[i].replace(words[i], (codePoint !== undefined) ? Emoji.fromCodePoint(codePoint) : words[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
messageInputField.remove(0, messageInputField.length);
|
||||
insertInTextInput(0, Emoji.parse(words.join(' '), '26x26'));
|
||||
}
|
||||
|
||||
// since emoji length is not 1 we need to match that position that TextArea returns
|
||||
// to the actual position in the string.
|
||||
function extrapolateCursorPosition() {
|
||||
// we need only the message part to be html
|
||||
const text = chatsModel.plainText(Emoji.deparse(messageInputField.text));
|
||||
const plainText = Emoji.parse(text, '26x26');
|
||||
|
||||
var bracketEvent = false;
|
||||
var length = 0;
|
||||
|
||||
for (var i = 0; i < plainText.length;) {
|
||||
if (length >= messageInputField.cursorPosition) break;
|
||||
|
||||
if (!bracketEvent && plainText.charAt(i) !== '<') {
|
||||
i++;
|
||||
length++;
|
||||
} else if (!bracketEvent && plainText.charAt(i) === '<') {
|
||||
bracketEvent = true;
|
||||
i++;
|
||||
} else if (bracketEvent && plainText.charAt(i) !== '>') {
|
||||
i++;
|
||||
} else if (bracketEvent && plainText.charAt(i) === '>') {
|
||||
bracketEvent = false;
|
||||
i++;
|
||||
length++;
|
||||
}
|
||||
}
|
||||
|
||||
let textBeforeCursor = Emoji.deparseFromParse(plainText.substr(0, i));
|
||||
return {
|
||||
cursor: countEmojiLengths(plainText.substr(0, i)) + messageInputField.cursorPosition,
|
||||
data: Emoji.deparseFromParse(textBeforeCursor),
|
||||
};
|
||||
}
|
||||
|
||||
function emojiHandler(event) {
|
||||
let message = extrapolateCursorPosition();
|
||||
pollEmojiEvent(message);
|
||||
|
||||
// state machine to handle different forms of the emoji event state
|
||||
if (!emojiEvent && isColonPressed) {
|
||||
return (message.data.length <= 1 || Utils.isSpace(message.data.charAt(message.cursor - 1))) ? true : false;
|
||||
} else if (emojiEvent && isColonPressed) {
|
||||
const index = message.data.lastIndexOf(':', message.cursor - 2);
|
||||
if (index >= 0 && message.cursor > 0) {
|
||||
const shortname = message.data.substr(index, message.cursor);
|
||||
const codePoint = Emoji.getEmojiUnicode(shortname);
|
||||
if (codePoint !== undefined) {
|
||||
replaceWithEmoji(message, shortname, codePoint);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (emojiEvent && isKeyValid(event.key) && !isColonPressed) {
|
||||
// popup
|
||||
const index2 = message.data.lastIndexOf(':', message.cursor - 1);
|
||||
if (index2 >= 0 && message.cursor > 0) {
|
||||
const emojiPart = message.data.substr(index2, message.cursor);
|
||||
if (emojiPart.length > 2) {
|
||||
const emojis = EmojiJSON.emoji_json.filter(function (emoji) {
|
||||
return emoji.name.includes(emojiPart) ||
|
||||
emoji.shortname.includes(emojiPart) ||
|
||||
emoji.aliases.some(a => a.includes(emojiPart))
|
||||
})
|
||||
|
||||
emojiSuggestions.openPopup(emojis, emojiPart)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else if (emojiEvent && !isKeyValid(event.key) && !isColonPressed) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function countEmojiLengths(value) {
|
||||
const match = Emoji.getEmojis(value);
|
||||
var length = 0;
|
||||
|
||||
if (match && match.length > 0) {
|
||||
for (var i = 0; i < match.length; i++) {
|
||||
length += Emoji.deparseFromParse(match[i]).length;
|
||||
}
|
||||
length = length - match.length;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
function replaceWithEmoji(message, shortname, codePoint) {
|
||||
const encodedCodePoint = Emoji.getEmojiCodepoint(codePoint)
|
||||
const newMessage = message.data
|
||||
.replace(shortname, encodedCodePoint)
|
||||
.replace(/ /g, " ");
|
||||
messageInputField.remove(0, messageInputField.cursorPosition);
|
||||
insertInTextInput(0, Emoji.parse(newMessage, '26x26'));
|
||||
emojiSuggestions.close()
|
||||
emojiEvent = false
|
||||
}
|
||||
|
||||
// check if user has placed cursor near valid emoji colon token
|
||||
function pollEmojiEvent(message) {
|
||||
const index = message.data.lastIndexOf(':', message.cursor);
|
||||
if (index >= 0) {
|
||||
emojiEvent = validSubstr(message.data.substr(index, message.cursor - index));
|
||||
}
|
||||
}
|
||||
|
||||
function validSubstr(substr) {
|
||||
for(var i = 0; i < substr.length; i++) {
|
||||
var c = substr.charAt(i);
|
||||
if (Utils.isSpace(c) === true || Utils.isPunct(c) === true)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function isKeyValid(key) {
|
||||
if (key === Qt.Key_Space || key === Qt.Key_Tab ||
|
||||
(key >= Qt.Key_Exclam && key <= Qt.Key_Slash) ||
|
||||
(key >= Qt.Key_Semicolon && key <= Qt.Key_Question) ||
|
||||
(key >= Qt.Key_BracketLeft && key <= Qt.Key_hyphen))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function sendMsg(event){
|
||||
if(control.isImage){
|
||||
chatsModel.sendImage(imageArea.imageSource);
|
||||
}
|
||||
var msg = chatsModel.plainText(Emoji.deparse(messageInputField.text).trim()).trim()
|
||||
if(msg.length > 0){
|
||||
msg = interpretMessage(msg)
|
||||
chatsModel.sendMessage(msg, control.isReply ? SelectedMessage.messageId : "", Utils.isOnlyEmoji(msg) ? Constants.emojiType : Constants.messageType);
|
||||
messageInputField.text = "";
|
||||
if(event) event.accepted = true
|
||||
sendMessageSound.stop()
|
||||
Qt.callLater(sendMessageSound.play);
|
||||
}
|
||||
control.hideExtendedArea();
|
||||
}
|
||||
|
||||
function hideExtendedArea() {
|
||||
isImage = false;
|
||||
isReply = false;
|
||||
imageArea.imageSource = "";
|
||||
replyArea.userName = ""
|
||||
replyArea.identicon = ""
|
||||
replyArea.message = ""
|
||||
}
|
||||
|
||||
function showImageArea(imagePath) {
|
||||
isImage = true;
|
||||
isReply = false;
|
||||
imageArea.imageSource = imageDialog.fileUrls[0]
|
||||
}
|
||||
|
||||
function showReplyArea(userName, message, identicon) {
|
||||
isReply = true
|
||||
replyArea.userName = userName
|
||||
replyArea.message = message
|
||||
replyArea.identicon = identicon
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: imageDialog
|
||||
//% "Please choose an image"
|
||||
title: qsTrId("please-choose-an-image")
|
||||
folder: shortcuts.pictures
|
||||
nameFilters: [
|
||||
//% "Image files (*.jpg *.jpeg *.png)"
|
||||
qsTrId("image-files----jpg---jpeg---png-")
|
||||
]
|
||||
onAccepted: {
|
||||
imageBtn.highlighted = false
|
||||
control.showImageArea()
|
||||
messageInputField.forceActiveFocus();
|
||||
}
|
||||
onRejected: {
|
||||
imageBtn.highlighted = false
|
||||
}
|
||||
}
|
||||
|
||||
MessageDialog {
|
||||
id: messageTooLongDialog
|
||||
//% "Your message is too long."
|
||||
title: qsTrId("your-message-is-too-long.")
|
||||
icon: StandardIcon.Critical
|
||||
//% "Please make your message shorter. We have set the limit to 2000 characters to be courteous of others."
|
||||
text: qsTrId("please-make-your-message-shorter.-we-have-set-the-limit-to-2000-characters-to-be-courteous-of-others.")
|
||||
standardButtons: StandardButton.Ok
|
||||
}
|
||||
|
||||
Popup {
|
||||
property var emojis
|
||||
property string shortname
|
||||
|
||||
function openPopup(emojisParam, shortnameParam) {
|
||||
emojis = emojisParam
|
||||
shortname = shortnameParam
|
||||
emojiSuggestions.open()
|
||||
}
|
||||
|
||||
function addEmoji(index) {
|
||||
if (index === undefined) {
|
||||
index = emojiList.currentIndex
|
||||
}
|
||||
|
||||
const message = extrapolateCursorPosition();
|
||||
const unicode = emojiSuggestions.emojis[index].unicode_alternates || emojiSuggestions.emojis[index].unicode
|
||||
replaceWithEmoji(message, emojiSuggestions.shortname, unicode)
|
||||
}
|
||||
|
||||
id: emojiSuggestions
|
||||
width: parent.width - Style.current.padding * 2
|
||||
height: Math.min(400, emojiList.contentHeight + Style.current.smallPadding * 2)
|
||||
x : Style.current.padding / 2
|
||||
y: -height - Style.current.smallPadding
|
||||
background: Rectangle {
|
||||
visible: !!emojiSuggestions.emojis && emojiSuggestions.emojis.length > 0
|
||||
color: Style.current.secondaryBackground
|
||||
border.width: 1
|
||||
border.color: Style.current.borderSecondary
|
||||
radius: Style.current.radius
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: emojiList
|
||||
model: emojiSuggestions.emojis || []
|
||||
keyNavigationEnabled: true
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
|
||||
delegate: Rectangle {
|
||||
id: rectangle
|
||||
color: emojiList.currentIndex === index ? Style.current.inputBorderFocus : Style.current.transparent
|
||||
border.width: 0
|
||||
width: parent.width
|
||||
height: 42
|
||||
radius: Style.current.radius
|
||||
|
||||
SVGImage {
|
||||
id: emojiImage
|
||||
source: `../../imports/twemoji/26x26/${modelData.unicode}.png`
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.smallPadding
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: modelData.shortname
|
||||
color: emojiList.currentIndex === index ? Style.current.currentUserTextColor : Style.current.textColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: emojiImage.right
|
||||
anchors.leftMargin: Style.current.smallPadding
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
emojiList.currentIndex = index
|
||||
}
|
||||
onClicked: {
|
||||
emojiSuggestions.addEmoji(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusChatCommandsPopup {
|
||||
id: chatCommandsPopup
|
||||
x: 8
|
||||
y: -height
|
||||
onSendTransactionCommandButtonClicked: {
|
||||
control.sendTransactionCommandButtonClicked()
|
||||
chatCommandsPopup.close()
|
||||
}
|
||||
onReceiveTransactionCommandButtonClicked: {
|
||||
control.receiveTransactionCommandButtonClicked()
|
||||
chatCommandsPopup.close()
|
||||
}
|
||||
onClosed: {
|
||||
chatCommandsBtn.highlighted = false
|
||||
}
|
||||
}
|
||||
|
||||
StatusEmojiPopup {
|
||||
id: emojiPopup
|
||||
width: 360
|
||||
height: 440
|
||||
x: parent.width - width - Style.current.halfPadding
|
||||
y: -height
|
||||
emojiSelected: function (text, atCursor) {
|
||||
insertInTextInput(atCursor ? messageInputField.cursorPosition : messageInputField.length, text)
|
||||
emojiBtn.highlighted = false
|
||||
messageInputField.forceActiveFocus();
|
||||
}
|
||||
onClosed: {
|
||||
emojiBtn.highlighted = false
|
||||
}
|
||||
}
|
||||
|
||||
StatusStickersPopup {
|
||||
id: stickersPopup
|
||||
width: 360
|
||||
height: 440
|
||||
x: parent.width - width - Style.current.halfPadding
|
||||
y: -height
|
||||
recentStickers: control.recentStickers
|
||||
stickerPackList: control.stickerPackList
|
||||
onStickerSelected: {
|
||||
control.stickerSelected(hashId, packId)
|
||||
messageInputField.forceActiveFocus();
|
||||
stickersPopup.close()
|
||||
}
|
||||
onClosed: {
|
||||
stickersBtn.highlighted = false
|
||||
}
|
||||
}
|
||||
|
||||
StatusIconButton {
|
||||
id: chatCommandsBtn
|
||||
icon.name: "chat-commands"
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 4
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 16
|
||||
visible: control.chatType === Constants.chatTypeOneToOne
|
||||
onClicked: {
|
||||
highlighted = true
|
||||
chatCommandsPopup.open()
|
||||
}
|
||||
}
|
||||
|
||||
StatusIconButton {
|
||||
id: imageBtn
|
||||
icon.name: "images_icon"
|
||||
icon.height: 18
|
||||
icon.width: 20
|
||||
anchors.left: chatCommandsBtn.visible ? chatCommandsBtn.right : parent.left
|
||||
anchors.leftMargin: chatCommandsBtn.visible ? 2 : 4
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 16
|
||||
visible: control.chatType !== Constants.chatTypePublic
|
||||
|
||||
onClicked: {
|
||||
highlighted = true
|
||||
imageDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: extendedArea
|
||||
visible: isImage || isReply
|
||||
height: {
|
||||
if (visible) {
|
||||
if (isImage) {
|
||||
return imageArea.height
|
||||
}
|
||||
|
||||
if (isReply) {
|
||||
return replyArea.height + replyArea.anchors.topMargin
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
anchors.left: messageInput.left
|
||||
anchors.right: messageInput.right
|
||||
anchors.bottom: messageInput.top
|
||||
color: Style.current.inputBackground
|
||||
radius: 16
|
||||
|
||||
Rectangle {
|
||||
height: 16
|
||||
color: parent.color
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
|
||||
StatusChatInputImageArea {
|
||||
id: imageArea
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
visible: isImage
|
||||
onImageRemoved: {
|
||||
isImage = false
|
||||
}
|
||||
}
|
||||
|
||||
StatusChatInputReplyArea {
|
||||
id: replyArea
|
||||
visible: isReply
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 2
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 2
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 2
|
||||
onCloseButtonClicked: {
|
||||
isReply = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: messageInput
|
||||
property int maxInputFieldHeight: 112
|
||||
property int defaultInputFieldHeight: 40
|
||||
anchors.left: imageBtn.visible ? imageBtn.right : parent.left
|
||||
anchors.leftMargin: imageBtn.visible ? 5 : Style.current.smallPadding
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 12
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.smallPadding
|
||||
height: scrollView.height
|
||||
color: Style.current.inputBackground
|
||||
radius: height > defaultInputFieldHeight || extendedArea.visible ? 16 : 32
|
||||
|
||||
Rectangle {
|
||||
color: parent.color
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
height: 18
|
||||
visible: extendedArea.visible
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: scrollView
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.smallPadding
|
||||
anchors.right: actions.left
|
||||
anchors.rightMargin: 0
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
height: {
|
||||
if (messageInputField.height <= messageInput.defaultInputFieldHeight) {
|
||||
return messageInput.defaultInputFieldHeight
|
||||
}
|
||||
if (messageInputField.height >= messageInput.maxInputFieldHeight) {
|
||||
return messageInput.maxInputFieldHeight
|
||||
}
|
||||
return messageInputField.height
|
||||
}
|
||||
|
||||
TextArea {
|
||||
id: messageInputField
|
||||
textFormat: Text.RichText
|
||||
verticalAlignment: TextEdit.AlignVCenter
|
||||
font.pixelSize: 15
|
||||
font.family: Style.current.fontRegular.name
|
||||
wrapMode: TextArea.Wrap
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
placeholderText: qsTr("Type a message")
|
||||
placeholderTextColor: Style.current.secondaryText
|
||||
selectByMouse: true
|
||||
color: Style.current.textColor
|
||||
topPadding: Style.current.smallPadding
|
||||
bottomPadding: 12
|
||||
Keys.onPressed: onEnter(event)
|
||||
Keys.onReleased: onRelease(event) // gives much more up to date cursorPosition
|
||||
leftPadding: 0
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: parent.color
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
height: parent.height / 2
|
||||
width: 32
|
||||
radius: Style.current.radius
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: messageLengthLimit
|
||||
property int remainingChars: messageLimit - messageInputField.length
|
||||
text: remainingChars.toString()
|
||||
visible: remainingChars <= control.messageLimitVisible
|
||||
color: (remainingChars <= 0) ? Style.current.danger : Style.current.textColor
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: actions.top
|
||||
anchors.rightMargin: Style.current.radius
|
||||
leftPadding: Style.current.halfPadding
|
||||
rightPadding: Style.current.halfPadding
|
||||
}
|
||||
|
||||
Item {
|
||||
id: actions
|
||||
width: childrenRect.width
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 4
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.radius
|
||||
height: emojiBtn.height
|
||||
|
||||
StatusIconButton {
|
||||
id: emojiBtn
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
icon.name: "emojiBtn"
|
||||
type: "secondary"
|
||||
onClicked: {
|
||||
stickersPopup.close()
|
||||
if (emojiPopup.opened) {
|
||||
emojiPopup.close()
|
||||
highlighted = false
|
||||
} else {
|
||||
emojiPopup.open()
|
||||
highlighted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusIconButton {
|
||||
id: stickersBtn
|
||||
anchors.left: emojiBtn.right
|
||||
anchors.leftMargin: 2
|
||||
anchors.bottom: parent.bottom
|
||||
icon.name: "stickers_icon"
|
||||
type: "secondary"
|
||||
onClicked: {
|
||||
emojiPopup.close()
|
||||
if (stickersPopup.opened) {
|
||||
stickersPopup.close()
|
||||
highlighted = false
|
||||
} else {
|
||||
stickersPopup.open()
|
||||
highlighted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import "../../imports"
|
||||
import "../../shared"
|
||||
|
||||
Rectangle {
|
||||
id: imageArea
|
||||
height: 72
|
||||
|
||||
signal imageRemoved()
|
||||
property url imageSource: ""
|
||||
color: "transparent"
|
||||
|
||||
Image {
|
||||
id: chatImage
|
||||
property bool hovered: false
|
||||
height: 64
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.halfPadding
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Style.current.halfPadding
|
||||
fillMode: Image.PreserveAspectFit
|
||||
mipmap: true
|
||||
smooth: false
|
||||
antialiasing: true
|
||||
source: parent.imageSource
|
||||
MouseArea {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
chatImage.hovered = true
|
||||
}
|
||||
onExited: {
|
||||
chatImage.hovered = false
|
||||
}
|
||||
}
|
||||
|
||||
RoundButton {
|
||||
id: closeBtn
|
||||
implicitWidth: 24
|
||||
implicitHeight: 24
|
||||
padding: 0
|
||||
anchors.top: chatImage.top
|
||||
anchors.topMargin: -5
|
||||
anchors.right: chatImage.right
|
||||
anchors.rightMargin: -Style.current.halfPadding
|
||||
visible: chatImage.hovered || hovered
|
||||
contentItem: SVGImage {
|
||||
source: !closeBtn.hovered ?
|
||||
"../../app/img/close-filled.svg" : "../../app/img/close-filled-hovered.svg"
|
||||
width: closeBtn.width
|
||||
height: closeBtn.height
|
||||
}
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
}
|
||||
onClicked: {
|
||||
imageArea.imageRemoved()
|
||||
chatImage.source = ""
|
||||
}
|
||||
MouseArea {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
onPressed: mouse.accepted = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtGraphicalEffects 1.13
|
||||
import "../../imports"
|
||||
import "../../shared"
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
height: 50
|
||||
color: Style.current.lightGrey
|
||||
radius: 16
|
||||
clip: true
|
||||
|
||||
property string userName: ""
|
||||
property string message : ""
|
||||
property string identicon: ""
|
||||
|
||||
signal closeButtonClicked()
|
||||
|
||||
Rectangle {
|
||||
color: parent.color
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
height: parent.height / 2
|
||||
width: 32
|
||||
radius: Style.current.radius
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: replyToUsername
|
||||
text: "↪ " + userName
|
||||
color: Style.current.black
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Style.current.halfPadding
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.smallPadding
|
||||
font.pixelSize: 13
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: replyText
|
||||
text: Emoji.parse(message, "26x26")
|
||||
anchors.left: replyToUsername.left
|
||||
anchors.top: replyToUsername.bottom
|
||||
anchors.topMargin: 2
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding
|
||||
anchors.bottom: parent.bottom
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: 13
|
||||
font.weight: Font.Normal
|
||||
// Eliding only works for PlainText: https://bugreports.qt.io/browse/QTBUG-16567
|
||||
textFormat: Text.PlainText
|
||||
color: Style.current.black
|
||||
}
|
||||
|
||||
RoundButton {
|
||||
id: closeBtn
|
||||
implicitWidth: 20
|
||||
implicitHeight: 20
|
||||
radius: 10
|
||||
padding: 0
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 4
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 4
|
||||
contentItem: SVGImage {
|
||||
id: iconImg
|
||||
source: "../../app/img/close.svg"
|
||||
width: closeBtn.width
|
||||
height: closeBtn.height
|
||||
|
||||
ColorOverlay {
|
||||
anchors.fill: iconImg
|
||||
source: iconImg
|
||||
color: Style.current.black
|
||||
antialiasing: true
|
||||
}
|
||||
}
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
width: closeBtn.width
|
||||
height: closeBtn.height
|
||||
radius: closeBtn.radius
|
||||
}
|
||||
onClicked: {
|
||||
root.userName = ""
|
||||
root.message = ""
|
||||
root.identicon = ""
|
||||
root.closeButtonClicked()
|
||||
}
|
||||
MouseArea {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
onPressed: mouse.accepted = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -27,7 +27,7 @@ RoundButton {
|
|||
if (type === "secondary") {
|
||||
return "transparent"
|
||||
}
|
||||
return hovered || highlighted ? Style.current.lightBlue : "transparent"
|
||||
return hovered || highlighted ? Style.current.secondaryBackground : "transparent"
|
||||
}
|
||||
radius: control.radius
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ StatusChatCommandButton 1.0 StatusChatCommandButton.qml
|
|||
StatusChatCommandPopup 1.0 StatusChatCommandPopup.qml
|
||||
StatusChatInfo 1.0 StatusChatInfo.qml
|
||||
StatusChatInfoButton 1.0 StatusChatInfoButton.qml
|
||||
StatusChatInput 1.0 StatusChatInput.qml
|
||||
StatusEmojiCategoryButton 1.0 StatusEmojiCategoryButton.qml
|
||||
StatusEmojiPopup 1.0 StatusEmojiPopup.qml
|
||||
StatusEmojiSection 1.0 StatusEmojiSection.qml
|
||||
|
@ -19,5 +20,5 @@ StatusStickerList 1.0 StatusStickerList.qml
|
|||
StatusStickerMarket 1.0 StatusStickerMarket.qml
|
||||
StatusStickerPackDetails 1.0 StatusStickerPackDetails.qml
|
||||
StatusStickerPackPurchaseModal 1.0 StatusStickerPackPurchaseModal.qml
|
||||
StatusStickersPopup 1.0 StatusStickerPopup.qml
|
||||
StatusStickersPopup 1.0 StatusStickersPopup.qml
|
||||
StatusToolTip 1.0 StatusToolTip.qml
|
||||
|
|
Loading…
Reference in New Issue