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 ../../status/mailservers as mailserver_model
import ../../signals/types import ../../signals/types
import ../../status/libstatus/types as status_types import ../../status/libstatus/types as status_types
import ../../status/libstatus/wallet as status_wallet
import ../../status/[chat, contacts, status] import ../../status/[chat, contacts, status]
import view, views/channels_list import view, views/channels_list
from eth/common/utils import parseAddress
logScope: logScope:
topics = "chat-controller" topics = "chat-controller"
@ -70,6 +73,12 @@ proc init*(self: ChatController) =
self.status.mailservers.init() self.status.mailservers.init()
self.status.chat.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) = proc handleMessage(self: ChatController, data: MessageSignal) =
self.status.chat.update(data.chats, data.messages) 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/chat as status_chat
import ../../status/contacts as status_contacts import ../../status/contacts as status_contacts
import ../../status/chat/[chat, message] import ../../status/chat/[chat, message]
import ../../status/libstatus/types
import ../../status/profile/profile 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: logScope:
topics = "chats-view" topics = "chats-view"
@ -19,6 +20,10 @@ QtObject:
callResult: string callResult: string
messageList: Table[string, ChatMessageList] messageList: Table[string, ChatMessageList]
activeChannel*: ChatItemView activeChannel*: ChatItemView
activeStickerPackId*: int
stickerPacks*: StickerPackList
stickers*: Table[int, StickerList]
emptyStickerList: StickerList
proc setup(self: ChatsView) = self.QAbstractListModel.setup proc setup(self: ChatsView) = self.QAbstractListModel.setup
@ -28,6 +33,7 @@ QtObject:
for msg in self.messageList.values: for msg in self.messageList.values:
msg.delete msg.delete
self.messageList = initTable[string, ChatMessageList]() self.messageList = initTable[string, ChatMessageList]()
self.stickers = initTable[int, StickerList]()
self.QAbstractListModel.delete self.QAbstractListModel.delete
proc newChatsView*(status: Status): ChatsView = proc newChatsView*(status: Status): ChatsView =
@ -35,9 +41,23 @@ QtObject:
result.status = status result.status = status
result.chats = newChannelsList() result.chats = newChannelsList()
result.activeChannel = newChatItemView(status) result.activeChannel = newChatItemView(status)
result.activeStickerPackId = -1
result.messageList = initTable[string, ChatMessageList]() result.messageList = initTable[string, ChatMessageList]()
result.stickerPacks = newStickerPackList()
result.stickers = initTable[int, StickerList]()
result.emptyStickerList = newStickerList()
result.setup() 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.} = proc getChatsList(self: ChatsView): QVariant {.slot.} =
newQVariant(self.chats) newQVariant(self.chats)
@ -68,6 +88,24 @@ QtObject:
write = setActiveChannelByIndex write = setActiveChannelByIndex
notify = activeChannelChanged 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) = proc setActiveChannel*(self: ChatsView, channel: string) =
if(channel == ""): return if(channel == ""): return
self.activeChannel.setChatItem(self.chats.getChannel(self.chats.chats.findIndexById(channel))) self.activeChannel.setChatItem(self.chats.getChannel(self.chats.chats.findIndexById(channel)))
@ -121,6 +159,9 @@ QtObject:
proc sendMessage*(self: ChatsView, message: string) {.slot.} = proc sendMessage*(self: ChatsView, message: string) {.slot.} =
discard self.status.chat.sendMessage(self.activeChannel.id, message) 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.} = proc joinChat*(self: ChatsView, channel: string, chatTypeInt: int): int {.slot.} =
self.status.chat.join(channel, ChatType(chatTypeInt)) 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 # Ideally, this module should call getSettings once, and fill the
# profile with all the information comming from the settings. # profile with all the information comming from the settings.
let response = status_settings.getSettings() let response = status_settings.getSettings()
let pubKey = parseJSON($response)["result"]["public-key"].getStr let pubKey = response["public-key"].getStr
let mnemonic = parseJSON($response)["result"]["mnemonic"].getStr let mnemonic = response["mnemonic"].getStr
profile.id = pubKey profile.id = pubKey
self.view.setNewProfile(profile) self.view.setNewProfile(profile)

View File

