feat: add sticker popup

Add sticker popup
Add send sticker message
Add ability to select sticker pack and show stickers for pack with scroll

1. Sticker history
2. Install sticker packs
3. Sticker market

1. Sticker packs are installed on app start up until installation of sticker pack functionality is added
2. Optimisations such as preloading images to be done so that sticker images are not downloaded each time.
This commit is contained in:
emizzle 2020-06-24 13:23:49 +10:00 committed by Iuri Matias
parent 6a49070431
commit 847eb2623f
39 changed files with 942 additions and 285 deletions

View File

@ -3,9 +3,12 @@ import ../../status/chat as chat_model
import ../../status/mailservers as mailserver_model
import ../../signals/types
import ../../status/libstatus/types as status_types
import ../../status/libstatus/wallet as status_wallet
import ../../status/[chat, contacts, status]
import view, views/channels_list
from eth/common/utils import parseAddress
logScope:
topics = "chat-controller"
@ -70,6 +73,12 @@ proc init*(self: ChatController) =
self.status.mailservers.init()
self.status.chat.init()
let currAcct = status_wallet.getWalletAccounts()[0] # TODO: make generic
let currAddr = parseAddress(currAcct.address)
let installedStickers = self.status.chat.getInstalledStickers(currAddr)
for stickerPack in installedStickers:
self.view.addStickerPackToList(stickerPack)
proc handleMessage(self: ChatController, data: MessageSignal) =
self.status.chat.update(data.chats, data.messages)

View File

@ -4,9 +4,10 @@ import ../../status/status
import ../../status/chat as status_chat
import ../../status/contacts as status_contacts
import ../../status/chat/[chat, message]
import ../../status/libstatus/types
import ../../status/profile/profile
import views/channels_list, views/message_list, views/chat_item
import views/channels_list, views/message_list, views/chat_item, views/sticker_pack_list, views/sticker_list
logScope:
topics = "chats-view"
@ -19,6 +20,10 @@ QtObject:
callResult: string
messageList: Table[string, ChatMessageList]
activeChannel*: ChatItemView
activeStickerPackId*: int
stickerPacks*: StickerPackList
stickers*: Table[int, StickerList]
emptyStickerList: StickerList
proc setup(self: ChatsView) = self.QAbstractListModel.setup
@ -28,6 +33,7 @@ QtObject:
for msg in self.messageList.values:
msg.delete
self.messageList = initTable[string, ChatMessageList]()
self.stickers = initTable[int, StickerList]()
self.QAbstractListModel.delete
proc newChatsView*(status: Status): ChatsView =
@ -35,9 +41,23 @@ QtObject:
result.status = status
result.chats = newChannelsList()
result.activeChannel = newChatItemView(status)
result.activeStickerPackId = -1
result.messageList = initTable[string, ChatMessageList]()
result.stickerPacks = newStickerPackList()
result.stickers = initTable[int, StickerList]()
result.emptyStickerList = newStickerList()
result.setup()
proc addStickerPackToList*(self: ChatsView, stickerPack: StickerPack) =
discard self.stickerPacks.addStickerPackToList(stickerPack)
self.stickers[stickerPack.id] = newStickerList(stickerPack.stickers)
proc getStickerPackList(self: ChatsView): QVariant {.slot.} =
newQVariant(self.stickerPacks)
QtProperty[QVariant] stickerPacks:
read = getStickerPackList
proc getChatsList(self: ChatsView): QVariant {.slot.} =
newQVariant(self.chats)
@ -67,6 +87,24 @@ QtObject:
read = getActiveChannelIdx
write = setActiveChannelByIndex
notify = activeChannelChanged
proc activeStickerPackChanged*(self: ChatsView) {.signal.}
proc setActiveStickerPackById*(self: ChatsView, id: int) {.slot.} =
if self.activeStickerPackId == id:
return
self.activeStickerPackId = id
self.activeStickerPackChanged()
proc getStickerList*(self: ChatsView): QVariant {.slot.} =
if self.activeStickerPackId <= 0:
return newQVariant(self.emptyStickerList)
result = newQVariant(self.stickers[self.activeStickerPackId])
QtProperty[QVariant] stickers:
read = getStickerList
notify = activeStickerPackChanged
proc setActiveChannel*(self: ChatsView, channel: string) =
if(channel == ""): return
@ -120,6 +158,9 @@ QtObject:
proc sendMessage*(self: ChatsView, message: string) {.slot.} =
discard self.status.chat.sendMessage(self.activeChannel.id, message)
proc sendSticker*(self: ChatsView, hash: string, pack: int) {.slot.} =
self.status.chat.sendSticker(self.activeChannel.id, hash, pack)
proc joinChat*(self: ChatsView, channel: string, chatTypeInt: int): int {.slot.} =
self.status.chat.join(channel, ChatType(chatTypeInt))

View File

