parent
dc14bbe9ec
commit
12a7d7c067
|
@ -2,9 +2,11 @@ import QtQuick 2.13
|
||||||
import QtQuick.Controls 2.13
|
import QtQuick.Controls 2.13
|
||||||
import QtQuick.Layouts 1.13
|
import QtQuick.Layouts 1.13
|
||||||
import "../../../shared"
|
import "../../../shared"
|
||||||
|
import "../../../shared/status"
|
||||||
import "../../../imports"
|
import "../../../imports"
|
||||||
import "./components"
|
import "./components"
|
||||||
import "./ChatColumn"
|
import "./ChatColumn"
|
||||||
|
import "./ChatColumn/ChatComponents"
|
||||||
import "./data"
|
import "./data"
|
||||||
|
|
||||||
StackLayout {
|
StackLayout {
|
||||||
|
@ -27,7 +29,6 @@ StackLayout {
|
||||||
chatInput.textInput.forceActiveFocus(Qt.MouseFocusReason)
|
chatInput.textInput.forceActiveFocus(Qt.MouseFocusReason)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.minimumWidth: 300
|
Layout.minimumWidth: 300
|
||||||
|
@ -37,21 +38,33 @@ StackLayout {
|
||||||
function showReplyArea() {
|
function showReplyArea() {
|
||||||
isReply = true;
|
isReply = true;
|
||||||
isImage = false;
|
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) {
|
function requestAddressForTransaction(address, amount, tokenAddress, tokenDecimals = 18) {
|
||||||
isImage = true;
|
amount = walletModel.eth2Wei(amount.toString(), tokenDecimals)
|
||||||
isReply = false;
|
chatsModel.requestAddressForTransaction(chatsModel.activeChannel.id,
|
||||||
sendImageArea.image = imagePath[0];
|
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 {
|
ColumnLayout {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
@ -164,14 +177,12 @@ StackLayout {
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: inputArea
|
id: inputArea
|
||||||
color: Style.current.background
|
|
||||||
border.width: 1
|
|
||||||
border.color: Style.current.border
|
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredWidth: parent.width
|
Layout.preferredWidth: parent.width
|
||||||
height: (!isExtendedInput ? 70 : 140) * chatInput.extraHeightFactor
|
height: chatInput.height
|
||||||
Layout.preferredHeight: height
|
Layout.preferredHeight: height
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
SuggestionBox {
|
SuggestionBox {
|
||||||
id: suggestionsBox
|
id: suggestionsBox
|
||||||
|
@ -209,16 +220,6 @@ StackLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ReplyArea {
|
|
||||||
id: replyAreaContainer
|
|
||||||
visible: isReply
|
|
||||||
}
|
|
||||||
|
|
||||||
SendImageArea {
|
|
||||||
id: sendImageArea
|
|
||||||
visible: isImage
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
active: chatsModel.loadingMessages
|
active: chatsModel.loadingMessages
|
||||||
sourceComponent: loadingIndicator
|
sourceComponent: loadingIndicator
|
||||||
|
@ -247,31 +248,56 @@ StackLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatInput {
|
StatusChatInput {
|
||||||
id: chatInput
|
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
|
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 {}
|
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">
|
<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="#4360DF"/>
|
<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="#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="#939BA1"/>
|
||||||
</svg>
|
</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 QtQuick 2.13
|
||||||
import "./twemoji/twemoji.js" as Twemoji
|
import "./twemoji/twemoji.js" as Twemoji
|
||||||
import "../app/AppLayouts/Chat/components/emojiList.js" as EmojiJSON
|
import "../shared/status/emojiList.js" as EmojiJSON
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
property string base: Qt.resolvedUrl("twemoji/")
|
property string base: Qt.resolvedUrl("twemoji/")
|
||||||
|
|
|
@ -7,6 +7,7 @@ Theme {
|
||||||
property color black: "#000000"
|
property color black: "#000000"
|
||||||
property color almostBlack: "#141414"
|
property color almostBlack: "#141414"
|
||||||
property color grey: "#EEF2F5"
|
property color grey: "#EEF2F5"
|
||||||
|
property color lightGrey: "#ccd0d4"
|
||||||
property color lightBlue: "#ECEFFC"
|
property color lightBlue: "#ECEFFC"
|
||||||
property color cyan: "#00FFFF"
|
property color cyan: "#00FFFF"
|
||||||
property color blue: "#758EF0"
|
property color blue: "#758EF0"
|
||||||
|
|
|
@ -6,6 +6,7 @@ Theme {
|
||||||
property color white2: "#FCFCFC"
|
property color white2: "#FCFCFC"
|
||||||
property color black: "#000000"
|
property color black: "#000000"
|
||||||
property color grey: "#EEF2F5"
|
property color grey: "#EEF2F5"
|
||||||
|
property color lightGrey: "#ccd0d4"
|
||||||
property color lightBlue: "#ECEFFC"
|
property color lightBlue: "#ECEFFC"
|
||||||
property color cyan: "#00FFFF"
|
property color cyan: "#00FFFF"
|
||||||
property color blue: "#4360DF"
|
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") {
|
if (type === "secondary") {
|
||||||
return "transparent"
|
return "transparent"
|
||||||
}
|
}
|
||||||
return hovered || highlighted ? Style.current.lightBlue : "transparent"
|
return hovered || highlighted ? Style.current.secondaryBackground : "transparent"
|
||||||
}
|
}
|
||||||
radius: control.radius
|
radius: control.radius
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ StatusChatCommandButton 1.0 StatusChatCommandButton.qml
|
||||||
StatusChatCommandPopup 1.0 StatusChatCommandPopup.qml
|
StatusChatCommandPopup 1.0 StatusChatCommandPopup.qml
|
||||||
StatusChatInfo 1.0 StatusChatInfo.qml
|
StatusChatInfo 1.0 StatusChatInfo.qml
|
||||||
StatusChatInfoButton 1.0 StatusChatInfoButton.qml
|
StatusChatInfoButton 1.0 StatusChatInfoButton.qml
|
||||||
|
StatusChatInput 1.0 StatusChatInput.qml
|
||||||
StatusEmojiCategoryButton 1.0 StatusEmojiCategoryButton.qml
|
StatusEmojiCategoryButton 1.0 StatusEmojiCategoryButton.qml
|
||||||
StatusEmojiPopup 1.0 StatusEmojiPopup.qml
|
StatusEmojiPopup 1.0 StatusEmojiPopup.qml
|
||||||
StatusEmojiSection 1.0 StatusEmojiSection.qml
|
StatusEmojiSection 1.0 StatusEmojiSection.qml
|
||||||
|
@ -19,5 +20,5 @@ StatusStickerList 1.0 StatusStickerList.qml
|
||||||
StatusStickerMarket 1.0 StatusStickerMarket.qml
|
StatusStickerMarket 1.0 StatusStickerMarket.qml
|
||||||
StatusStickerPackDetails 1.0 StatusStickerPackDetails.qml
|
StatusStickerPackDetails 1.0 StatusStickerPackDetails.qml
|
||||||
StatusStickerPackPurchaseModal 1.0 StatusStickerPackPurchaseModal.qml
|
StatusStickerPackPurchaseModal 1.0 StatusStickerPackPurchaseModal.qml
|
||||||
StatusStickersPopup 1.0 StatusStickerPopup.qml
|
StatusStickersPopup 1.0 StatusStickersPopup.qml
|
||||||
StatusToolTip 1.0 StatusToolTip.qml
|
StatusToolTip 1.0 StatusToolTip.qml
|
||||||
|
|
Loading…
Reference in New Issue