@ -1,9 +1,12 @@
import eventemitter, json, strutils, sequtils, tables, chronicles import eventemitter, json, strutils, sequtils, tables, chronicles
import libstatus/chat as status_chat import libstatus/chat as status_chat
import libstatus/stickers as status_stickers
import libstatus/types
import profile/profile import profile/profile
import chat/[chat, message] import chat/[chat, message], wallet
import ../signals/messages import ../signals/messages
import ens import ens
import eth/common/eth_types
type type
ChatUpdateArgs* = ref object of Args ChatUpdateArgs* = ref object of Args
@ -78,9 +81,21 @@ proc join*(self: ChatModel, chatId: string, chatType: ChatType) =
self.events.emit("channelJoined", ChannelArgs(chat: chat)) 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) = proc init*(self: ChatModel) =
let chatList = status_chat.loadChats() 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] = @[] var filters:seq[JsonNode] = @[]
for chat in chatList: for chat in chatList:
if self.hasChannel(chat.id): continue if self.hasChannel(chat.id): continue
@ -133,6 +148,10 @@ proc sendMessage*(self: ChatModel, chatId: string, msg: string): string =
self.emitUpdate(sentMessage) self.emitUpdate(sentMessage)
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) = proc chatMessages*(self: ChatModel, chatId: string, initialLoad:bool = true) =
if not self.msgCursor.hasKey(chatId): if not self.msgCursor.hasKey(chatId):
self.msgCursor[chatId] = ""; self.msgCursor[chatId] = "";

View File