@ -0,0 +1,43 @@
import NimQml, Tables
import ../../../status/chat/stickers
import ../../../status/libstatus/types
type
StickerRoles {.pure.} = enum
Url = UserRole + 1
Hash = UserRole + 2
QtObject:
type
StickerList* = ref object of QAbstractListModel
stickers*: seq[Sticker]
proc setup(self: StickerList) = self.QAbstractListModel.setup
proc delete(self: StickerList) = self.QAbstractListModel.delete
proc newStickerList*(stickers: seq[Sticker] = @[]): StickerList =
new(result, delete)
result.stickers = stickers
result.setup()
method rowCount(self: StickerList, index: QModelIndex = nil): int = self.stickers.len
method data(self: StickerList, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
if index.row < 0 or index.row >= self.stickers.len:
return
let sticker = self.stickers[index.row]
let stickerRole = role.StickerRoles
case stickerRole:
of StickerRoles.Url: result = newQVariant(decodeContentHash(sticker.hash))
of StickerRoles.Hash: result = newQVariant(sticker.hash)
method roleNames(self: StickerList): Table[int, string] =
{
StickerRoles.Url.int:"url",
StickerRoles.Hash.int:"hash"
}.toTable

View File

@ -0,0 +1,60 @@
import NimQml, Tables
import ../../../status/chat/stickers
import ../../../status/libstatus/types
type
StickerPackRoles {.pure.} = enum
Author = UserRole + 1,
Id = UserRole + 2
Name = UserRole + 3
Price = UserRole + 4
Preview = UserRole + 5
Thumbnail = UserRole + 6
QtObject:
type
StickerPackList* = ref object of QAbstractListModel
packs*: seq[StickerPack]
proc setup(self: StickerPackList) = self.QAbstractListModel.setup
proc delete(self: StickerPackList) = self.QAbstractListModel.delete
proc newStickerPackList*(): StickerPackList =
new(result, delete)
result.packs = @[]
result.setup()
method rowCount(self: StickerPackList, index: QModelIndex = nil): int = self.packs.len
method data(self: StickerPackList, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
if index.row < 0 or index.row >= self.packs.len:
return
let stickerPack = self.packs[index.row]
let stickerPackRole = role.StickerPackRoles
case stickerPackRole:
of StickerPackRoles.Author: result = newQVariant(stickerPack.author)
of StickerPackRoles.Id: result = newQVariant($stickerPack.id)
of StickerPackRoles.Name: result = newQVariant(stickerPack.name)
of StickerPackRoles.Price: result = newQVariant(stickerPack.price)
of StickerPackRoles.Preview: result = newQVariant(decodeContentHash(stickerPack.preview))
of StickerPackRoles.Thumbnail: result = newQVariant(decodeContentHash(stickerPack.thumbnail))
method roleNames(self: StickerPackList): Table[int, string] =
{
StickerPackRoles.Author.int:"author",
StickerPackRoles.Id.int:"id",
StickerPackRoles.Name.int: "name",
StickerPackRoles.Price.int: "price",
StickerPackRoles.Preview.int: "preview",
StickerPackRoles.Thumbnail.int: "thumbnail"
}.toTable
proc addStickerPackToList*(self: StickerPackList, pack: StickerPack): int =
self.beginInsertRows(newQModelIndex(), 0, 0)
self.packs.insert(pack, 0)
self.endInsertRows()
result = 0

View File

@ -31,8 +31,8 @@ proc init*(self: ProfileController, account: Account) =
# Ideally, this module should call getSettings once, and fill the
# profile with all the information comming from the settings.
let response = status_settings.getSettings()
let pubKey = parseJSON($response)["result"]["public-key"].getStr
let mnemonic = parseJSON($response)["result"]["mnemonic"].getStr
let pubKey = response["public-key"].getStr
let mnemonic = response["mnemonic"].getStr
profile.id = pubKey
self.view.setNewProfile(profile)

View File

@ -1,9 +1,12 @@
import eventemitter, json, strutils, sequtils, tables, chronicles
import libstatus/chat as status_chat
import libstatus/stickers as status_stickers
import libstatus/types
import profile/profile
import chat/[chat, message]
import chat/[chat, message], wallet
import ../signals/messages
import ens
import eth/common/eth_types
type
ChatUpdateArgs* = ref object of Args
@ -78,9 +81,21 @@ proc join*(self: ChatModel, chatId: string, chatType: ChatType) =
self.events.emit("channelJoined", ChannelArgs(chat: chat))
proc getInstalledStickers*(self: ChatModel, address: EthAddress): seq[StickerPack] =
# TODO: needs more fleshing out to determine which sticker packs
# we own -- owned sticker packs will simply allowed them to be installed
discard status_stickers.getBalance(address)
result = status_stickers.getInstalledStickers()
proc init*(self: ChatModel) =
let chatList = status_chat.loadChats()
# TODO: Temporarily install sticker packs as a first step. Later, once installation
# of sticker packs is supported, this should be removed, and a default "No
# stickers installed" view should show if no sticker packs are installed.
status_stickers.installStickers()
var filters:seq[JsonNode] = @[]
for chat in chatList:
if self.hasChannel(chat.id): continue
@ -133,6 +148,10 @@ proc sendMessage*(self: ChatModel, chatId: string, msg: string): string =
self.emitUpdate(sentMessage)
sentMessage
proc sendSticker*(self: ChatModel, chatId: string, hash: string, pack: int) =
var response = status_chat.sendStickerMessage(chatId, hash, pack)
self.emitUpdate(response)
proc chatMessages*(self: ChatModel, chatId: string, initialLoad:bool = true) =
if not self.msgCursor.hasKey(chatId):
self.msgCursor[chatId] = "";

View File

@ -78,7 +78,22 @@ proc sendChatMessage*(chatId: string, msg: string): string =
"responseTo": nil,
"ensName": nil,
"sticker": nil,
"contentType": 1
"contentType": ContentType.Message.int
}
])
proc sendStickerMessage*(chatId: string, hash: string, pack: int): string =
callPrivateRPC("sendChatMessage".prefix, %* [
{
"chatId": chatId,
"text": "Update to latest version to see a nice sticker here!",
"responseTo": nil,
"ensName": nil,
"sticker": {
"hash": hash,
"pack": pack
},
"contentType": ContentType.Sticker.int
}
])

View File

@ -1,4 +1,5 @@
import sequtils, strformat, sugar, macros, tables, eth/common/eth_types, stew/byteutils, nimcrypto
import sequtils, strformat, sugar, macros, tables
import eth/common/eth_types, stew/byteutils, nimcrypto
from eth/common/utils import parseAddress
type
@ -18,14 +19,26 @@ type Contract* = ref object
methods*: Table[string, Method]
let CONTRACTS: seq[Contract] = @[
Contract(name: "snt", network: Network.Mainnet, address: parseAddress("0x744d70fdbe2ba4cf95131626614a1763df805b9e")),
Contract(name: "snt", network: Network.Mainnet, address: parseAddress("0x744d70fdbe2ba4cf95131626614a1763df805b9e"),
methods: [
("approveAndCall", Method(signature: "approveAndCall(address,uint256,bytes)"))
].toTable
),
Contract(name: "snt", network: Network.Testnet, address: parseAddress("0xc55cf4b03948d7ebc8b9e8bad92643703811d162")),
Contract(name: "tribute-to-talk", network: Network.Testnet, address: parseAddress("0xC61aa0287247a0398589a66fCD6146EC0F295432")),
Contract(name: "stickers", network: Network.Mainnet, address: parseAddress("0x0577215622f43a39f4bc9640806dfea9b10d2a36")),
Contract(name: "stickers", network: Network.Testnet, address: parseAddress("0x8cc272396be7583c65bee82cd7b743c69a87287d")),
Contract(name: "sticker-market", network: Network.Mainnet, address: parseAddress("0x12824271339304d3a9f7e096e62a2a7e73b4a7e7")),
Contract(name: "sticker-market", network: Network.Mainnet, address: parseAddress("0x12824271339304d3a9f7e096e62a2a7e73b4a7e7"),
methods: [
("buyToken", Method(signature: "buyToken(uint256,address,uint256)"))
].toTable
),
Contract(name: "sticker-market", network: Network.Testnet, address: parseAddress("0x6CC7274aF9cE9572d22DFD8545Fb8c9C9Bcb48AD")),
Contract(name: "sticker-pack", network: Network.Mainnet, address: parseAddress("0x110101156e8F0743948B2A61aFcf3994A8Fb172e")),
Contract(name: "sticker-pack", network: Network.Mainnet, address: parseAddress("0x110101156e8F0743948B2A61aFcf3994A8Fb172e"),
methods: [
("balanceOf", Method(signature: "balanceOf(address)"))
].toTable
),
Contract(name: "sticker-pack", network: Network.Testnet, address: parseAddress("0xf852198d0385c4b871e0b91804ecd47c6ba97351")),
# Strikers seems dead. Their website doesn't work anymore
Contract(name: "strikers", network: Network.Mainnet, address: parseAddress("0xdcaad9fd9a74144d226dbf94ce6162ca9f09ed7e"),

View File

@ -1,14 +1,18 @@
import json
import core
import json
proc saveSettings*(key: string, value: string): string =
callPrivateRPC("settings_saveSetting", %* [
key, $value
])
proc getSettings*(): string =
callPrivateRPC("settings_getSettings")
# TODO: return an Table/Object instead of string
proc getWeb3ClientVersion*(): string =
parseJson(callPrivateRPC("web3_clientVersion"))["result"].getStr
proc getSettings*(): JsonNode =
callPrivateRPC("settings_getSettings").parseJSON()["result"]
# TODO: return an Table/Object instead
proc getSetting*(name: string): string =
let settings: JsonNode = getSettings()
result = settings{name}.getStr

File diff suppressed because one or more lines are too long

View File

@ -63,6 +63,13 @@ type
keyUid*: string
photoPath*: string
type
RpcResponse* = ref object
jsonrpc*: string
result*: string
id*: int
error*: string
proc toAccount*(account: GeneratedAccount): Account =
result = Account(name: account.name, photoPath: account.photoPath, keyUid: account.address)
@ -90,3 +97,18 @@ type
value*: string
fromAddress*: string
to*: string
type
RpcException* = object of Exception
type Sticker* = ref object
hash*: string
type StickerPack* = ref object
author*: string
id*: int
name*: string
price*: int
preview*: string
stickers*: seq[Sticker]
thumbnail*: string

View File

@ -50,7 +50,7 @@ proc sendTransaction*(self: WalletModel, from_value: string, to: string, value:
proc getDefaultCurrency*(self: WalletModel): string =
# TODO: this should come from a model? It is going to be used too in the
# profile section and ideally we should not call the settings more than once
status_settings.getSettings().parseJSON()["result"]["currency"].getStr
status_settings.getSetting("currency")
proc setDefaultCurrency*(self: WalletModel, currency: string) =
discard status_settings.saveSettings("currency", currency)

View File

@ -1,5 +1,6 @@
import strformat, httpclient, json, chronicles, sequtils, strutils, tables
import ../libstatus/[core, contracts]
import ../libstatus/core as status
import ../libstatus/contracts as contracts
import eth/common/eth_types
import account

View File

@ -36,15 +36,24 @@ StackLayout {
}
}
RowLayout {
Rectangle {
id: chatInputContainer
height: 70
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
Layout.fillWidth: true
Layout.bottomMargin: 0
Layout.alignment: Qt.AlignLeft | Qt.AlignBottom
Layout.preferredWidth: parent.width
Layout.preferredHeight: height
transformOrigin: Item.Bottom
clip: true
ChatInput {}
ChatInput {
anchors.fill: parent
anchors.leftMargin: -border.width
border.width: 1
border.color: Theme.grey
}
}
}

View File

@ -0,0 +1,73 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import "../../../../imports"
import "../components"
Rectangle {
border.width: 0
Button {
id: chatSendBtn
visible: txtData.length > 0
width: 30
height: 30
text: ""
anchors.rightMargin: Theme.padding
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
onClicked: {
chatsModel.sendMessage(txtData.text)
txtData.text = ""
}
background: Rectangle {
color: parent.enabled ? Theme.blue : Theme.grey
radius: 50
}
Image {
source: "../../../img/arrowUp.svg"
width: 12
fillMode: Image.PreserveAspectFit
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
}
}
Image {
id: stickersIcon
visible: txtData.length == 0
width: 20
height: 20
anchors.rightMargin: Theme.padding
anchors.right: parent.right
fillMode: Image.PreserveAspectFit
source: "../../../img/stickers_icon" + (stickersPopup.opened ? "_open.svg" : ".svg")
anchors.verticalCenter: parent.verticalCenter
MouseArea {
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
onClicked: {
if (stickersPopup.opened) {
stickersPopup.close()
} else {
stickersPopup.open()
}
}
}
}
StickersPopup {
id: stickersPopup
width: 360
height: 440
x: parent.width - width - 8
y: parent.height - sendBtns.height - height - 8
stickerList: chatsModel.stickers
stickerPackList: chatsModel.stickerPacks
}
}
/*##^##
Designer {
D{i:0;autoSize:true;height:480;width:640}
}
##^##*/

View File

@ -1,67 +1,31 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import "../components"
import "../../../../shared"
import "../../../../imports"
Rectangle {
id: element2
width: 200
height: 70
Layout.fillWidth: true
color: "white"
border.width: 0
visible: chatsModel.activeChannel.chatType != Constants.chatTypePrivateGroupChat || chatsModel.activeChannel.isMember(profileModel.profile.pubKey)
Rectangle {
id: rectangle
color: "#00000000"
border.color: Theme.grey
RowLayout {
spacing: 0
anchors.fill: parent
Button {
id: chatSendBtn
visible: txtData.length > 0
x: 100
width: 30
height: 30
text: ""
anchors.top: parent.top
anchors.topMargin: 20
anchors.right: parent.right
anchors.rightMargin: 16
onClicked: {
chatsModel.sendMessage(txtData.text)
txtData.text = ""
}
background: Rectangle {
color: parent.enabled ? Theme.blue : Theme.grey
radius: 50
}
Image {
source: "../../../img/arrowUp.svg"
width: 12
fillMode: Image.PreserveAspectFit
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
}
}
StyledTextField {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: parent.width - sendBtns.width
id: txtData
text: ""
padding: 0
leftPadding: 12
rightPadding: Theme.padding
font.pixelSize: 14
placeholderText: qsTr("Type a message...")
anchors.right: chatSendBtn.left
anchors.rightMargin: 16
anchors.top: parent.top
anchors.topMargin: 0
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.left: parent.left
leftPadding: 24
selectByMouse: true
Keys.onEnterPressed: {
chatsModel.sendMessage(txtData.text)
@ -75,10 +39,28 @@ Rectangle {
color: "#00000000"
}
}
ChatButtons {
id: sendBtns
Layout.topMargin: 1
Layout.fillHeight: true
Layout.preferredWidth: 30 + Theme.padding
Layout.minimumWidth: 30 + Theme.padding
Layout.maximumWidth: 200
}
}
MouseArea {
id: mouseArea1
anchors.rightMargin: 50
anchors.fill: parent
onClicked: {
txtData.forceActiveFocus(Qt.MouseFocusReason)
}
}
}
/*##^##
Designer {
D{i:0;width:600}
D{i:0;autoSize:true;formeditorColor:"#ffffff";height:100;width:600}
}
##^##*/

View File

@ -1,4 +1,5 @@
TopBar 1.0 TopBar.qml
ChatMessages 1.0 ChatMessages.qml
ChatInput 1.0 ChatInput.qml
EmptyChat 1.0 EmptyChat.qml
EmptyChat 1.0 EmptyChat.qml
ChatButtons 1.0 ChatButtons.qml

View File

@ -0,0 +1,66 @@
import QtQuick 2.3
import QtQuick.Controls 2.3
import QtQuick.Controls 2.12 as QQC2
import QtQuick.Layouts 1.3
import Qt.labs.platform 1.1
ListModel {
ListElement {
hash: "e30101701220fff8527a1b37070d46c9077877b7f7cc74da5c31adafe77ba65e5efefebf5d91"
url: "QmfZrHmLR5VvkXSDbArDR3TX6j4FgpDcrvNz2fHSJk1VvG"
}
ListElement {
hash: "e301017012208023d8c6bd327b0ac2be66423d59776a753d5f5492975fe0bd5b5601d7c1d9d3"
url: "QmWxrbdgU5q3VxStTQr4VTBdJNqwatMAUf8KZBLEfqZyii"
}
ListElement {
hash: "e3010170122064f4e8fa00a5b8164689e038a4d74e0b12f4490dcd4112e80057c254f6fbc135"
url: "QmV8k5Y4mE8hhiydohBw8hqRQgFzAQMiveCYKZ6PkKNVgg"
}
ListElement {
hash: "e301017012200d50bd618b0aed0562ed153de0bf77da766646e81a848982a2f8aaf7d7e94dcc"
url: "QmPEdR6ayeLouro7FfkQSt5aq3nfzYK4FT4nxnoxn5FuhV"
}
ListElement {
hash: "e3010170122055f08854a40acaac60355d9bb3eaa730b994e2e13484e67d2675103e0cda0c88"
url: "QmU886Hu3XAwYp8hfkMVhKoMNsPeeAJr7wgjYqAiAd4enB"
}
ListElement {
hash: "e301017012203fc2acfed328918bf000ee637ab4c25fa38f2c69b378b69b9212d61747d30c02"
url: "QmSdYZpjEAUG3fUpkPpC9ATnoaaiTLLj3W587uwx943AC1"
}
ListElement {
hash: "e3010170122096930b99e08c6c28c88c0b74bae7a0159f5c6438ab7d50294987533dabfee863"
url: "QmYURuqdkoycSLLz2qfMgjeK18Y4kT8PQfExRfbziVLSZ8"
}
ListElement {
hash: "e3010170122051ddbe29bee4bbc5fcf50d81faad0872f32b88cea4e4e4fcdbf2daf5d09eda76"
url: "QmTrDqsuN4DgKfjnDrWgo92EFCD1vFffYcZ1jMEr3AgCnH"
}
ListElement {
hash: "e301017012200647e07651c163515ce34d18b3c8636eeb4798dbaa1766b2a60facc59999b261"
url: "QmNmAiwa9PDcZs7b2oQ2D1wHfQfi1MFjp6QcfzHzGhhNJt"
}
ListElement {
hash: "e30101701220c539bfa744e39cf2ece1ab379a15c95338d513a9ce5178d4ad28be486b801bc2"
url: "QmbcY6hwDt3EKK6pJGJ6yFn3A872y2ajeBRrrKRj7KC3BX"
}
ListElement {
hash: "e301017012205ea333b9eb89918ed592f43372bd58dc3a91a7a71aa68b37369c2f66f931fd87"
url: "QmUi5NQSMg2kmhkLUtQ9Sjxnkwgn68x88wL2n357VAZdyx"
}
ListElement {
hash: "e3010170122007f05ba31bd77003bff562ed932a8b440de1ad05481dc622b1c0c571d6b39ffc"
url: "QmNse8urb4GJGEHYgUpRJBRwgiWaTGZtkoibVVUpveqfBq"
}
ListElement {
hash: "e30101701220906b7a664a87707db72921cf5c7416c61a717dfcb5fcff9bc04b28c612ae554d"
url: "QmY4QULmzFQ2AAbEuMvnd3Nd7qD8eWtyxiLD9CAf3kFZWU"
}
}
/*##^##
Designer {
D{i:0;autoSize:true;height:480;width:640}
}
##^##*/

View File

@ -0,0 +1,32 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
ListModel {
ListElement {
author: "cryptoworld1373"
name: "Status Cat"
price: 0
preview: "e3010170122050efc0a3e661339f31e1e44b3d15a1bf4e501c965a0523f57b701667fa90ccca"
thumbnail: "QmfZrHmLR5VvkXSDbArDR3TX6j4FgpDcrvNz2fHSJk1VvG"
}
ListElement {
author: "ETHDenver"
name: "ETHDenver Bufficorn"
price: 0
preview: "e30101701220a62487ef23b1bbdc2bf39583bb4259bda032450ac90d199eec8b0b74fe8de580"
thumbnail: "e30101701220d06f13f3de8da081ef2a1bc36ffa283c1bfe093bf45bc0332a6d748196e8ce16"
}
ListElement {
author: "Brooklyn Design Factory"
name: "Ghostatus"
price: 0
preview: "e3010170122027c67c9acbe98786f6db4aabca3fd3ec04993eaa3e08811aefe27d9786c3bf00"
thumbnail: "e30101701220a7beb4be086ad31ae19c64e5a832853571e239d9799a923a03779c4435c6fdad"
}
}
/*##^##
Designer {
D{i:0;autoSize:true;height:480;width:640}
}
##^##*/

View File

@ -1 +1,3 @@
MessagesData 1.0 MessagesData.qml
StickerData 1.0 StickerData.qml
StickerPackData 1.0 StickerPackData.qml

View File

@ -1,6 +1,7 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import "../../../imports"
import "../../../shared"
import "./components"
@ -50,6 +51,10 @@ Item {
AddChat {
id: addChat
anchors.right: parent.right
anchors.rightMargin: Theme.padding
anchors.top: parent.top
anchors.topMargin: 59
}
StackLayout {

View File

@ -2,108 +2,36 @@ import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import "../../../../shared"
import "../../../../imports"
import "../components"
Rectangle {
id: addChat
AddButton {
id: btnAdd
width: 36
height: 36
color: Theme.blue
radius: 50
anchors.right: parent.right
anchors.rightMargin: 16
anchors.top: parent.top
anchors.topMargin: 59
Image {
id: addChatLbl
fillMode: Image.PreserveAspectFit
source: "../../../img/plusSign.svg"
width: 14
height: 14
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
state: "default"
rotation: 0
states: [
State {
name: "default"
PropertyChanges {
target: addChatLbl
rotation: 0
}
},
State {
name: "rotated"
PropertyChanges {
target: addChatLbl
rotation: 45
}
}
]
transitions: [
Transition {
from: "default"
to: "rotated"
RotationAnimation {
duration: 150
direction: RotationAnimation.Clockwise
easing.type: Easing.InCubic
}
},
Transition {
from: "rotated"
to: "default"
RotationAnimation {
duration: 150
direction: RotationAnimation.Counterclockwise
easing.type: Easing.OutCubic
}
}
]
onClicked: {
let x = btnAdd.icon.x + btnAdd.icon.width / 2 - newChatMenu.width / 2
newChatMenu.popup(x, btnAdd.icon.height + 10)
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
cursorShape: Qt.PointingHandCursor
onClicked: {
addChatLbl.state = "rotated"
let x = addChatLbl.x + addChatLbl.width / 2 - newChatMenu.width / 2
newChatMenu.popup(x, addChatLbl.height + 10)
PopupMenu {
id: newChatMenu
Action {
text: qsTr("Start new chat")
icon.source: "../../../img/new_chat.svg"
onTriggered: privateChatPopup.open()
}
PopupMenu {
id: newChatMenu
Action {
text: qsTr("Start new chat")
icon.source: "../../../img/new_chat.svg"
onTriggered: privateChatPopup.open()
}
Action {
text: qsTr("Start group chat")
icon.source: "../../../img/group_chat.svg"
onTriggered: {
onTriggered: groupChatPopup.open()
}
}
Action {
text: qsTr("Join public chat")
icon.source: "../../../img/public_chat.svg"
onTriggered: publicChatPopup.open()
}
onAboutToHide: {
addChatLbl.state = "default"
}
Action {
text: qsTr("Start group chat")
icon.source: "../../../img/group_chat.svg"
onTriggered: groupChatPopup.open()
}
Action {
text: qsTr("Join public chat")
icon.source: "../../../img/public_chat.svg"
onTriggered: publicChatPopup.open()
}
onAboutToHide: {
btnAdd.icon.state = "default"
}
}
}
/*##^##
Designer {
D{i:0;formeditorZoom:3}
}
##^##*/

View File

@ -27,7 +27,7 @@ Rectangle {
border.width: 0
radius: Theme.radius
RoundImage {
Identicon {
id: accountImage
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter

View File

@ -0,0 +1,192 @@
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"
Popup {
id: popup
property var stickerList: StickerData {}
property var stickerPackList: StickerPackData {}
modal: false
property int selectedPackId
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
background: Rectangle {
radius: 8
border.color: Theme.grey
layer.enabled: true
layer.effect: DropShadow{
verticalOffset: 3
radius: 8
samples: 15
fast: true
cached: true
color: "#22000000"
}
}
contentItem: ColumnLayout {
parent: popup
anchors.fill: parent
spacing: 0
Item {
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: stickerHistory
anchors.fill: parent
visible: true
Image {
id: imgNoStickers
width: 56
height: 56
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 134
source: "../../../img/stickers_sad_icon.svg"
}
Text {
id: lblNoStickers
width: parent.width
font.pixelSize: 15
text: qsTr("You don't have any stickers yet")
horizontalAlignment: Text.AlignHCenter
anchors.top: imgNoStickers.bottom
anchors.topMargin: 8
}
StyledButton {
label: qsTr("Get Stickers")
anchors.top: lblNoStickers.bottom
anchors.topMargin: Theme.padding
anchors.horizontalCenter: parent.horizontalCenter
}
}
GridView {
id: stickerGrid
visible: false
anchors.fill: parent
cellWidth: 88
cellHeight: 88
model: stickerList
focus: true
clip: true
delegate: Item {
width: stickerGrid.cellWidth
height: stickerGrid.cellHeight
Column {
anchors.fill: parent
anchors.topMargin: 4
anchors.leftMargin: 4
Image {
width: 80
height: 80
fillMode: Image.PreserveAspectFit
source: "https://ipfs.infura.io/ipfs/" + url
MouseArea {
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
onClicked: {
chatsModel.sendSticker(hash, popup.selectedPackId)
popup.close()
}
}
}
}
}
}
}
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
AddButton {
id: btnAddStickerPack
anchors.left: parent.left
anchors.top: parent.top
width: 24
height: 24
}
RoundedIcon {
id: btnHistory
size: 24
color: Theme.darkGrey
imgPath: "../../../img/history_icon.svg"
anchors.left: btnAddStickerPack.right
anchors.leftMargin: Theme.padding
onClicked: {
packIndicator.updatePosition(-1)
stickerGrid.visible = false;
stickerHistory.visible = true;
}
}
RowLayout {
spacing: Theme.padding
anchors.top: parent.top
anchors.left: btnHistory.right
anchors.leftMargin: Theme.padding
Repeater {
id: stickerPackListView
model: stickerPackList
delegate: RoundedImage {
Layout.preferredHeight: height
Layout.preferredWidth: width
width: 24
height: 24
source: "https://ipfs.infura.io/ipfs/" + thumbnail
onClicked: {
chatsModel.setActiveStickerPackById(id)
popup.selectedPackId = id
packIndicator.updatePosition(index)
stickerGrid.visible = true;
stickerHistory.visible = false;
}
}
}
}
Rectangle {
id: packIndicator
border.color: Theme.blue
border.width: 1
height: 2
width: 16
x: 44
y: footerContent.height + 8 - height
function updatePosition(index) {
const startX = 44
const skipX = 40
const idx = index + 1
packIndicator.x = startX + skipX * idx;
}
}
}
}
}
/*##^##
Designer {
D{i:0;formeditorColor:"#ffffff";height:440;width:360}
}
##^##*/

View File

@ -5,4 +5,5 @@ GroupInfoPopup 1.0 GroupInfoPopup.qml
ProfilePropup 1.0 ProfilePopup.qml
ChannelIcon 1.0 ChannelIcon.qml
RenameGroupPopup 1.0 RenameGroupPopup.qml
GroupChatPopup 1.0 GroupChatPopup.qml
GroupChatPopup 1.0 GroupChatPopup.qml
StickersPopup 1.0 StickersPopup.qml

View File

@ -19,7 +19,7 @@ Rectangle {
border.width: 0
radius: Theme.radius
RoundImage {
Identicon {
id: accountImage
anchors.left: parent.left
anchors.leftMargin: Theme.padding

View File

@ -3,124 +3,59 @@ import QtQuick.Controls 2.13
import "../../../../shared"
import "../../../../imports"
Rectangle {
id: addAccount
width: 36
height: 36
color: Theme.blue
radius: 50
anchors.right: parent.right
anchors.rightMargin: 16
anchors.top: parent.top
anchors.topMargin: 59
Image {
id: addAccountLbl
fillMode: Image.PreserveAspectFit
source: "../../../img/plusSign.svg"
width: 14
height: 14
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
state: "default"
rotation: 0
states: [
State {
name: "default"
PropertyChanges {
target: addAccountLbl
rotation: 0
}
},
State {
name: "rotated"
PropertyChanges {
target: addAccountLbl
rotation: 45
}
}
]
transitions: [
Transition {
from: "default"
to: "rotated"
RotationAnimation {
duration: 150
direction: RotationAnimation.Clockwise
easing.type: Easing.InCubic
}
},
Transition {
from: "rotated"
to: "default"
RotationAnimation {
duration: 150
direction: RotationAnimation.Counterclockwise
easing.type: Easing.OutCubic
}
}
]
AddButton {
id: btnAdd
onClicked: {
let x = btnAdd.icon.x + btnAdd.icon.width / 2 - newAccountMenu.width / 2
newAccountMenu.popup(x, btnAdd.icon.height + 10)
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
cursorShape: Qt.PointingHandCursor
onClicked: {
addAccountLbl.state = "rotated"
let x = addAccountLbl.x + addAccountLbl.width / 2 - newAccountMenu.width / 2
newAccountMenu.popup(x, addAccountLbl.height + 10)
}
GenerateAccountModal {
id: generateAccountModal
}
AddAccountWithSeed {
id: addAccountWithSeedModal
}
AddAccountWithPrivateKey {
id: addAccountWithPrivateKeydModal
}
AddWatchOnlyAccount {
id: addWatchOnlyAccountModal
}
GenerateAccountModal {
id: generateAccountModal
PopupMenu {
id: newAccountMenu
width: 280
Action {
text: qsTr("Generate an account")
icon.source: "../../../img/generate_account.svg"
onTriggered: {
generateAccountModal.open()
}
}
AddAccountWithSeed {
id: addAccountWithSeedModal
Action {
text: qsTr("Add a watch-only address")
icon.source: "../../../img/add_watch_only.svg"
onTriggered: {
addWatchOnlyAccountModal.open()
}
}
AddAccountWithPrivateKey {
id: addAccountWithPrivateKeydModal
Action {
text: qsTr("Enter a seed phrase")
icon.source: "../../../img/enter_seed_phrase.svg"
onTriggered: {
addAccountWithSeedModal.open()
}
}
AddWatchOnlyAccount {
id: addWatchOnlyAccountModal
Action {
text: qsTr("Enter a private key")
icon.source: "../../../img/enter_private_key.svg"
onTriggered: {
addAccountWithPrivateKeydModal.open()
}
}
PopupMenu {
id: newAccountMenu
width: 280
Action {
text: qsTr("Generate an account")
icon.source: "../../../img/generate_account.svg"
onTriggered: {
generateAccountModal.open()
}
}
Action {
text: qsTr("Add a watch-only address")
icon.source: "../../../img/add_watch_only.svg"
onTriggered: {
addWatchOnlyAccountModal.open()
}
}
Action {
text: qsTr("Enter a seed phrase")
icon.source: "../../../img/enter_seed_phrase.svg"
onTriggered: {
addAccountWithSeedModal.open()
}
}
Action {
text: qsTr("Enter a private key")
icon.source: "../../../img/enter_private_key.svg"
onTriggered: {
addAccountWithPrivateKeydModal.open()
}
}
onAboutToHide: {
addAccountLbl.state = "default"
}
onAboutToHide: {
btnAdd.icon.state = "default"
}
}
}

View File

@ -0,0 +1,3 @@
<svg width="7" height="13" viewBox="0 0 7 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 1V6.17157C1 6.70201 1.21071 7.21071 1.58579 7.58579L6 12" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 254 B

View File

@ -0,0 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.56858 13.4863C7.22903 13.2491 6.75712 13.2426 6.46422 13.5354C6.17133 13.8283 6.16876 14.3077 6.49708 14.5603C7.46695 15.3063 8.68155 15.7499 9.99976 15.7499C11.318 15.7499 12.5326 15.3063 13.5024 14.5603C13.8308 14.3077 13.8282 13.8283 13.5353 13.5354C13.2424 13.2426 12.7705 13.2491 12.4309 13.4863C11.742 13.9676 10.9039 14.2499 9.99976 14.2499C9.09565 14.2499 8.25747 13.9676 7.56858 13.4863Z" fill="#939BA1"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.8645 11.6499C19.9401 11.1946 19.8959 10.7435 19.7602 10.3174C19.5838 9.76409 19.253 9.25286 18.8292 8.82907L11.1708 1.17069C10.747 0.746897 10.2358 0.416048 9.68247 0.239716C9.62921 0.222742 9.57555 0.2072 9.52155 0.193144C9.14354 0.0947561 8.7484 0.0692148 8.35001 0.135358C8.28899 0.145488 8.22816 0.156171 8.16753 0.1674C3.51984 1.02813 0 5.10295 0 9.99988C0 15.5227 4.47715 19.9999 10 19.9999C14.8969 19.9999 18.9717 16.48 19.8325 11.8324C19.8437 11.7717 19.8544 11.7109 19.8645 11.6499ZM1.5 9.99988C1.5 6.08625 4.14622 2.78816 7.74701 1.8013C7.87574 1.76602 8 1.8664 8 1.99988C8 7.52272 12.4772 11.9999 18 11.9999C18.1335 11.9999 18.2339 12.1241 18.1986 12.2529C17.2117 15.8537 13.9136 18.4999 10 18.4999C5.30558 18.4999 1.5 14.6943 1.5 9.99988ZM18 10.4999C18.1011 10.4999 18.1641 10.3935 18.1094 10.3085C18.0185 10.1673 17.9054 10.0266 17.7685 9.88973L10.1101 2.23135C9.97328 2.09448 9.83259 1.98135 9.69142 1.89052C9.60642 1.83582 9.5 1.89879 9.5 1.99988C9.5 6.6943 13.3056 10.4999 18 10.4999Z" fill="#939BA1"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.56858 13.4863C7.22903 13.2491 6.75712 13.2426 6.46422 13.5354C6.17133 13.8283 6.16876 14.3077 6.49708 14.5603C7.46695 15.3063 8.68155 15.7499 9.99976 15.7499C11.318 15.7499 12.5326 15.3063 13.5024 14.5603C13.8308 14.3077 13.8282 13.8283 13.5353 13.5354C13.2424 13.2426 12.7705 13.2491 12.4309 13.4863C11.742 13.9676 10.9039 14.2499 9.99976 14.2499C9.09565 14.2499 8.25747 13.9676 7.56858 13.4863Z" fill="#4360DF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.8645 11.6499C19.9401 11.1946 19.8959 10.7435 19.7602 10.3174C19.5838 9.76409 19.253 9.25286 18.8292 8.82907L11.1708 1.17069C10.747 0.746897 10.2358 0.416048 9.68247 0.239716C9.62921 0.222742 9.57555 0.2072 9.52155 0.193144C9.14354 0.0947561 8.7484 0.0692148 8.35001 0.135358C8.28899 0.145488 8.22816 0.156171 8.16753 0.1674C3.51984 1.02813 0 5.10295 0 9.99988C0 15.5227 4.47715 19.9999 10 19.9999C14.8969 19.9999 18.9717 16.48 19.8325 11.8324C19.8437 11.7717 19.8544 11.7109 19.8645 11.6499ZM1.5 9.99988C1.5 6.08625 4.14622 2.78816 7.74701 1.8013C7.87574 1.76602 8 1.8664 8 1.99988C8 7.52272 12.4772 11.9999 18 11.9999C18.1335 11.9999 18.2339 12.1241 18.1986 12.2529C17.2117 15.8537 13.9136 18.4999 10 18.4999C5.30558 18.4999 1.5 14.6943 1.5 9.99988ZM18 10.4999C18.1011 10.4999 18.1641 10.3935 18.1094 10.3085C18.0185 10.1673 17.9054 10.0266 17.7685 9.88973L10.1101 2.23135C9.97328 2.09448 9.83259 1.98135 9.69142 1.89052C9.60642 1.83582 9.5 1.89879 9.5 1.99988C9.5 6.6943 13.3056 10.4999 18 10.4999Z" fill="#4360DF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,4 @@
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="56" height="56" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M28 49.875C38.4868 49.875 47.25 42.4958 49.3805 32.6466C49.7619 30.8835 48.0357 29.5236 46.2386 29.6801C45.7068 29.7264 45.1686 29.75 44.625 29.75C34.4768 29.75 26.25 21.5232 26.25 11.375C26.25 10.8314 26.2736 10.2932 26.3199 9.76137C26.4764 7.96426 25.1165 6.23814 23.3534 6.61951C13.5042 8.74997 6.125 17.5132 6.125 28C6.125 40.0812 15.9188 49.875 28 49.875ZM44.625 28C46.0743 28 47.4794 27.8147 48.8183 27.4669C49.2523 27.3541 49.4609 26.873 49.2278 26.49C48.7244 25.663 47.973 24.6784 47.0151 23.5608C45.0993 21.3257 42.5276 18.7525 39.8876 16.1124C37.2475 13.4724 34.6743 10.9007 32.4392 8.98495C31.3216 8.02699 30.337 7.27556 29.51 6.77219C29.127 6.53905 28.6459 6.74772 28.5331 7.1817C28.1853 8.52059 28 9.92566 28 11.375C28 20.5567 35.4433 28 44.625 28ZM28 51.625C41.0477 51.625 51.625 41.0477 51.625 28C51.625 25.375 46.375 20.125 41.125 14.875C35.875 9.625 30.625 4.375 28 4.375C14.9523 4.375 4.375 14.9523 4.375 28C4.375 41.0477 14.9523 51.625 28 51.625ZM19.8578 41.7544C21.9375 39.7461 24.8144 38.5 28 38.5C31.1856 38.5 34.0625 39.7461 36.1422 41.7544C36.4898 42.0901 37.0437 42.0804 37.3794 41.7328C37.7151 41.3852 37.7054 40.8313 37.3578 40.4956C34.9588 38.1789 31.6486 36.75 28 36.75C24.3514 36.75 21.0412 38.1789 18.6422 40.4956C18.2946 40.8313 18.2849 41.3852 18.6206 41.7328C18.9563 42.0804 19.5102 42.0901 19.8578 41.7544Z" fill="#939BA1"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -72,8 +72,11 @@ DISTFILES += \
Theme.qml \
app/AppLayouts/Browser/BrowserLayout.qml \
app/AppLayouts/Chat/ChatColumn.qml \
app/AppLayouts/Chat/ChatColumn/MessagesData.qml \
app/AppLayouts/Chat/ChatColumn/samples/MessagesData.qml \
app/AppLayouts/Chat/ChatColumn/samples/StickerData.qml \
app/AppLayouts/Chat/ChatColumn/samples/StickerPackData.qml \
app/AppLayouts/Chat/ChatColumn/ChatInput.qml \
app/AppLayouts/Chat/ChatColumn/ChatButtons.qml \
app/AppLayouts/Chat/ChatColumn/ChatMessages.qml \
app/AppLayouts/Chat/ChatColumn/EmptyChat.qml \
app/AppLayouts/Chat/ChatColumn/Message.qml \
@ -81,7 +84,6 @@ DISTFILES += \
app/AppLayouts/Chat/ChatColumn/qmldir \
app/AppLayouts/Chat/ChatLayout.qml \
app/AppLayouts/Chat/ContactsColumn.qml \
app/AppLayouts/Chat/ContactsColumn/AddChat.qml \
app/AppLayouts/Chat/ContactsColumn/Channel.qml \
app/AppLayouts/Chat/ContactsColumn/ChannelList.qml \
app/AppLayouts/Chat/ContactsColumn/EmptyView.qml \
@ -91,6 +93,7 @@ DISTFILES += \
app/AppLayouts/Chat/components/PrivateChatPopup.qml \
app/AppLayouts/Chat/components/RenameGroupPopup.qml \
app/AppLayouts/Chat/components/SuggestedChannel.qml \
app/AppLayouts/Chat/components/StickersPopup.qml \
app/AppLayouts/Chat/components/qmldir \
app/AppLayouts/Chat/qmldir \
app/AppLayouts/Node/NodeLayout.qml \
@ -151,6 +154,7 @@ DISTFILES += \
app/img/compassActive.svg \
app/img/group_chat.svg \
app/img/hash.svg \
app/img/history_icon.svg \
app/img/message.svg \
app/img/messageActive.svg \
app/img/new_chat.svg \
@ -159,6 +163,9 @@ DISTFILES += \
app/img/public_chat.svg \
app/img/search.svg \
app/img/wallet.svg \
app/img/stickers_icon.svg \
app/img/stickers_icon_open.svg \
app/img/stickers_sad_icon.svg \
app/img/walletActive.svg \
app/qmldir \
imports/Utils.qml \
@ -196,10 +203,12 @@ DISTFILES += \
onboarding/img/wallet@2x.jpg \
onboarding/img/wallet@3x.jpg \
onboarding/qmldir \
shared/AddButton.qml \
shared/Input.qml \
shared/ModalPopup.qml \
shared/PopupMenu.qml \
shared/RoundImage.qml \
shared/Identicon.qml \
shared/RoundedImage.qml \
shared/SearchBox.qml \
shared/Select.qml \
shared/Separator.qml \

View File

@ -25,7 +25,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
RoundImage {
Identicon {
id: userImage
width: 40
height: 40

View File

@ -22,7 +22,7 @@ Rectangle {
color: selected || isHovered ? Theme.grey : Theme.transparent
radius: Theme.radius
RoundImage {
Identicon {
id: accountImage
anchors.left: parent.left
anchors.leftMargin: Theme.padding

80
ui/shared/AddButton.qml Normal file
View File

@ -0,0 +1,80 @@
import QtQuick 2.3
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import Qt.labs.platform 1.1
import "../imports"
Rectangle {
signal clicked
property int iconWidth: 14
property int iconHeight: 14
property alias icon: imgIcon
id: btnAddContainer
width: 36
height: 36
color: Theme.blue
radius: width / 2
Image {
id: imgIcon
fillMode: Image.PreserveAspectFit
source: "../app/img/plusSign.svg"
width: iconWidth
height: iconHeight
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
state: "default"
rotation: 0
states: [
State {
name: "default"
PropertyChanges {
target: imgIcon
rotation: 0
}
},
State {
name: "rotated"
PropertyChanges {
target: imgIcon
rotation: 45
}
}
]
transitions: [
Transition {
from: "default"
to: "rotated"
RotationAnimation {
duration: 150
direction: RotationAnimation.Clockwise
easing.type: Easing.InCubic
}
},
Transition {
from: "rotated"
to: "default"
RotationAnimation {
duration: 150
direction: RotationAnimation.Counterclockwise
easing.type: Easing.OutCubic
}
}
]
}
MouseArea {
id: mouseArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
cursorShape: Qt.PointingHandCursor
onClicked: {
imgIcon.state = "rotated"
btnAddContainer.clicked()
}
}
}

View File

@ -2,14 +2,16 @@ import QtQuick 2.13
import "../imports"
Rectangle {
id: root
property int size: 36
property color bg: Theme.blue
property url imgPath: ""
signal clicked
width: size
height: size
color: bg
radius: 50
radius: size / 2
Image {
id: roundedIconImage
@ -20,6 +22,16 @@ Rectangle {
fillMode: Image.PreserveAspectFit
source: imgPath
}
MouseArea {
id: mouseArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
cursorShape: Qt.PointingHandCursor
onClicked: {
root.clicked()
}
}
}
/*##^##

View File

@ -0,0 +1,38 @@
import QtQuick 2.12
import QtGraphicalEffects 1.0
Rectangle {
id: root;
signal clicked
property alias source: imgStickerPackThumb.source
radius: width / 2
width: 24
height: 24
// apply rounded corners mask
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
x: root.x; y: root.y
width: root.width
height: root.height
radius: root.radius
}
}
Image {
id: imgStickerPackThumb
opacity: 1
smooth: false
anchors.fill: parent
source: "https://ipfs.infura.io/ipfs/" + thumbnail
MouseArea {
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
onClicked: root.clicked()
}
}
}

View File

@ -12,4 +12,5 @@ StyledTextArea 1.0 StyledTextArea.qml
StyledText 1.0 StyledText.qml
StyledTextField 1.0 StyledTextField.qml
StyledTextEdit 1.0 StyledTextEdit.qml
RoundImage 1.0 RoundImage.qml
Identicon 1.0 Identicon.qml
RoundedImage 1.0 RoundedImage.qml