@ -78,7 +78,22 @@ proc sendChatMessage*(chatId: string, msg: string): string =
"responseTo": nil, "responseTo": nil,
"ensName": nil, "ensName": nil,
"sticker": 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 from eth/common/utils import parseAddress
type type
@ -18,14 +19,26 @@ type Contract* = ref object
methods*: Table[string, Method] methods*: Table[string, Method]
let CONTRACTS: seq[Contract] = @[ 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: "snt", network: Network.Testnet, address: parseAddress("0xc55cf4b03948d7ebc8b9e8bad92643703811d162")),
Contract(name: "tribute-to-talk", network: Network.Testnet, address: parseAddress("0xC61aa0287247a0398589a66fCD6146EC0F295432")), 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.Mainnet, address: parseAddress("0x0577215622f43a39f4bc9640806dfea9b10d2a36")),
Contract(name: "stickers", network: Network.Testnet, address: parseAddress("0x8cc272396be7583c65bee82cd7b743c69a87287d")), 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-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")), Contract(name: "sticker-pack", network: Network.Testnet, address: parseAddress("0xf852198d0385c4b871e0b91804ecd47c6ba97351")),
# Strikers seems dead. Their website doesn't work anymore # Strikers seems dead. Their website doesn't work anymore
Contract(name: "strikers", network: Network.Mainnet, address: parseAddress("0xdcaad9fd9a74144d226dbf94ce6162ca9f09ed7e"), Contract(name: "strikers", network: Network.Mainnet, address: parseAddress("0xdcaad9fd9a74144d226dbf94ce6162ca9f09ed7e"),

View File

@ -1,14 +1,18 @@
import json
import core import core
import json
proc saveSettings*(key: string, value: string): string = proc saveSettings*(key: string, value: string): string =
callPrivateRPC("settings_saveSetting", %* [ callPrivateRPC("settings_saveSetting", %* [
key, $value key, $value
]) ])
proc getSettings*(): string =
callPrivateRPC("settings_getSettings")
# TODO: return an Table/Object instead of string
proc getWeb3ClientVersion*(): string = proc getWeb3ClientVersion*(): string =
parseJson(callPrivateRPC("web3_clientVersion"))["result"].getStr 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 keyUid*: string
photoPath*: string photoPath*: string
type
RpcResponse* = ref object
jsonrpc*: string
result*: string
id*: int
error*: string
proc toAccount*(account: GeneratedAccount): Account = proc toAccount*(account: GeneratedAccount): Account =
result = Account(name: account.name, photoPath: account.photoPath, keyUid: account.address) result = Account(name: account.name, photoPath: account.photoPath, keyUid: account.address)
@ -90,3 +97,18 @@ type
value*: string value*: string
fromAddress*: string fromAddress*: string
to*: 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 = proc getDefaultCurrency*(self: WalletModel): string =
# TODO: this should come from a model? It is going to be used too in the # 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 # 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) = proc setDefaultCurrency*(self: WalletModel, currency: string) =
discard status_settings.saveSettings("currency", currency) discard status_settings.saveSettings("currency", currency)

View File

@ -1,5 +1,6 @@
import strformat, httpclient, json, chronicles, sequtils, strutils, tables 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 eth/common/eth_types
import account import account

View File

@ -36,15 +36,24 @@ StackLayout {
} }
} }
RowLayout {
Rectangle {
id: chatInputContainer id: chatInputContainer
height: 70 height: 70
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
Layout.fillWidth: true Layout.fillWidth: true
Layout.bottomMargin: 0 Layout.preferredWidth: parent.width
Layout.alignment: Qt.AlignLeft | Qt.AlignBottom Layout.preferredHeight: height
transformOrigin: Item.Bottom 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 2.13
import QtQuick.Controls 2.13 import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13 import QtQuick.Layouts 1.13
import "../components"
import "../../../../shared" import "../../../../shared"
import "../../../../imports" import "../../../../imports"
Rectangle { Rectangle {
id: element2
width: 200
height: 70
Layout.fillWidth: true
color: "white"
border.width: 0 border.width: 0
visible: chatsModel.activeChannel.chatType != Constants.chatTypePrivateGroupChat || chatsModel.activeChannel.isMember(profileModel.profile.pubKey) visible: chatsModel.activeChannel.chatType != Constants.chatTypePrivateGroupChat || chatsModel.activeChannel.isMember(profileModel.profile.pubKey)
Rectangle { RowLayout {
id: rectangle spacing: 0
color: "#00000000"
border.color: Theme.grey
anchors.fill: parent 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 { StyledTextField {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: parent.width - sendBtns.width
id: txtData id: txtData
text: "" text: ""
padding: 0 leftPadding: 12
rightPadding: Theme.padding
font.pixelSize: 14 font.pixelSize: 14
placeholderText: qsTr("Type a message...") 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 selectByMouse: true
Keys.onEnterPressed: { Keys.onEnterPressed: {
chatsModel.sendMessage(txtData.text) chatsModel.sendMessage(txtData.text)
@ -75,10 +39,28 @@ Rectangle {
color: "#00000000" 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 { Designer {
D{i:0;width:600} D{i:0;autoSize:true;formeditorColor:"#ffffff";height:100;width:600}
} }
##^##*/ ##^##*/

View File

@ -2,3 +2,4 @@ TopBar 1.0 TopBar.qml
ChatMessages 1.0 ChatMessages.qml ChatMessages 1.0 ChatMessages.qml
ChatInput 1.0 ChatInput.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 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 2.13
import QtQuick.Controls 2.13 import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13 import QtQuick.Layouts 1.13
import "../../../imports" import "../../../imports"
import "../../../shared" import "../../../shared"
import "./components" import "./components"
@ -50,6 +51,10 @@ Item {
AddChat { AddChat {
id: addChat id: addChat
anchors.right: parent.right
anchors.rightMargin: Theme.padding
anchors.top: parent.top
anchors.topMargin: 59
} }
StackLayout { StackLayout {

View File

@ -2,108 +2,36 @@ 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 "../../../../imports"
import "../components" import "../components"
AddButton {
Rectangle { id: btnAdd
id: addChat
width: 36 width: 36
height: 36 height: 36
color: Theme.blue
radius: 50
anchors.right: parent.right
anchors.rightMargin: 16
anchors.top: parent.top
anchors.topMargin: 59
Image { onClicked: {
id: addChatLbl let x = btnAdd.icon.x + btnAdd.icon.width / 2 - newChatMenu.width / 2
fillMode: Image.PreserveAspectFit newChatMenu.popup(x, btnAdd.icon.height + 10)
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
}
}
]
} }
MouseArea { PopupMenu {
anchors.fill: parent id: newChatMenu
acceptedButtons: Qt.LeftButton | Qt.RightButton Action {
cursorShape: Qt.PointingHandCursor text: qsTr("Start new chat")
onClicked: { icon.source: "../../../img/new_chat.svg"
addChatLbl.state = "rotated" onTriggered: privateChatPopup.open()
let x = addChatLbl.x + addChatLbl.width / 2 - newChatMenu.width / 2
newChatMenu.popup(x, addChatLbl.height + 10)
} }
Action {
PopupMenu { text: qsTr("Start group chat")
id: newChatMenu icon.source: "../../../img/group_chat.svg"
Action { onTriggered: groupChatPopup.open()
text: qsTr("Start new chat") }
icon.source: "../../../img/new_chat.svg" Action {
onTriggered: privateChatPopup.open() text: qsTr("Join public chat")
} icon.source: "../../../img/public_chat.svg"
Action { onTriggered: publicChatPopup.open()
text: qsTr("Start group chat") }
icon.source: "../../../img/group_chat.svg" onAboutToHide: {
onTriggered: { btnAdd.icon.state = "default"
onTriggered: groupChatPopup.open()
}
}
Action {
text: qsTr("Join public chat")
icon.source: "../../../img/public_chat.svg"
onTriggered: publicChatPopup.open()
}
onAboutToHide: {
addChatLbl.state = "default"
}
} }
} }
} }
/*##^##
Designer {
D{i:0;formeditorZoom:3}
}
##^##*/

View File

@ -27,7 +27,7 @@ Rectangle {
border.width: 0 border.width: 0
radius: Theme.radius radius: Theme.radius
RoundImage { Identicon {
id: accountImage id: accountImage
anchors.left: parent.left anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter 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

@ -6,3 +6,4 @@ ProfilePropup 1.0 ProfilePopup.qml
ChannelIcon 1.0 ChannelIcon.qml ChannelIcon 1.0 ChannelIcon.qml
RenameGroupPopup 1.0 RenameGroupPopup.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 border.width: 0
radius: Theme.radius radius: Theme.radius
RoundImage { Identicon {
id: accountImage id: accountImage
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: Theme.padding anchors.leftMargin: Theme.padding

View File

@ -3,124 +3,59 @@ import QtQuick.Controls 2.13
import "../../../../shared" import "../../../../shared"
import "../../../../imports" import "../../../../imports"
Rectangle { AddButton {
id: addAccount id: btnAdd
width: 36 onClicked: {
height: 36 let x = btnAdd.icon.x + btnAdd.icon.width / 2 - newAccountMenu.width / 2
color: Theme.blue newAccountMenu.popup(x, btnAdd.icon.height + 10)
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
}
}
]
} }
MouseArea { GenerateAccountModal {
anchors.fill: parent id: generateAccountModal
acceptedButtons: Qt.LeftButton | Qt.RightButton }
cursorShape: Qt.PointingHandCursor AddAccountWithSeed {
onClicked: { id: addAccountWithSeedModal
addAccountLbl.state = "rotated" }
let x = addAccountLbl.x + addAccountLbl.width / 2 - newAccountMenu.width / 2 AddAccountWithPrivateKey {
newAccountMenu.popup(x, addAccountLbl.height + 10) id: addAccountWithPrivateKeydModal
} }
AddWatchOnlyAccount {
id: addWatchOnlyAccountModal
}
GenerateAccountModal { PopupMenu {
id: generateAccountModal id: newAccountMenu
width: 280
Action {
text: qsTr("Generate an account")
icon.source: "../../../img/generate_account.svg"
onTriggered: {
generateAccountModal.open()
}
} }
AddAccountWithSeed { Action {
id: addAccountWithSeedModal text: qsTr("Add a watch-only address")
icon.source: "../../../img/add_watch_only.svg"
onTriggered: {
addWatchOnlyAccountModal.open()
}
} }
AddAccountWithPrivateKey { Action {
id: addAccountWithPrivateKeydModal text: qsTr("Enter a seed phrase")
icon.source: "../../../img/enter_seed_phrase.svg"
onTriggered: {
addAccountWithSeedModal.open()
}
} }
AddWatchOnlyAccount { Action {
id: addWatchOnlyAccountModal text: qsTr("Enter a private key")
icon.source: "../../../img/enter_private_key.svg"
onTriggered: {
addAccountWithPrivateKeydModal.open()
}
} }
onAboutToHide: {
PopupMenu { btnAdd.icon.state = "default"
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"
}
} }
} }
} }

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

View File

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

View File

@ -22,7 +22,7 @@ Rectangle {
color: selected || isHovered ? Theme.grey : Theme.transparent color: selected || isHovered ? Theme.grey : Theme.transparent
radius: Theme.radius radius: Theme.radius
RoundImage { Identicon {
id: accountImage id: accountImage
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: Theme.padding 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" import "../imports"
Rectangle { Rectangle {
id: root
property int size: 36 property int size: 36
property color bg: Theme.blue property color bg: Theme.blue
property url imgPath: "" property url imgPath: ""
signal clicked
width: size width: size
height: size height: size
color: bg color: bg
radius: 50 radius: size / 2
Image { Image {
id: roundedIconImage id: roundedIconImage
@ -20,6 +22,16 @@ Rectangle {
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: imgPath 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 StyledText 1.0 StyledText.qml
StyledTextField 1.0 StyledTextField.qml StyledTextField 1.0 StyledTextField.qml
StyledTextEdit 1.0 StyledTextEdit.qml StyledTextEdit 1.0 StyledTextEdit.qml
RoundImage 1.0 RoundImage.qml Identicon 1.0 Identicon.qml
RoundedImage 1.0 RoundedImage.qml