feat: add profile pic support
This commit is contained in:
parent
43d9d5184d
commit
d01c9fef79
|
@ -15,6 +15,8 @@ import web3/[conversions, ethtypes]
|
||||||
import ../../status/threads
|
import ../../status/threads
|
||||||
import views/[channels_list, message_list, chat_item, suggestions_list, reactions, stickers, groups, transactions]
|
import views/[channels_list, message_list, chat_item, suggestions_list, reactions, stickers, groups, transactions]
|
||||||
import json_serialization
|
import json_serialization
|
||||||
|
import ../../status/libstatus/utils
|
||||||
|
import ../utils/image_utils
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "chats-view"
|
topics = "chats-view"
|
||||||
|
@ -149,10 +151,7 @@ QtObject:
|
||||||
proc sendImage*(self: ChatsView, imagePath: string): string {.slot.} =
|
proc sendImage*(self: ChatsView, imagePath: string): string {.slot.} =
|
||||||
result = ""
|
result = ""
|
||||||
try:
|
try:
|
||||||
var image: string = replace(imagePath, "file://", "")
|
var image = image_utils.formatImagePath(imagePath)
|
||||||
if defined(windows):
|
|
||||||
# Windows doesn't work with paths starting with a slash
|
|
||||||
image.removePrefix('/')
|
|
||||||
let tmpImagePath = image_resizer(image, 2000, TMPDIR)
|
let tmpImagePath = image_resizer(image, 2000, TMPDIR)
|
||||||
self.status.chat.sendImage(self.activeChannel.id, tmpImagePath)
|
self.status.chat.sendImage(self.activeChannel.id, tmpImagePath)
|
||||||
removeFile(tmpImagePath)
|
removeFile(tmpImagePath)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import NimQml, Tables, json, nimcrypto, strformat, json_serialization
|
import NimQml, Tables, json, nimcrypto, strformat, json_serialization, chronicles
|
||||||
import ../../status/signals/types
|
import ../../status/signals/types
|
||||||
import ../../status/libstatus/types as status_types
|
import ../../status/libstatus/types as status_types
|
||||||
import ../../status/libstatus/accounts as status_accounts
|
import ../../status/libstatus/accounts as status_accounts
|
||||||
|
@ -9,9 +9,11 @@ import core
|
||||||
|
|
||||||
type
|
type
|
||||||
AccountRoles {.pure.} = enum
|
AccountRoles {.pure.} = enum
|
||||||
Username = UserRole + 1,
|
Username = UserRole + 1
|
||||||
Identicon = UserRole + 2,
|
Identicon = UserRole + 2
|
||||||
Address = UserRole + 3
|
Address = UserRole + 3
|
||||||
|
ThumbnailImage = UserRole + 4
|
||||||
|
LargeImage = UserRole + 5
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type LoginView* = ref object of QAbstractListModel
|
type LoginView* = ref object of QAbstractListModel
|
||||||
|
@ -41,7 +43,12 @@ QtObject:
|
||||||
|
|
||||||
proc setCurrentAccount*(self: LoginView, selectedAccountIdx: int) {.slot.} =
|
proc setCurrentAccount*(self: LoginView, selectedAccountIdx: int) {.slot.} =
|
||||||
let currNodeAcct = self.accounts[selectedAccountIdx]
|
let currNodeAcct = self.accounts[selectedAccountIdx]
|
||||||
self.currentAccount.setAccount(GeneratedAccount(name: currNodeAcct.name, photoPath: currNodeAcct.photoPath, address: currNodeAcct.keyUid))
|
self.currentAccount.setAccount(GeneratedAccount(
|
||||||
|
name: currNodeAcct.name,
|
||||||
|
identicon: currNodeAcct.identicon,
|
||||||
|
address: currNodeAcct.keyUid,
|
||||||
|
identityImage: currNodeAcct.identityImage
|
||||||
|
))
|
||||||
|
|
||||||
QtProperty[QVariant] currentAccount:
|
QtProperty[QVariant] currentAccount:
|
||||||
read = getCurrentAccount
|
read = getCurrentAccount
|
||||||
|
@ -72,13 +79,25 @@ QtObject:
|
||||||
let assetRole = role.AccountRoles
|
let assetRole = role.AccountRoles
|
||||||
case assetRole:
|
case assetRole:
|
||||||
of AccountRoles.Username: result = newQVariant(asset.name)
|
of AccountRoles.Username: result = newQVariant(asset.name)
|
||||||
of AccountRoles.Identicon: result = newQVariant(asset.photoPath)
|
of AccountRoles.Identicon: result = newQVariant(asset.identicon)
|
||||||
of AccountRoles.Address: result = newQVariant(asset.keyUid)
|
of AccountRoles.Address: result = newQVariant(asset.keyUid)
|
||||||
|
of AccountRoles.ThumbnailImage:
|
||||||
|
if (not asset.identityImage.isNil):
|
||||||
|
result = newQVariant(asset.identityImage.thumbnail)
|
||||||
|
else:
|
||||||
|
result = newQVariant(asset.identicon)
|
||||||
|
of AccountRoles.LargeImage:
|
||||||
|
if (not asset.identityImage.isNil):
|
||||||
|
result = newQVariant(asset.identityImage.large)
|
||||||
|
else:
|
||||||
|
result = newQVariant(asset.identicon)
|
||||||
|
|
||||||
method roleNames(self: LoginView): Table[int, string] =
|
method roleNames(self: LoginView): Table[int, string] =
|
||||||
{ AccountRoles.Username.int:"username",
|
{ AccountRoles.Username.int:"username",
|
||||||
AccountRoles.Identicon.int:"identicon",
|
AccountRoles.Identicon.int:"identicon",
|
||||||
AccountRoles.Address.int:"address" }.toTable
|
AccountRoles.Address.int:"address",
|
||||||
|
AccountRoles.ThumbnailImage.int:"thumbnailImage",
|
||||||
|
AccountRoles.LargeImage.int:"largeImage" }.toTable
|
||||||
|
|
||||||
proc login(self: LoginView, password: string): string {.slot.} =
|
proc login(self: LoginView, password: string): string {.slot.} =
|
||||||
var currentAccountId = 0
|
var currentAccountId = 0
|
||||||
|
|
|
@ -57,7 +57,7 @@ QtObject:
|
||||||
let assetRole = role.AccountRoles
|
let assetRole = role.AccountRoles
|
||||||
case assetRole:
|
case assetRole:
|
||||||
of AccountRoles.Username: result = newQVariant(asset.name)
|
of AccountRoles.Username: result = newQVariant(asset.name)
|
||||||
of AccountRoles.Identicon: result = newQVariant(asset.photoPath)
|
of AccountRoles.Identicon: result = newQVariant(asset.identicon)
|
||||||
of AccountRoles.Address: result = newQVariant(asset.keyUid)
|
of AccountRoles.Address: result = newQVariant(asset.keyUid)
|
||||||
|
|
||||||
method roleNames(self: OnboardingView): Table[int, string] =
|
method roleNames(self: OnboardingView): Table[int, string] =
|
||||||
|
|
|
@ -28,11 +28,29 @@ QtObject:
|
||||||
read = username
|
read = username
|
||||||
notify = accountChanged
|
notify = accountChanged
|
||||||
|
|
||||||
proc identicon*(self: AccountInfoView): string {.slot.} = result = ?.self.account.photoPath
|
proc identicon*(self: AccountInfoView): string {.slot.} = result = ?.self.account.identicon
|
||||||
QtProperty[string] identicon:
|
QtProperty[string] identicon:
|
||||||
read = identicon
|
read = identicon
|
||||||
notify = accountChanged
|
notify = accountChanged
|
||||||
|
|
||||||
|
proc thumbnailImage*(self: AccountInfoView): string {.slot.} =
|
||||||
|
if (not self.account.identityImage.isNil):
|
||||||
|
result = self.account.identityImage.thumbnail
|
||||||
|
else:
|
||||||
|
result = ?.self.account.identicon
|
||||||
|
QtProperty[string] thumbnailImage:
|
||||||
|
read = thumbnailImage
|
||||||
|
notify = identityImageChanged
|
||||||
|
|
||||||
|
proc largeImage*(self: AccountInfoView): string {.slot.} =
|
||||||
|
if (not self.account.identityImage.isNil):
|
||||||
|
result = self.account.identityImage.large
|
||||||
|
else:
|
||||||
|
result = ?.self.account.identicon
|
||||||
|
QtProperty[string] largeImage:
|
||||||
|
read = largeImage
|
||||||
|
notify = identityImageChanged
|
||||||
|
|
||||||
proc address*(self: AccountInfoView): string {.slot.} = result = ?.self.account.address
|
proc address*(self: AccountInfoView): string {.slot.} = result = ?.self.account.address
|
||||||
QtProperty[string] address:
|
QtProperty[string] address:
|
||||||
read = address
|
read = address
|
||||||
|
|
|
@ -43,6 +43,11 @@ proc init*(self: ProfileController, account: Account) =
|
||||||
profile.id = pubKey
|
profile.id = pubKey
|
||||||
profile.address = account.keyUid
|
profile.address = account.keyUid
|
||||||
|
|
||||||
|
let identityImage = self.status.profile.getIdentityImage(profile.address)
|
||||||
|
|
||||||
|
if (identityImage.thumbnail != ""):
|
||||||
|
profile.identityImage = identityImage
|
||||||
|
|
||||||
self.view.devices.addDevices(status_devices.getAllDevices())
|
self.view.devices.addDevices(status_devices.getAllDevices())
|
||||||
self.view.devices.setDeviceSetup(status_devices.isDeviceSetup())
|
self.view.devices.setDeviceSetup(status_devices.isDeviceSetup())
|
||||||
self.view.setNewProfile(profile)
|
self.view.setNewProfile(profile)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import NimQml, sequtils, strutils, sugar, os, json
|
import NimQml, sequtils, strutils, sugar, os, json, chronicles
|
||||||
import views/[mailservers_list, ens_manager, contacts, devices, mailservers, mnemonic, network, fleets, profile_info, device_list, dapp_list]
|
import views/[mailservers_list, ens_manager, contacts, devices, mailservers, mnemonic, network, fleets, profile_info, device_list, dapp_list]
|
||||||
import ../chat/views/channels_list
|
import ../chat/views/channels_list
|
||||||
import ../../status/profile/profile
|
import ../../status/profile/profile
|
||||||
|
@ -13,6 +13,7 @@ import ../../status/threads
|
||||||
import ../../status/libstatus/types
|
import ../../status/libstatus/types
|
||||||
import ../../status/libstatus/accounts/constants as accountConstants
|
import ../../status/libstatus/accounts/constants as accountConstants
|
||||||
import qrcode/qrcode
|
import qrcode/qrcode
|
||||||
|
import ../utils/image_utils
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type ProfileView* = ref object of QObject
|
type ProfileView* = ref object of QObject
|
||||||
|
@ -209,3 +210,24 @@ QtObject:
|
||||||
|
|
||||||
QtProperty[QVariant] network:
|
QtProperty[QVariant] network:
|
||||||
read = getNetwork
|
read = getNetwork
|
||||||
|
|
||||||
|
proc uploadNewProfilePic*(self: ProfileView, imageUrl: string, aX: int, aY: int, bX: int, bY: int): string {.slot.} =
|
||||||
|
var image = image_utils.formatImagePath(imageUrl)
|
||||||
|
# FIXME the function to get the file size is messed up
|
||||||
|
# var size = image_getFileSize(image)
|
||||||
|
# TODO find a way to i18n this (maybe send just a code and then QML sets the right string)
|
||||||
|
# return "Max file size is 20MB"
|
||||||
|
|
||||||
|
try:
|
||||||
|
# TODO add crop tool for the image
|
||||||
|
let identityImage = self.status.profile.storeIdentityImage(self.profile.address, image, aX, aY, bX, bY)
|
||||||
|
self.profile.setIdentityImage(identityImage)
|
||||||
|
result = ""
|
||||||
|
except Exception as e:
|
||||||
|
error "Error storing identity image", msg=e.msg
|
||||||
|
result = "Error storing identity image: " & e.msg
|
||||||
|
|
||||||
|
proc deleteProfilePic*(self: ProfileView): string {.slot.} =
|
||||||
|
result = self.status.profile.deleteIdentityImage(self.profile.address)
|
||||||
|
if (result == ""):
|
||||||
|
self.profile.removeIdentityImage()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import NimQml
|
import NimQml, chronicles
|
||||||
import Tables
|
import Tables
|
||||||
import ../../../status/profile/profile
|
import ../../../status/profile/profile
|
||||||
from ../../../status/ens import nil
|
from ../../../status/ens import nil
|
||||||
|
@ -14,6 +14,8 @@ type
|
||||||
Alias = UserRole + 7
|
Alias = UserRole + 7
|
||||||
EnsVerified = UserRole + 8
|
EnsVerified = UserRole + 8
|
||||||
LocalNickname = UserRole + 9
|
LocalNickname = UserRole + 9
|
||||||
|
ThumbnailImage = UserRole + 10
|
||||||
|
LargeImage = UserRole + 11
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type ContactList* = ref object of QAbstractListModel
|
type ContactList* = ref object of QAbstractListModel
|
||||||
|
@ -42,6 +44,14 @@ QtObject:
|
||||||
return ens.userNameOrAlias(contact)
|
return ens.userNameOrAlias(contact)
|
||||||
return defaultValue
|
return defaultValue
|
||||||
|
|
||||||
|
proc getContactIndexByPubkey(self: ContactList, pubkey: string): int {.slot.} =
|
||||||
|
var i = 0
|
||||||
|
for contact in self.contacts:
|
||||||
|
if (contact.id == pubkey):
|
||||||
|
return i
|
||||||
|
i = i + 1
|
||||||
|
return -1
|
||||||
|
|
||||||
proc rowData(self: ContactList, index: int, column: string): string {.slot.} =
|
proc rowData(self: ContactList, index: int, column: string): string {.slot.} =
|
||||||
let contact = self.contacts[index]
|
let contact = self.contacts[index]
|
||||||
case column:
|
case column:
|
||||||
|
@ -54,6 +64,8 @@ QtObject:
|
||||||
of "alias": result = contact.alias
|
of "alias": result = contact.alias
|
||||||
of "ensVerified": result = $contact.ensVerified
|
of "ensVerified": result = $contact.ensVerified
|
||||||
of "localNickname": result = $contact.localNickname
|
of "localNickname": result = $contact.localNickname
|
||||||
|
of "thumbnailImage": result = $contact.identityImage.thumbnail
|
||||||
|
of "largeImage": result = $contact.identityImage.large
|
||||||
|
|
||||||
method data(self: ContactList, index: QModelIndex, role: int): QVariant =
|
method data(self: ContactList, index: QModelIndex, role: int): QVariant =
|
||||||
if not index.isValid:
|
if not index.isValid:
|
||||||
|
@ -71,6 +83,8 @@ QtObject:
|
||||||
of ContactRoles.Alias: result = newQVariant(contact.alias)
|
of ContactRoles.Alias: result = newQVariant(contact.alias)
|
||||||
of ContactRoles.EnsVerified: result = newQVariant(contact.ensVerified)
|
of ContactRoles.EnsVerified: result = newQVariant(contact.ensVerified)
|
||||||
of ContactRoles.LocalNickname: result = newQVariant(contact.localNickname)
|
of ContactRoles.LocalNickname: result = newQVariant(contact.localNickname)
|
||||||
|
of ContactRoles.ThumbnailImage: result = newQVariant(contact.identityImage.thumbnail)
|
||||||
|
of ContactRoles.LargeImage: result = newQVariant(contact.identityImage.large)
|
||||||
|
|
||||||
method roleNames(self: ContactList): Table[int, string] =
|
method roleNames(self: ContactList): Table[int, string] =
|
||||||
{
|
{
|
||||||
|
@ -82,7 +96,9 @@ QtObject:
|
||||||
ContactRoles.IsBlocked.int:"isBlocked",
|
ContactRoles.IsBlocked.int:"isBlocked",
|
||||||
ContactRoles.Alias.int:"alias",
|
ContactRoles.Alias.int:"alias",
|
||||||
ContactRoles.LocalNickname.int:"localNickname",
|
ContactRoles.LocalNickname.int:"localNickname",
|
||||||
ContactRoles.EnsVerified.int:"ensVerified"
|
ContactRoles.EnsVerified.int:"ensVerified",
|
||||||
|
ContactRoles.ThumbnailImage.int:"thumbnailImage",
|
||||||
|
ContactRoles.LargeImage.int:"largeImage"
|
||||||
}.toTable
|
}.toTable
|
||||||
|
|
||||||
proc addContactToList*(self: ContactList, contact: Profile) =
|
proc addContactToList*(self: ContactList, contact: Profile) =
|
||||||
|
@ -95,6 +111,8 @@ QtObject:
|
||||||
if(c.isContact()): return true
|
if(c.isContact()): return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
proc contactChanged*(self: ContactList, pubkey: string) {.signal.}
|
||||||
|
|
||||||
proc updateContact*(self: ContactList, contact: Profile) =
|
proc updateContact*(self: ContactList, contact: Profile) =
|
||||||
var found = false
|
var found = false
|
||||||
let topLeft = self.createIndex(0, 0, nil)
|
let topLeft = self.createIndex(0, 0, nil)
|
||||||
|
@ -104,11 +122,13 @@ QtObject:
|
||||||
found = true
|
found = true
|
||||||
c.ensName = contact.ensName
|
c.ensName = contact.ensName
|
||||||
c.ensVerified = contact.ensVerified
|
c.ensVerified = contact.ensVerified
|
||||||
|
c.identityImage = contact.identityImage
|
||||||
|
|
||||||
if not found:
|
if not found:
|
||||||
self.addContactToList(contact)
|
self.addContactToList(contact)
|
||||||
else:
|
else:
|
||||||
self.dataChanged(topLeft, bottomRight, @[ContactRoles.Name.int])
|
self.dataChanged(topLeft, bottomRight, @[ContactRoles.Name.int])
|
||||||
|
self.contactChanged(contact.id)
|
||||||
|
|
||||||
proc setNewData*(self: ContactList, contactList: seq[Profile]) =
|
proc setNewData*(self: ContactList, contactList: seq[Profile]) =
|
||||||
self.beginResetModel()
|
self.beginResetModel()
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
import NimQml
|
import NimQml
|
||||||
import chronicles
|
import chronicles
|
||||||
import ../../../status/profile/profile
|
import ../../../status/profile/profile
|
||||||
|
import ../../../status/libstatus/types
|
||||||
|
import std/wrapnils
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type ProfileInfoView* = ref object of QObject
|
type ProfileInfoView* = ref object of QObject
|
||||||
username*: string
|
username*: string
|
||||||
identicon*: string
|
identicon*: string
|
||||||
address*: string
|
address*: string
|
||||||
|
identityImage*: IdentityImage
|
||||||
pubKey*: string
|
pubKey*: string
|
||||||
appearance*: int
|
appearance*: int
|
||||||
ensVerified*: bool
|
ensVerified*: bool
|
||||||
|
@ -24,11 +27,14 @@ QtObject:
|
||||||
result.username = ""
|
result.username = ""
|
||||||
result.identicon = ""
|
result.identicon = ""
|
||||||
result.appearance = 0
|
result.appearance = 0
|
||||||
|
result.identityImage = IdentityImage()
|
||||||
result.ensVerified = false
|
result.ensVerified = false
|
||||||
result.setup
|
result.setup
|
||||||
|
|
||||||
proc profileChanged*(self: ProfileInfoView) {.signal.}
|
proc profileChanged*(self: ProfileInfoView) {.signal.}
|
||||||
|
|
||||||
|
proc identityImageChanged*(self: ProfileInfoView) {.signal.}
|
||||||
|
|
||||||
proc setProfile*(self: ProfileInfoView, profile: Profile) =
|
proc setProfile*(self: ProfileInfoView, profile: Profile) =
|
||||||
self.username = profile.username
|
self.username = profile.username
|
||||||
self.identicon = profile.identicon
|
self.identicon = profile.identicon
|
||||||
|
@ -36,8 +42,17 @@ QtObject:
|
||||||
self.pubKey = profile.id
|
self.pubKey = profile.id
|
||||||
self.address = profile.address
|
self.address = profile.address
|
||||||
self.ensVerified = profile.ensVerified
|
self.ensVerified = profile.ensVerified
|
||||||
|
self.identityImage = profile.identityImage
|
||||||
self.profileChanged()
|
self.profileChanged()
|
||||||
|
|
||||||
|
proc setIdentityImage*(self: ProfileInfoView, identityImage: IdentityImage) =
|
||||||
|
self.identityImage = identityImage
|
||||||
|
self.identityImageChanged()
|
||||||
|
|
||||||
|
proc removeIdentityImage*(self: ProfileInfoView) =
|
||||||
|
self.identityImage = IdentityImage()
|
||||||
|
self.identityImageChanged()
|
||||||
|
|
||||||
proc username*(self: ProfileInfoView): string {.slot.} = result = self.username
|
proc username*(self: ProfileInfoView): string {.slot.} = result = self.username
|
||||||
QtProperty[string] username:
|
QtProperty[string] username:
|
||||||
read = username
|
read = username
|
||||||
|
@ -59,6 +74,30 @@ QtObject:
|
||||||
read = identicon
|
read = identicon
|
||||||
notify = profileChanged
|
notify = profileChanged
|
||||||
|
|
||||||
|
proc thumbnailImage*(self: ProfileInfoView): string {.slot.} =
|
||||||
|
if (?.self.identityImage.thumbnail != ""):
|
||||||
|
result = self.identityImage.thumbnail
|
||||||
|
else:
|
||||||
|
result = self.identicon
|
||||||
|
QtProperty[string] thumbnailImage:
|
||||||
|
read = thumbnailImage
|
||||||
|
notify = identityImageChanged
|
||||||
|
|
||||||
|
proc largeImage*(self: ProfileInfoView): string {.slot.} =
|
||||||
|
if (?.self.identityImage.large != ""):
|
||||||
|
result = self.identityImage.large
|
||||||
|
else:
|
||||||
|
result = self.identicon
|
||||||
|
QtProperty[string] largeImage:
|
||||||
|
read = largeImage
|
||||||
|
notify = identityImageChanged
|
||||||
|
|
||||||
|
proc hasIdentityImage*(self: ProfileInfoView): bool {.slot.} =
|
||||||
|
result = (?.self.identityImage.thumbnail != "")
|
||||||
|
QtProperty[bool] hasIdentityImage:
|
||||||
|
read = hasIdentityImage
|
||||||
|
notify = identityImageChanged
|
||||||
|
|
||||||
proc pubKey*(self: ProfileInfoView): string {.slot.} = self.pubKey
|
proc pubKey*(self: ProfileInfoView): string {.slot.} = self.pubKey
|
||||||
|
|
||||||
QtProperty[string] pubKey:
|
QtProperty[string] pubKey:
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import strutils, os
|
||||||
|
|
||||||
|
proc formatImagePath*(imagePath: string): string =
|
||||||
|
var image: string = replace(imagePath, "file://", "")
|
||||||
|
if defined(windows):
|
||||||
|
# Windows doesn't work with paths starting with a slash
|
||||||
|
image.removePrefix('/')
|
||||||
|
return image
|
|
@ -19,7 +19,7 @@ proc generateAddresses*(self: AccountModel): seq[GeneratedAccount] =
|
||||||
var accounts = status_accounts.generateAddresses()
|
var accounts = status_accounts.generateAddresses()
|
||||||
for account in accounts.mitems:
|
for account in accounts.mitems:
|
||||||
account.name = status_accounts.generateAlias(account.derived.whisper.publicKey)
|
account.name = status_accounts.generateAlias(account.derived.whisper.publicKey)
|
||||||
account.photoPath = status_accounts.generateIdenticon(account.derived.whisper.publicKey)
|
account.identicon = status_accounts.generateIdenticon(account.derived.whisper.publicKey)
|
||||||
self.generatedAddresses.add(account)
|
self.generatedAddresses.add(account)
|
||||||
result = self.generatedAddresses
|
result = self.generatedAddresses
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ proc importMnemonic*(self: AccountModel, mnemonic: string): GeneratedAccount =
|
||||||
let importedAccount = status_accounts.multiAccountImportMnemonic(mnemonic)
|
let importedAccount = status_accounts.multiAccountImportMnemonic(mnemonic)
|
||||||
importedAccount.derived = status_accounts.deriveAccounts(importedAccount.id)
|
importedAccount.derived = status_accounts.deriveAccounts(importedAccount.id)
|
||||||
importedAccount.name = status_accounts.generateAlias(importedAccount.derived.whisper.publicKey)
|
importedAccount.name = status_accounts.generateAlias(importedAccount.derived.whisper.publicKey)
|
||||||
importedAccount.photoPath = status_accounts.generateIdenticon(importedAccount.derived.whisper.publicKey)
|
importedAccount.identicon = status_accounts.generateIdenticon(importedAccount.derived.whisper.publicKey)
|
||||||
result = importedAccount
|
result = importedAccount
|
||||||
|
|
||||||
proc reset*(self: AccountModel) =
|
proc reset*(self: AccountModel) =
|
||||||
|
|
|
@ -67,9 +67,34 @@ proc initNode*() =
|
||||||
|
|
||||||
discard $nim_status.initKeystore(KEYSTOREDIR)
|
discard $nim_status.initKeystore(KEYSTOREDIR)
|
||||||
|
|
||||||
|
proc parseIdentityImage*(images: JsonNode): IdentityImage =
|
||||||
|
result = IdentityImage()
|
||||||
|
if (images.kind != JNull):
|
||||||
|
for image in images:
|
||||||
|
if (image["type"].getStr == "thumbnail"):
|
||||||
|
# TODO check if this can be url or if it's always uri
|
||||||
|
result.thumbnail = image["uri"].getStr
|
||||||
|
elif (image["type"].getStr == "large"):
|
||||||
|
result.large = image["uri"].getStr
|
||||||
|
|
||||||
proc openAccounts*(): seq[NodeAccount] =
|
proc openAccounts*(): seq[NodeAccount] =
|
||||||
let strNodeAccounts = $nim_status.openAccounts(DATADIR)
|
let strNodeAccounts = nim_status.openAccounts(DATADIR).parseJson
|
||||||
result = Json.decode(strNodeAccounts, seq[NodeAccount])
|
# FIXME fix serialization
|
||||||
|
result = @[]
|
||||||
|
if (strNodeAccounts.kind != JNull):
|
||||||
|
for account in strNodeAccounts:
|
||||||
|
let nodeAccount = NodeAccount(
|
||||||
|
name: account["name"].getStr,
|
||||||
|
timestamp: account["timestamp"].getInt,
|
||||||
|
keyUid: account["key-uid"].getStr,
|
||||||
|
identicon: account["identicon"].getStr,
|
||||||
|
keycardPairing: account["keycard-pairing"].getStr
|
||||||
|
)
|
||||||
|
if (account{"images"}.kind != JNull):
|
||||||
|
nodeAccount.identityImage = parseIdentityImage(account["images"])
|
||||||
|
|
||||||
|
result.add(nodeAccount)
|
||||||
|
|
||||||
|
|
||||||
proc saveAccountAndLogin*(
|
proc saveAccountAndLogin*(
|
||||||
account: GeneratedAccount,
|
account: GeneratedAccount,
|
||||||
|
@ -91,7 +116,7 @@ proc saveAccountAndLogin*(
|
||||||
"public-key": account.derived.whisper.publicKey,
|
"public-key": account.derived.whisper.publicKey,
|
||||||
"address": account.derived.whisper.address,
|
"address": account.derived.whisper.address,
|
||||||
"name": account.name,
|
"name": account.name,
|
||||||
"photo-path": account.photoPath,
|
"identicon": account.identicon,
|
||||||
"path": constants.PATH_WHISPER,
|
"path": constants.PATH_WHISPER,
|
||||||
"chat": true
|
"chat": true
|
||||||
}
|
}
|
||||||
|
@ -127,7 +152,7 @@ proc getAccountData*(account: GeneratedAccount): JsonNode =
|
||||||
result = %* {
|
result = %* {
|
||||||
"name": account.name,
|
"name": account.name,
|
||||||
"address": account.address,
|
"address": account.address,
|
||||||
"photo-path": account.photoPath,
|
"identicon": account.identicon,
|
||||||
"key-uid": account.keyUid,
|
"key-uid": account.keyUid,
|
||||||
"keycard-pairing": nil
|
"keycard-pairing": nil
|
||||||
}
|
}
|
||||||
|
@ -148,7 +173,7 @@ proc getAccountSettings*(account: GeneratedAccount, defaultNetworks: JsonNode, i
|
||||||
"latest-derived-path": 0,
|
"latest-derived-path": 0,
|
||||||
"networks/networks": defaultNetworks,
|
"networks/networks": defaultNetworks,
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"photo-path": account.photoPath,
|
"identicon": account.identicon,
|
||||||
"waku-enabled": true,
|
"waku-enabled": true,
|
||||||
"wallet/visible-tokens": {
|
"wallet/visible-tokens": {
|
||||||
"mainnet": ["SNT"]
|
"mainnet": ["SNT"]
|
||||||
|
@ -336,3 +361,22 @@ proc deriveAccounts*(accountId: string): MultiAccounts =
|
||||||
|
|
||||||
proc logout*(): StatusGoError =
|
proc logout*(): StatusGoError =
|
||||||
result = Json.decode($nim_status.logout(), StatusGoError)
|
result = Json.decode($nim_status.logout(), StatusGoError)
|
||||||
|
|
||||||
|
proc storeIdentityImage*(keyUID: string, imagePath: string, aX, aY, bX, bY: int): IdentityImage =
|
||||||
|
let response = callPrivateRPC("multiaccounts_storeIdentityImage", %* [keyUID, imagePath, aX, aY, bX, bY]).parseJson
|
||||||
|
result = parseIdentityImage(response{"result"})
|
||||||
|
|
||||||
|
proc getIdentityImage*(keyUID: string): IdentityImage =
|
||||||
|
try:
|
||||||
|
let response = callPrivateRPC("multiaccounts_getIdentityImages", %* [keyUID]).parseJson
|
||||||
|
result = parseIdentityImage(response{"result"})
|
||||||
|
except Exception as e:
|
||||||
|
error "Error getting identity image", msg=e.msg
|
||||||
|
|
||||||
|
proc deleteIdentityImage*(keyUID: string): string =
|
||||||
|
try:
|
||||||
|
let response = callPrivateRPC("multiaccounts_deleteIdentityImage", %* [keyUID]).parseJson
|
||||||
|
result = ""
|
||||||
|
except Exception as e:
|
||||||
|
error "Error getting identity image", msg=e.msg
|
||||||
|
result = e.msg
|
||||||
|
|
|
@ -15,6 +15,7 @@ proc getOurInstallations*(useCached: bool = true): JsonNode =
|
||||||
result = installations
|
result = installations
|
||||||
|
|
||||||
proc syncDevices*(preferredName: string): string =
|
proc syncDevices*(preferredName: string): string =
|
||||||
|
# TODO change this to identicon when status-go is updated
|
||||||
let photoPath = ""
|
let photoPath = ""
|
||||||
result = callPrivateRPC("syncDevices".prefix, %* [preferredName, photoPath])
|
result = callPrivateRPC("syncDevices".prefix, %* [preferredName, photoPath])
|
||||||
|
|
||||||
|
|
|
@ -42,12 +42,17 @@ type MultiAccounts* = object
|
||||||
defaultWallet* {.serializedFieldName(PATH_DEFAULT_WALLET).}: DerivedAccount
|
defaultWallet* {.serializedFieldName(PATH_DEFAULT_WALLET).}: DerivedAccount
|
||||||
eip1581* {.serializedFieldName(PATH_EIP_1581).}: DerivedAccount
|
eip1581* {.serializedFieldName(PATH_EIP_1581).}: DerivedAccount
|
||||||
|
|
||||||
|
type
|
||||||
|
IdentityImage* = ref object
|
||||||
|
thumbnail*: string
|
||||||
|
large*: string
|
||||||
|
|
||||||
type
|
type
|
||||||
Account* = ref object of RootObj
|
Account* = ref object of RootObj
|
||||||
name*: string
|
name*: string
|
||||||
keyUid* {.serializedFieldName("key-uid").}: string
|
keyUid* {.serializedFieldName("key-uid").}: string
|
||||||
photoPath* {.serializedFieldName("photo-path").}: string
|
identityImage*: IdentityImage
|
||||||
|
identicon*: string
|
||||||
|
|
||||||
type
|
type
|
||||||
NodeAccount* = ref object of Account
|
NodeAccount* = ref object of Account
|
||||||
|
@ -66,7 +71,8 @@ type
|
||||||
# serializedFieldName pragma would need to be different
|
# serializedFieldName pragma would need to be different
|
||||||
name*: string
|
name*: string
|
||||||
keyUid*: string
|
keyUid*: string
|
||||||
photoPath*: string
|
identicon*: string
|
||||||
|
identityImage*: IdentityImage
|
||||||
|
|
||||||
type RpcError* = ref object
|
type RpcError* = ref object
|
||||||
code*: int
|
code*: int
|
||||||
|
@ -88,10 +94,10 @@ type
|
||||||
error*: RpcError
|
error*: RpcError
|
||||||
|
|
||||||
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, identityImage: account.identityImage, identicon: account.identicon, keyUid: account.address)
|
||||||
|
|
||||||
proc toAccount*(account: NodeAccount): Account =
|
proc toAccount*(account: NodeAccount): Account =
|
||||||
result = Account(name: account.name, photoPath: account.photoPath, keyUid: account.keyUid)
|
result = Account(name: account.name, identityImage: account.identityImage, identicon: account.identicon, keyUid: account.keyUid)
|
||||||
|
|
||||||
type AccountArgs* = ref object of Args
|
type AccountArgs* = ref object of Args
|
||||||
account*: Account
|
account*: Account
|
||||||
|
@ -247,4 +253,4 @@ proc getNodes*(self: FleetConfig, fleet: Fleet, nodeType: FleetNodes = FleetNode
|
||||||
result = toSeq(self.fleet[$fleet][$nodeType].values)
|
result = toSeq(self.fleet[$fleet][$nodeType].values)
|
||||||
|
|
||||||
proc getMailservers*(self: FleetConfig, fleet: Fleet): Table[string, string] =
|
proc getMailservers*(self: FleetConfig, fleet: Fleet): Table[string, string] =
|
||||||
result = self.fleet[$fleet][$FleetNodes.Mailservers]
|
result = self.fleet[$fleet][$FleetNodes.Mailservers]
|
||||||
|
|
|
@ -17,3 +17,12 @@ proc logout*(self: ProfileModel) =
|
||||||
|
|
||||||
proc getLinkPreviewWhitelist*(self: ProfileModel): JsonNode =
|
proc getLinkPreviewWhitelist*(self: ProfileModel): JsonNode =
|
||||||
result = status_settings.getLinkPreviewWhitelist()
|
result = status_settings.getLinkPreviewWhitelist()
|
||||||
|
|
||||||
|
proc storeIdentityImage*(self: ProfileModel, keyUID: string, imagePath: string, aX, aY, bX, bY: int): IdentityImage =
|
||||||
|
result = status_accounts.storeIdentityImage(keyUID, imagePath, aX, aY, bX, bY)
|
||||||
|
|
||||||
|
proc getIdentityImage*(self: ProfileModel, keyUID: string): IdentityImage =
|
||||||
|
result = status_accounts.getIdentityImage(keyUID)
|
||||||
|
|
||||||
|
proc deleteIdentityImage*(self: ProfileModel, keyUID: string): string =
|
||||||
|
result = status_accounts.deleteIdentityImage(keyUID)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import ../libstatus/types
|
||||||
type Profile* = ref object
|
type Profile* = ref object
|
||||||
id*, alias*, username*, identicon*, address*, ensName*, localNickname*: string
|
id*, alias*, username*, identicon*, address*, ensName*, localNickname*: string
|
||||||
ensVerified*: bool
|
ensVerified*: bool
|
||||||
|
identityImage*: IdentityImage
|
||||||
ensVerifiedAt*, ensVerificationRetries*, appearance*: int
|
ensVerifiedAt*, ensVerificationRetries*, appearance*: int
|
||||||
systemTags*: seq[string]
|
systemTags*: seq[string]
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ proc toProfileModel*(account: Account): Profile =
|
||||||
result = Profile(
|
result = Profile(
|
||||||
id: "",
|
id: "",
|
||||||
username: account.name,
|
username: account.name,
|
||||||
identicon: account.photoPath,
|
identicon: account.identicon,
|
||||||
alias: account.name,
|
alias: account.name,
|
||||||
ensName: "",
|
ensName: "",
|
||||||
ensVerified: false,
|
ensVerified: false,
|
||||||
|
@ -36,6 +37,7 @@ proc toProfileModel*(profile: JsonNode): Profile =
|
||||||
id: profile["id"].str,
|
id: profile["id"].str,
|
||||||
username: profile["alias"].str,
|
username: profile["alias"].str,
|
||||||
identicon: profile["identicon"].str,
|
identicon: profile["identicon"].str,
|
||||||
|
identityImage: IdentityImage(),
|
||||||
address: profile["id"].str,
|
address: profile["id"].str,
|
||||||
alias: profile["alias"].str,
|
alias: profile["alias"].str,
|
||||||
ensName: "",
|
ensName: "",
|
||||||
|
@ -51,3 +53,8 @@ proc toProfileModel*(profile: JsonNode): Profile =
|
||||||
|
|
||||||
if profile.hasKey("localNickname"):
|
if profile.hasKey("localNickname"):
|
||||||
result.localNickname = profile["localNickname"].str
|
result.localNickname = profile["localNickname"].str
|
||||||
|
if profile.hasKey("images"):
|
||||||
|
if profile["images"].hasKey("thumbnail"):
|
||||||
|
result.identityImage.thumbnail = profile["images"]["thumbnail"]["uri"].str
|
||||||
|
if profile["images"].hasKey("large"):
|
||||||
|
result.identityImage.large = profile["images"]["large"]["uri"].str
|
||||||
|
|
|
@ -41,7 +41,7 @@ Item {
|
||||||
onLinkActivated: function (linkClicked) {
|
onLinkActivated: function (linkClicked) {
|
||||||
switch (linkClicked) {
|
switch (linkClicked) {
|
||||||
case "shareKey":
|
case "shareKey":
|
||||||
openProfilePopup(profileModel.profile.username, profileModel.profile.pubKey, profileModel.profile.identicon);
|
openProfilePopup(profileModel.profile.username, profileModel.profile.pubKey, profileModel.profile.thumbnailImage);
|
||||||
break;
|
break;
|
||||||
case "invite": inviteFriendsPopup.open(); break;
|
case "invite": inviteFriendsPopup.open(); break;
|
||||||
default: //no idea what was clicked
|
default: //no idea what was clicked
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import QtQuick 2.3
|
import QtQuick 2.13
|
||||||
import "../../../../shared"
|
import "../../../../shared"
|
||||||
import "../../../../imports"
|
import "../../../../imports"
|
||||||
import "./MessageComponents"
|
import "./MessageComponents"
|
||||||
|
@ -25,6 +25,7 @@ Item {
|
||||||
property bool timeout: false
|
property bool timeout: false
|
||||||
property string linkUrls: ""
|
property string linkUrls: ""
|
||||||
property string imageUrls: ""
|
property string imageUrls: ""
|
||||||
|
property bool placeholderMessage: false
|
||||||
|
|
||||||
property string authorCurrentMsg: "authorCurrentMsg"
|
property string authorCurrentMsg: "authorCurrentMsg"
|
||||||
property string authorPrevMsg: "authorPrevMsg"
|
property string authorPrevMsg: "authorPrevMsg"
|
||||||
|
@ -48,6 +49,25 @@ Item {
|
||||||
|
|
||||||
property var imageClick: function () {}
|
property var imageClick: function () {}
|
||||||
property var scrollToBottom: function () {}
|
property var scrollToBottom: function () {}
|
||||||
|
property string userPubKey: {
|
||||||
|
if (contentType === Constants.chatIdentifier) {
|
||||||
|
return chatId
|
||||||
|
}
|
||||||
|
return fromAuthor
|
||||||
|
}
|
||||||
|
property bool useLargeImage: contentType === Constants.chatIdentifier
|
||||||
|
|
||||||
|
property string profileImageSource: !placeholderMessage && chatView.getProfileImage(userPubKey, isCurrentUser, useLargeImage) || ""
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
enabled: !placeholderMessage
|
||||||
|
target: profileModel.contacts.list
|
||||||
|
onContactChanged: {
|
||||||
|
if (pubkey === fromAuthor) {
|
||||||
|
profileImageSource = chatView.getProfileImage(userPubKey, isCurrentUser, useLargeImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
@ -82,13 +102,13 @@ Item {
|
||||||
messageContextMenu.isProfile = !!isProfileClick
|
messageContextMenu.isProfile = !!isProfileClick
|
||||||
messageContextMenu.isSticker = isSticker
|
messageContextMenu.isSticker = isSticker
|
||||||
messageContextMenu.emojiOnly = emojiOnly
|
messageContextMenu.emojiOnly = emojiOnly
|
||||||
messageContextMenu.show(userName, fromAuthor, identicon, "", nickname)
|
messageContextMenu.show(userName, fromAuthor, root.profileImageSource || identicon, "", nickname)
|
||||||
// Position the center of the menu where the mouse is
|
// Position the center of the menu where the mouse is
|
||||||
messageContextMenu.x = messageContextMenu.x - messageContextMenu.width / 2
|
messageContextMenu.x = messageContextMenu.x - messageContextMenu.width / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
active :true
|
active: true
|
||||||
width: parent.width
|
width: parent.width
|
||||||
sourceComponent: {
|
sourceComponent: {
|
||||||
switch(contentType) {
|
switch(contentType) {
|
||||||
|
@ -165,6 +185,7 @@ Item {
|
||||||
id: channelIdentifierComponent
|
id: channelIdentifierComponent
|
||||||
ChannelIdentifier {
|
ChannelIdentifier {
|
||||||
authorCurrentMsg: root.authorCurrentMsg
|
authorCurrentMsg: root.authorCurrentMsg
|
||||||
|
profileImage: profileImageSource
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +194,7 @@ Item {
|
||||||
id: privateGroupHeaderComponent
|
id: privateGroupHeaderComponent
|
||||||
StyledText {
|
StyledText {
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
text: {
|
text: {
|
||||||
return `<html>`+
|
return `<html>`+
|
||||||
`<head>`+
|
`<head>`+
|
||||||
`<style type="text/css">`+
|
`<style type="text/css">`+
|
||||||
|
|
|
@ -6,8 +6,10 @@ Item {
|
||||||
property string authorCurrentMsg: "authorCurrentMsg"
|
property string authorCurrentMsg: "authorCurrentMsg"
|
||||||
property int verticalMargin: 50
|
property int verticalMargin: 50
|
||||||
|
|
||||||
|
property string profileImage
|
||||||
|
|
||||||
id: channelIdentifier
|
id: channelIdentifier
|
||||||
visible: authorCurrentMsg == ""
|
visible: authorCurrentMsg === ""
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: this.visible ? verticalMargin : 0
|
anchors.topMargin: this.visible ? verticalMargin : 0
|
||||||
|
@ -28,15 +30,13 @@ Item {
|
||||||
return chatsModel.activeChannel.color
|
return chatsModel.activeChannel.color
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
RoundedImage {
|
||||||
visible: chatsModel.activeChannel.chatType === Constants.chatTypeOneToOne
|
visible: chatsModel.activeChannel.chatType === Constants.chatTypeOneToOne
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: 120
|
width: 120
|
||||||
height: 120
|
height: 120
|
||||||
fillMode: Image.PreserveAspectFit
|
source: channelIdentifier.profileImage || chatsModel.activeChannel.identicon
|
||||||
source: chatsModel.activeChannel.identicon
|
|
||||||
mipmap: true
|
|
||||||
smooth: false
|
smooth: false
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ Item {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Item {
|
||||||
id: channelDescription
|
id: channelDescription
|
||||||
visible: descText.visible
|
visible: descText.visible
|
||||||
width: visible ? 330 : 0
|
width: visible ? 330 : 0
|
||||||
|
@ -79,7 +79,6 @@ Item {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.top: channelName.bottom
|
anchors.top: channelName.bottom
|
||||||
anchors.topMargin: 16
|
anchors.topMargin: 16
|
||||||
color: Style.current.transparent
|
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
id: descText
|
id: descText
|
||||||
|
|
|
@ -50,7 +50,8 @@ Item {
|
||||||
|
|
||||||
if (link.startsWith('//')) {
|
if (link.startsWith('//')) {
|
||||||
let pk = link.replace("//", "");
|
let pk = link.replace("//", "");
|
||||||
openProfilePopup(chatsModel.userNameOrAlias(pk), pk, utilsModel.generateIdenticon(pk))
|
const userProfileImage = chatView.getProfileImage(pk)
|
||||||
|
openProfilePopup(chatsModel.userNameOrAlias(pk), pk, userProfileImage || utilsModel.generateIdenticon(pk))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,21 +7,24 @@ Loader {
|
||||||
height: active ? item.height : 0
|
height: active ? item.height : 0
|
||||||
|
|
||||||
sourceComponent: Component {
|
sourceComponent: Component {
|
||||||
Rectangle {
|
Item {
|
||||||
id: chatImage
|
id: chatImage
|
||||||
width: identiconImage.width
|
width: identiconImage.width
|
||||||
height: identiconImage.height
|
height: identiconImage.height
|
||||||
border.width: 1
|
|
||||||
border.color: Style.current.border
|
|
||||||
radius: 50
|
|
||||||
|
|
||||||
Image {
|
RoundedImage {
|
||||||
id: identiconImage
|
id: identiconImage
|
||||||
width: 36
|
width: 36
|
||||||
height: 36
|
height: 36
|
||||||
fillMode: Image.PreserveAspectFit
|
border.width: 1
|
||||||
source: !isCurrentUser ? identicon : profileModel.profile.identicon
|
border.color: Style.current.border
|
||||||
mipmap: true
|
source: {
|
||||||
|
if (profileImageSource) {
|
||||||
|
return profileImageSource
|
||||||
|
}
|
||||||
|
|
||||||
|
return !isCurrentUser ? identicon : profileModel.profile.identicon
|
||||||
|
}
|
||||||
smooth: false
|
smooth: false
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,8 @@ Rectangle {
|
||||||
groupInfoPopup.open()
|
groupInfoPopup.open()
|
||||||
break;
|
break;
|
||||||
case Constants.chatTypeOneToOne:
|
case Constants.chatTypeOneToOne:
|
||||||
openProfilePopup(chatsModel.activeChannel.name, chatsModel.activeChannel.id, chatsModel.activeChannel.identicon)
|
const profileImage = chatView.getProfileImage(chatsModel.activeChannel.id)
|
||||||
|
openProfilePopup(chatsModel.activeChannel.name, chatsModel.activeChannel.id, profileImage || chatsModel.activeChannel.identicon)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,19 @@ SplitView {
|
||||||
chatGroupsListViewCount: contactsColumn.chatGroupsListViewCount
|
chatGroupsListViewCount: contactsColumn.chatGroupsListViewCount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getProfileImage(pubkey, isCurrentUser, useLargeImage) {
|
||||||
|
if (isCurrentUser || (isCurrentUser === undefined && pubkey === profileModel.profile.pubKey)) {
|
||||||
|
return profileModel.profile.thumbnailImage
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = profileModel.contacts.list.getContactIndexByPubkey(pubkey)
|
||||||
|
if (index === -1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return profileModel.contacts.list.rowData(index, useLargeImage ? "largeImage" : "thumbnailImage")
|
||||||
|
}
|
||||||
|
|
||||||
function openProfilePopup(userNameParam, fromAuthorParam, identiconParam, textParam, nicknameParam, parentPopup){
|
function openProfilePopup(userNameParam, fromAuthorParam, identiconParam, textParam, nicknameParam, parentPopup){
|
||||||
var popup = profilePopupComponent.createObject(chatView);
|
var popup = profilePopupComponent.createObject(chatView);
|
||||||
if(parentPopup){
|
if(parentPopup){
|
||||||
|
|
|
@ -20,6 +20,18 @@ Rectangle {
|
||||||
property bool muted: false
|
property bool muted: false
|
||||||
property bool hovered: false
|
property bool hovered: false
|
||||||
|
|
||||||
|
property string profileImage: chatType === Constants.chatTypeOneToOne ? chatView.getProfileImage(chatId) || "" : ""
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
enabled: chatType === Constants.chatTypeOneToOne
|
||||||
|
target: profileModel.contacts.list
|
||||||
|
onContactChanged: {
|
||||||
|
if (pubkey === wrapper.chatId) {
|
||||||
|
wrapper.profileImage = chatView.getProfileImage(wrapper.chatId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
id: wrapper
|
id: wrapper
|
||||||
color: {
|
color: {
|
||||||
if (ListView.isCurrentItem || wrapper.hovered) {
|
if (ListView.isCurrentItem || wrapper.hovered) {
|
||||||
|
@ -42,7 +54,7 @@ Rectangle {
|
||||||
width: !isCompact ? 40 : 20
|
width: !isCompact ? 40 : 20
|
||||||
chatName: wrapper.name
|
chatName: wrapper.name
|
||||||
chatType: wrapper.chatType
|
chatType: wrapper.chatType
|
||||||
identicon: wrapper.identicon
|
identicon: wrapper.profileImage || wrapper.identicon
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: !isCompact ? Style.current.padding : Style.current.smallPadding
|
anchors.leftMargin: !isCompact ? Style.current.padding : Style.current.smallPadding
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
|
@ -156,7 +156,8 @@ ScrollView {
|
||||||
chatsModel.setActiveChannelByIndex(channelContextMenu.channelIndex)
|
chatsModel.setActiveChannelByIndex(channelContextMenu.channelIndex)
|
||||||
chatGroupsListView.currentIndex = channelContextMenu.channelIndex
|
chatGroupsListView.currentIndex = channelContextMenu.channelIndex
|
||||||
if (channelContextMenu.chatType === Constants.chatTypeOneToOne) {
|
if (channelContextMenu.chatType === Constants.chatTypeOneToOne) {
|
||||||
return openProfilePopup(channelContextMenu.chatName, channelContextMenu.chatId, channelContextMenu.chatIdenticon)
|
const userProfileImage = chatView.getProfileImage(channelContextMenu.chatId)
|
||||||
|
return openProfilePopup(channelContextMenu.chatName, channelContextMenu.chatId, userProfileImage || channelContextMenu.chatIdenticon)
|
||||||
}
|
}
|
||||||
if (channelContextMenu.chatType === Constants.chatTypePrivateGroupChat) {
|
if (channelContextMenu.chatType === Constants.chatTypePrivateGroupChat) {
|
||||||
return groupInfoPopup.open()
|
return groupInfoPopup.open()
|
||||||
|
|
|
@ -37,6 +37,7 @@ ModalPopup {
|
||||||
pubKey: profileModel.contacts.list.rowData(i, "pubKey"),
|
pubKey: profileModel.contacts.list.rowData(i, "pubKey"),
|
||||||
address: profileModel.contacts.list.rowData(i, "address"),
|
address: profileModel.contacts.list.rowData(i, "address"),
|
||||||
identicon: profileModel.contacts.list.rowData(i, "identicon"),
|
identicon: profileModel.contacts.list.rowData(i, "identicon"),
|
||||||
|
thumbnailImage: profileModel.contacts.list.rowData(i, "thumbnailImage"),
|
||||||
isUser: false,
|
isUser: false,
|
||||||
isContact: profileModel.contacts.list.rowData(i, "isContact") !== "false"
|
isContact: profileModel.contacts.list.rowData(i, "isContact") !== "false"
|
||||||
});
|
});
|
||||||
|
@ -47,6 +48,7 @@ ModalPopup {
|
||||||
pubKey: profileModel.profile.pubKey,
|
pubKey: profileModel.profile.pubKey,
|
||||||
address: "",
|
address: "",
|
||||||
identicon: profileModel.profile.identicon,
|
identicon: profileModel.profile.identicon,
|
||||||
|
thumbnailImage: profileModel.profile.thumbnailImage,
|
||||||
isUser: true
|
isUser: true
|
||||||
});
|
});
|
||||||
noContactsRect.visible = !profileModel.contacts.list.hasAddedContacts();
|
noContactsRect.visible = !profileModel.contacts.list.hasAddedContacts();
|
||||||
|
@ -186,7 +188,7 @@ ModalPopup {
|
||||||
name: !model.name.endsWith(".eth") && !!model.localNickname ?
|
name: !model.name.endsWith(".eth") && !!model.localNickname ?
|
||||||
model.localNickname : Utils.removeStatusEns(model.name)
|
model.localNickname : Utils.removeStatusEns(model.name)
|
||||||
address: model.address
|
address: model.address
|
||||||
identicon: model.identicon
|
identicon: model.thumbnailImage || model.identicon
|
||||||
onItemChecked: function(pubKey, itemChecked){
|
onItemChecked: function(pubKey, itemChecked){
|
||||||
var idx = pubKeys.indexOf(pubKey)
|
var idx = pubKeys.indexOf(pubKey)
|
||||||
if(itemChecked){
|
if(itemChecked){
|
||||||
|
|
|
@ -30,6 +30,7 @@ ModalPopup {
|
||||||
pubKey: profileModel.contacts.list.rowData(i, "pubKey"),
|
pubKey: profileModel.contacts.list.rowData(i, "pubKey"),
|
||||||
address: profileModel.contacts.list.rowData(i, "address"),
|
address: profileModel.contacts.list.rowData(i, "address"),
|
||||||
identicon: profileModel.contacts.list.rowData(i, "identicon"),
|
identicon: profileModel.contacts.list.rowData(i, "identicon"),
|
||||||
|
thumbnailImage: profileModel.contacts.list.rowData(i, "thumbnailImage"),
|
||||||
isUser: false
|
isUser: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -211,7 +212,7 @@ ModalPopup {
|
||||||
name: model.name.endsWith(".eth") || !model.localNickname ?
|
name: model.name.endsWith(".eth") || !model.localNickname ?
|
||||||
Utils.removeStatusEns(model.name) : model.localNickname
|
Utils.removeStatusEns(model.name) : model.localNickname
|
||||||
address: model.address
|
address: model.address
|
||||||
identicon: model.identicon
|
identicon: model.thumbnailImage || model.identicon
|
||||||
onItemChecked: function(pubKey, itemChecked){
|
onItemChecked: function(pubKey, itemChecked){
|
||||||
var idx = pubKeys.indexOf(pubKey)
|
var idx = pubKeys.indexOf(pubKey)
|
||||||
if(itemChecked){
|
if(itemChecked){
|
||||||
|
@ -278,7 +279,7 @@ ModalPopup {
|
||||||
StatusImageIdenticon {
|
StatusImageIdenticon {
|
||||||
id: identicon
|
id: identicon
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
source: model.identicon
|
source: chatView.getProfileImage(model.pubKey)|| model.identicon
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
|
@ -293,7 +294,10 @@ ModalPopup {
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: openProfilePopup(model.userName, model.pubKey, model.identicon, '', contactRow.nickname)
|
onClicked: {
|
||||||
|
const userProfileImage = chatView.getProfileImage(model.pubKey)
|
||||||
|
openProfilePopup(model.userName, model.pubKey, userProfileImage || model.identicon, '', contactRow.nickname)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ PopupMenu {
|
||||||
property var fromAuthor: ""
|
property var fromAuthor: ""
|
||||||
property var text: ""
|
property var text: ""
|
||||||
|
|
||||||
function show(userNameParam, fromAuthorParam, identiconParam, textParam, nicknameParam){
|
function show(userNameParam, fromAuthorParam, identiconParam, textParam, nicknameParam) {
|
||||||
userName = userNameParam || ""
|
userName = userNameParam || ""
|
||||||
nickname = nicknameParam || ""
|
nickname = nicknameParam || ""
|
||||||
fromAuthor = fromAuthorParam || ""
|
fromAuthor = fromAuthorParam || ""
|
||||||
|
|
|
@ -138,7 +138,7 @@ ModalPopup {
|
||||||
isUser: false
|
isUser: false
|
||||||
name: model.name
|
name: model.name
|
||||||
address: model.address
|
address: model.address
|
||||||
identicon: model.identicon
|
identicon: model.thumbnailImage || model.identicon
|
||||||
showListSelector: true
|
showListSelector: true
|
||||||
onItemChecked: function(pubKey, itemChecked){
|
onItemChecked: function(pubKey, itemChecked){
|
||||||
chatsModel.joinChat(pubKey, Constants.chatTypeOneToOne);
|
chatsModel.joinChat(pubKey, Constants.chatTypeOneToOne);
|
||||||
|
|
|
@ -49,21 +49,14 @@ ModalPopup {
|
||||||
header: Item {
|
header: Item {
|
||||||
height: children[0].height
|
height: children[0].height
|
||||||
width: parent.width
|
width: parent.width
|
||||||
Rectangle {
|
RoundedImage {
|
||||||
id: profilePic
|
id: profilePic
|
||||||
width: 40
|
width: 40
|
||||||
height: 40
|
height: 40
|
||||||
radius: 30
|
border.color: Style.current.border
|
||||||
border.color: "#10000000"
|
|
||||||
border.width: 1
|
border.width: 1
|
||||||
color: Style.current.transparent
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
SVGImage {
|
source: identicon
|
||||||
width: parent.width
|
|
||||||
height: parent.height
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
source: identicon
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledTextEdit {
|
StyledTextEdit {
|
||||||
|
|
|
@ -45,12 +45,7 @@ SplitView {
|
||||||
|
|
||||||
// This list needs to match LeftTab/constants.js
|
// This list needs to match LeftTab/constants.js
|
||||||
// Would be better if we could make them match automatically
|
// Would be better if we could make them match automatically
|
||||||
MyProfileContainer {
|
MyProfileContainer {}
|
||||||
username: profileModel.profile.username
|
|
||||||
ensName: profileModel.ens.preferredUsername
|
|
||||||
identicon: profileModel.profile.identicon
|
|
||||||
pubkey: profileModel.profile.pubKey
|
|
||||||
}
|
|
||||||
|
|
||||||
onCurrentIndexChanged: {
|
onCurrentIndexChanged: {
|
||||||
if(visibleChildren[0] === ensContainer){
|
if(visibleChildren[0] === ensContainer){
|
||||||
|
|
|
@ -81,7 +81,7 @@ ScrollView {
|
||||||
identicon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAb0lEQVR4Ae3UQQqAIBRF0Wj9ba9Bq6l5JBQqfn/ngDMH3YS3AAB/tO3H+XRG3b9bR/+gVoREI2RapVXpfd5+X5oXERKNkHS+rk3tOpWkeREh0QiZVu91ql2zNC8iJBoh0yqtSqt1slpCghICANDPBc0ESPh0bHkHAAAAAElFTkSuQmCC"
|
identicon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAb0lEQVR4Ae3UQQqAIBRF0Wj9ba9Bq6l5JBQqfn/ngDMH3YS3AAB/tO3H+XRG3b9bR/+gVoREI2RapVXpfd5+X5oXERKNkHS+rk3tOpWkeREh0QiZVu91ql2zNC8iJBoh0yqtSqt1slpCghICANDPBc0ESPh0bHkHAAAAAElFTkSuQmCC"
|
||||||
message: qsTr("Blockchains will drop search costs, causing a kind of decomposition that allows you to have markets of entities that are horizontally segregated and vertically segregated.")
|
message: qsTr("Blockchains will drop search costs, causing a kind of decomposition that allows you to have markets of entities that are horizontally segregated and vertically segregated.")
|
||||||
contentType: Constants.messageType
|
contentType: Constants.messageType
|
||||||
|
placeholderMessage: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Dialogs 1.3
|
||||||
|
import "../../../../imports"
|
||||||
|
import "../../../../shared"
|
||||||
|
import "../../../../shared/status"
|
||||||
|
|
||||||
|
ModalPopup {
|
||||||
|
property string selectedImage // selectedImage is for us to be able to analyze it before setting it as current
|
||||||
|
property string uploadError
|
||||||
|
|
||||||
|
id: popup
|
||||||
|
|
||||||
|
title: qsTr("Profile picture")
|
||||||
|
|
||||||
|
onClosed: {
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelectedImageChanged: {
|
||||||
|
if (!selectedImage) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cropImageModal.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
RoundedImage {
|
||||||
|
id: profilePic
|
||||||
|
source: profileModel.profile.largeImage
|
||||||
|
width: 160
|
||||||
|
height: 160
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
border.width: 1
|
||||||
|
border.color: Style.current.border
|
||||||
|
onClicked: imageDialog.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
visible: !!uploadError
|
||||||
|
text: uploadError
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: profilePic.bottom
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
font.pixelSize: 13
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
anchors.topMargin: 13
|
||||||
|
font.weight: Font.Thin
|
||||||
|
color: Style.current.danger
|
||||||
|
}
|
||||||
|
|
||||||
|
ModalPopup {
|
||||||
|
id: cropImageModal
|
||||||
|
width: image.width + 50
|
||||||
|
height: image.height + 170
|
||||||
|
title: qsTr("Crop your image (optionnal)")
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: image
|
||||||
|
width: 400
|
||||||
|
source: popup.selectedImage
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageCropper {
|
||||||
|
id: imageCropper
|
||||||
|
x: image.x
|
||||||
|
y: image.y
|
||||||
|
image: image
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: StatusButton {
|
||||||
|
id: doUploadBtn
|
||||||
|
text: qsTr("Finish")
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
onClicked: {
|
||||||
|
const aXPercent = imageCropper.selectorRectangle.x / image.width
|
||||||
|
const aYPercent = imageCropper.selectorRectangle.y / image.height
|
||||||
|
const bXPercent = (imageCropper.selectorRectangle.x + imageCropper.selectorRectangle.width) / image.width
|
||||||
|
const bYPercent = (imageCropper.selectorRectangle.y + imageCropper.selectorRectangle.height) / image.height
|
||||||
|
|
||||||
|
|
||||||
|
const aX = Math.round(aXPercent * image.sourceSize.width)
|
||||||
|
const aY = Math.round(aYPercent * image.sourceSize.height)
|
||||||
|
|
||||||
|
const bX = Math.round(bXPercent * image.sourceSize.width)
|
||||||
|
const bY = Math.round(bYPercent * image.sourceSize.height)
|
||||||
|
|
||||||
|
uploadError = profileModel.uploadNewProfilePic(selectedImage, aX, aY, bX, bY)
|
||||||
|
cropImageModal.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: Item {
|
||||||
|
anchors.fill:parent
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
visible: profileModel.profile.hasIdentityImage
|
||||||
|
type: "secondary"
|
||||||
|
flat: true
|
||||||
|
color: Style.current.danger
|
||||||
|
text: qsTr("Remove")
|
||||||
|
anchors.right: uploadBtn.left
|
||||||
|
anchors.rightMargin: Style.current.padding
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
onClicked: {
|
||||||
|
uploadError = profileModel.deleteProfilePic()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
id: uploadBtn
|
||||||
|
text: qsTr("Upload")
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
onClicked: {
|
||||||
|
imageDialog.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
FileDialog {
|
||||||
|
id: imageDialog
|
||||||
|
//% "Please choose an image"
|
||||||
|
title: qsTrId("please-choose-an-image")
|
||||||
|
folder: shortcuts.pictures
|
||||||
|
nameFilters: [
|
||||||
|
//% "Image files (*.jpg *.jpeg *.png)"
|
||||||
|
qsTrId("image-files----jpg---jpeg---png-")
|
||||||
|
]
|
||||||
|
onAccepted: {
|
||||||
|
selectedImage = imageDialog.fileUrls[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*##^##
|
||||||
|
Designer {
|
||||||
|
D{i:0;autoSize:true;height:480;width:640}
|
||||||
|
}
|
||||||
|
##^##*/
|
|
@ -25,7 +25,7 @@ ListView {
|
||||||
name: Utils.removeStatusEns(model.name)
|
name: Utils.removeStatusEns(model.name)
|
||||||
address: model.address
|
address: model.address
|
||||||
localNickname: model.localNickname
|
localNickname: model.localNickname
|
||||||
identicon: model.identicon
|
identicon: model.thumbnailImage || model.identicon
|
||||||
isContact: model.isContact
|
isContact: model.isContact
|
||||||
isBlocked: model.isBlocked
|
isBlocked: model.isBlocked
|
||||||
selectable: contactList.selectable
|
selectable: contactList.selectable
|
||||||
|
|
|
@ -6,15 +6,19 @@ import "../../../../shared"
|
||||||
import "../../../../shared/status"
|
import "../../../../shared/status"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property string username: "Jotaro Kujo"
|
property string ensName: profileModel.profile.preferredUsername || ""
|
||||||
property string identicon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAhklEQVR4nOzWwQ1AQBgFYUQvelKHMtShJ9VwFyvrsExe5jvKXiYv+WPoQhhCYwiNITSG0MSEjLUPt3097r7P09L/8f4qZhFDaAyhqboIT76+TiUxixhCYwhN9b/WW6Xr1ErMIobQGEJjCI0hNIbQGEJjCI0haiRmEUNoDKExhMYQmjMAAP//B2kXcP2uDV8AAAAASUVORK5CYII="
|
property string username: profileModel.profile.username
|
||||||
property string pubkey: "0x04d8c07dd137bd1b73a6f51df148b4f77ddaa11209d36e43d8344c0a7d6db1cad6085f27cfb75dd3ae21d86ceffebe4cf8a35b9ce8d26baa19dc264efe6d8f221b"
|
property string pubkey: profileModel.profile.pubKey
|
||||||
property string ensName: "joestar.eth"
|
|
||||||
|
|
||||||
id: profileHeaderContent
|
id: profileHeaderContent
|
||||||
height: parent.height
|
height: parent.height
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: changeProfileModalComponent
|
||||||
|
ChangeProfilePicModal {}
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: profileImgNameContainer
|
id: profileImgNameContainer
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
|
@ -26,43 +30,58 @@ Item {
|
||||||
|
|
||||||
height: this.childrenRect.height
|
height: this.childrenRect.height
|
||||||
|
|
||||||
Rectangle {
|
Item {
|
||||||
id: profileImg
|
id: profileImgContainer
|
||||||
width: identiconImage.width
|
width: profileImg.width
|
||||||
height: identiconImage.height
|
height: profileImg.height
|
||||||
border.width: 1
|
|
||||||
border.color: Style.current.border
|
|
||||||
radius: 50
|
|
||||||
color: Style.current.background
|
|
||||||
|
|
||||||
Image {
|
RoundedImage {
|
||||||
id: identiconImage
|
id: profileImg
|
||||||
width: 60
|
width: 64
|
||||||
height: 60
|
height: 64
|
||||||
fillMode: Image.PreserveAspectFit
|
border.width: 1
|
||||||
source: identicon
|
border.color: Style.current.border
|
||||||
mipmap: true
|
source: profileModel.profile.thumbnailImage || ""
|
||||||
smooth: false
|
smooth: false
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RoundedIcon {
|
||||||
|
source: "../../../img/pencil.svg"
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: -3
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: -3
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
border.width: 1
|
||||||
|
border.color: Style.current.background
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
const popup = changeProfileModalComponent.createObject(profileHeaderContent);
|
||||||
|
popup.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
id: profileName
|
id: profileName
|
||||||
text: ensName !== "" ? ensName : username
|
text: ensName !== "" ? ensName : username
|
||||||
anchors.left: profileImg.right
|
anchors.left: profileImgContainer.right
|
||||||
anchors.leftMargin: 8
|
anchors.leftMargin: 8
|
||||||
anchors.top: profileImg.top
|
anchors.top: profileImgContainer.top
|
||||||
anchors.topMargin: 4
|
font.weight: Font.Medium
|
||||||
font.family: "Inter"
|
font.pixelSize: 15
|
||||||
font.weight: Font.Bold
|
|
||||||
font.pixelSize: 20
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Address {
|
Address {
|
||||||
id: pubkeyText
|
id: pubkeyText
|
||||||
text: ensName !== "" ? username : pubkey
|
text: ensName !== "" ? username : pubkey
|
||||||
anchors.bottom: profileImg.bottom
|
anchors.bottom: profileImgContainer.bottom
|
||||||
anchors.left: profileName.left
|
anchors.left: profileName.left
|
||||||
anchors.bottomMargin: 4
|
anchors.bottomMargin: 4
|
||||||
width: 200
|
width: 200
|
||||||
|
@ -85,7 +104,9 @@ Item {
|
||||||
|
|
||||||
Separator {
|
Separator {
|
||||||
id: lineSeparator
|
id: lineSeparator
|
||||||
anchors.top: profileImg.bottom
|
anchors.top: profileImgContainer.bottom
|
||||||
|
anchors.topMargin: 36
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2.10144 13.5959L2.79885 10.8062C2.83108 10.6773 2.87171 10.5512 2.92034 10.4285C2.9822 10.2724 3.18194 10.2423 3.30066 10.361L5.63945 12.6998C5.75817 12.8185 5.72809 13.0183 5.572 13.0801C5.44932 13.1288 5.32315 13.1694 5.19424 13.2016L2.40461 13.899C2.22152 13.9448 2.05567 13.779 2.10144 13.5959Z" fill="white"/>
|
||||||
|
<path d="M6.32361 11.2627C6.71413 11.6532 7.3473 11.6532 7.73782 11.2627L14.2933 4.7072C14.6838 4.31668 14.6838 3.68351 14.2933 3.29299L12.7075 1.7072C12.317 1.31668 11.6838 1.31668 11.2933 1.7072L4.73782 8.26266C4.3473 8.65318 4.3473 9.28635 4.73782 9.67687L6.32361 11.2627Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 720 B |
|
@ -182,6 +182,7 @@ DISTFILES += \
|
||||||
app/AppLayouts/Profile/Sections/AppearanceContainer.qml \
|
app/AppLayouts/Profile/Sections/AppearanceContainer.qml \
|
||||||
app/AppLayouts/Profile/Sections/BackupSeedModal.qml \
|
app/AppLayouts/Profile/Sections/BackupSeedModal.qml \
|
||||||
app/AppLayouts/Profile/Sections/BrowserContainer.qml \
|
app/AppLayouts/Profile/Sections/BrowserContainer.qml \
|
||||||
|
app/AppLayouts/Profile/Sections/ChangeProfilePicModal.qml \
|
||||||
app/AppLayouts/Profile/Sections/MyProfileContainer.qml \
|
app/AppLayouts/Profile/Sections/MyProfileContainer.qml \
|
||||||
app/AppLayouts/Profile/Sections/SoundsContainer.qml \
|
app/AppLayouts/Profile/Sections/SoundsContainer.qml \
|
||||||
app/AppLayouts/UIComponents/UIComponents.qml \
|
app/AppLayouts/UIComponents/UIComponents.qml \
|
||||||
|
@ -364,8 +365,10 @@ DISTFILES += \
|
||||||
shared/AccountSelector.qml \
|
shared/AccountSelector.qml \
|
||||||
shared/AddButton.qml \
|
shared/AddButton.qml \
|
||||||
shared/Address.qml \
|
shared/Address.qml \
|
||||||
|
shared/CropCornerRectangle.qml \
|
||||||
shared/FormGroup.qml \
|
shared/FormGroup.qml \
|
||||||
shared/IconButton.qml \
|
shared/IconButton.qml \
|
||||||
|
shared/ImageCropper.qml \
|
||||||
shared/Input.qml \
|
shared/Input.qml \
|
||||||
shared/LabelValueRow.qml \
|
shared/LabelValueRow.qml \
|
||||||
shared/ModalPopup.qml \
|
shared/ModalPopup.qml \
|
||||||
|
|
|
@ -35,7 +35,7 @@ Item {
|
||||||
StatusImageIdenticon {
|
StatusImageIdenticon {
|
||||||
id: userImage
|
id: userImage
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
source: loginModel.currentAccount.identicon
|
source: loginModel.currentAccount.thumbnailImage
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
|
|
|
@ -17,7 +17,7 @@ ListView {
|
||||||
delegate: AddressView {
|
delegate: AddressView {
|
||||||
username: model.username
|
username: model.username
|
||||||
address: model.address
|
address: model.address
|
||||||
identicon: model.identicon
|
identicon: model.thumbnailImage
|
||||||
isSelected: function (index, address) {
|
isSelected: function (index, address) {
|
||||||
return addressesView.isSelected(index, address)
|
return addressesView.isSelected(index, address)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import "../imports"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
width: 25
|
||||||
|
height: 25
|
||||||
|
border.width: 2
|
||||||
|
border.color: Style.current.orange
|
||||||
|
color: Utils.setColorAlpha(Style.current.orange, 0.5)
|
||||||
|
|
||||||
|
Drag.active: dragArea.drag.active
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: dragArea
|
||||||
|
property int oldX
|
||||||
|
property int oldY
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
drag.target: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import "."
|
||||||
|
import "../imports"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
property var image
|
||||||
|
property alias selectorRectangle: selectorRectangle
|
||||||
|
|
||||||
|
width: image.width
|
||||||
|
height: image.height
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: selectorRectangle
|
||||||
|
visible: false
|
||||||
|
x: 0
|
||||||
|
y: 0
|
||||||
|
border.width: 2
|
||||||
|
border.color: Style.current.orange
|
||||||
|
color: Style.current.transparent
|
||||||
|
|
||||||
|
function initialSetup() {
|
||||||
|
topLeftCorner.x = 0
|
||||||
|
topLeftCorner.y = 0
|
||||||
|
topRightCorner.x = image.width - topRightCorner.width
|
||||||
|
topRightCorner.y = 0
|
||||||
|
bottomLeftCorner.x = 0
|
||||||
|
bottomLeftCorner.y = image.height - topRightCorner.height
|
||||||
|
bottomRightCorner.x = image.width - topRightCorner.width
|
||||||
|
bottomRightCorner.y = image.height - topRightCorner.height
|
||||||
|
|
||||||
|
selectorRectangle.width = image.width
|
||||||
|
selectorRectangle.height = image.height
|
||||||
|
}
|
||||||
|
|
||||||
|
function adjustRectangleSize() {
|
||||||
|
selectorRectangle.width = bottomRightCorner.x + bottomRightCorner.width - topLeftCorner.x
|
||||||
|
selectorRectangle.height = bottomRightCorner.y + bottomRightCorner.height - topLeftCorner.y
|
||||||
|
selectorRectangle.x = topLeftCorner.x
|
||||||
|
selectorRectangle.y = topLeftCorner.y
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: image
|
||||||
|
onStatusChanged: {
|
||||||
|
if (image.status === Image.Ready) {
|
||||||
|
selectorRectangle.initialSetup()
|
||||||
|
selectorRectangle.visible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size calculations are only done on top-left and bottom-right, because the other two corners follow them
|
||||||
|
CropCornerRectangle {
|
||||||
|
id: topLeftCorner
|
||||||
|
onXChanged: {
|
||||||
|
if (x < 0) x = 0
|
||||||
|
if (x > topRightCorner.x - width) x = topRightCorner.x - width
|
||||||
|
|
||||||
|
bottomLeftCorner.x = x
|
||||||
|
selectorRectangle.adjustRectangleSize()
|
||||||
|
}
|
||||||
|
onYChanged: {
|
||||||
|
if (y < 0) y = 0
|
||||||
|
if (y > bottomRightCorner.y - height) y = bottomRightCorner.y - height
|
||||||
|
|
||||||
|
topRightCorner.y = y
|
||||||
|
selectorRectangle.adjustRectangleSize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CropCornerRectangle {
|
||||||
|
id: topRightCorner
|
||||||
|
onXChanged: {
|
||||||
|
bottomRightCorner.x = x
|
||||||
|
}
|
||||||
|
onYChanged: {
|
||||||
|
topLeftCorner.y = y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CropCornerRectangle {
|
||||||
|
id: bottomLeftCorner
|
||||||
|
onXChanged: {
|
||||||
|
topLeftCorner.x = x
|
||||||
|
}
|
||||||
|
onYChanged: {
|
||||||
|
bottomRightCorner.y = y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CropCornerRectangle {
|
||||||
|
id: bottomRightCorner
|
||||||
|
onXChanged: {
|
||||||
|
if (x < topLeftCorner.x + topLeftCorner.width) x = topLeftCorner.x + topLeftCorner.width
|
||||||
|
if (x > image.width - width) x = image.width - width
|
||||||
|
topRightCorner.x = x
|
||||||
|
selectorRectangle.adjustRectangleSize()
|
||||||
|
}
|
||||||
|
onYChanged: {
|
||||||
|
if (y < topRightCorner.y + topRightCorner.height) y = topRightCorner.y + topRightCorner.height
|
||||||
|
if (y > image.height - height) y = image.height - height
|
||||||
|
bottomLeftCorner.y = y
|
||||||
|
selectorRectangle.adjustRectangleSize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import "../imports"
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
property bool noHover: false
|
||||||
property alias source: image.source
|
property alias source: image.source
|
||||||
property alias fillMode: image.fillMode
|
property alias fillMode: image.fillMode
|
||||||
signal loaded
|
signal loaded
|
||||||
|
@ -138,7 +139,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MouseArea {
|
MouseArea {
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: noHover ? Qt.ArrowCursor : Qt.PointingHandCursor
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.clicked()
|
root.clicked()
|
||||||
|
|
|
@ -4,6 +4,7 @@ import QtGraphicalEffects 1.0
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
signal clicked
|
signal clicked
|
||||||
|
property bool noHover: false
|
||||||
property alias source: imgStickerPackThumb.source
|
property alias source: imgStickerPackThumb.source
|
||||||
property alias fillMode: imgStickerPackThumb.fillMode
|
property alias fillMode: imgStickerPackThumb.fillMode
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@ Rectangle {
|
||||||
|
|
||||||
ImageLoader {
|
ImageLoader {
|
||||||
id: imgStickerPackThumb
|
id: imgStickerPackThumb
|
||||||
|
noHover: root.noHover
|
||||||
opacity: 1
|
opacity: 1
|
||||||
smooth: false
|
smooth: false
|
||||||
radius: root.radius
|
radius: root.radius
|
||||||
|
@ -32,4 +34,4 @@ Rectangle {
|
||||||
source: "https://ipfs.infura.io/ipfs/" + thumbnail
|
source: "https://ipfs.infura.io/ipfs/" + thumbnail
|
||||||
onClicked: root.clicked()
|
onClicked: root.clicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,14 +14,26 @@ Item {
|
||||||
property int identiconSize: 40
|
property int identiconSize: 40
|
||||||
property bool isCompact: false
|
property bool isCompact: false
|
||||||
|
|
||||||
|
property string profileImage: chatType === Constants.chatTypeOneToOne ? chatView.getProfileImage(chatId) || "" : ""
|
||||||
|
|
||||||
height: 48
|
height: 48
|
||||||
width: nameAndInfo.width + chatIdenticon.width + Style.current.smallPadding
|
width: nameAndInfo.width + chatIdenticon.width + Style.current.smallPadding
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
enabled: chatType === Constants.chatTypeOneToOne
|
||||||
|
target: profileModel.contacts.list
|
||||||
|
onContactChanged: {
|
||||||
|
if (pubkey === root.chatId) {
|
||||||
|
root.profileImage = chatView.getProfileImage(root.chatId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StatusIdenticon {
|
StatusIdenticon {
|
||||||
id: chatIdenticon
|
id: chatIdenticon
|
||||||
chatType: root.chatType
|
chatType: root.chatType
|
||||||
chatName: root.chatName
|
chatName: root.chatName
|
||||||
identicon: root.identicon
|
identicon: root.profileImage || root.identicon
|
||||||
width: root.isCompact ? 20 : root.identiconSize
|
width: root.isCompact ? 20 : root.identiconSize
|
||||||
height: root.isCompact ? 20 : root.identiconSize
|
height: root.isCompact ? 20 : root.identiconSize
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
|
@ -24,7 +24,12 @@ Button {
|
||||||
chatId: control.chatId
|
chatId: control.chatId
|
||||||
chatName: control.chatName
|
chatName: control.chatName
|
||||||
chatType: control.chatType
|
chatType: control.chatType
|
||||||
identicon: control.identicon
|
identicon: {
|
||||||
|
if (control.chatType === Constants.chatTypeOneToOne) {
|
||||||
|
return chatView.getProfileImage(control.chatId) || control.identicon
|
||||||
|
}
|
||||||
|
return control.identicon
|
||||||
|
}
|
||||||
identiconSize: control.identiconSize
|
identiconSize: control.identiconSize
|
||||||
isCompact: control.isCompact
|
isCompact: control.isCompact
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,25 +2,14 @@ import QtQuick 2.13
|
||||||
import "../../imports"
|
import "../../imports"
|
||||||
import "../../shared"
|
import "../../shared"
|
||||||
|
|
||||||
Rectangle {
|
RoundedImage {
|
||||||
id: root
|
id: root
|
||||||
property url source:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAg0lEQVR4nOzXwQmAMBAFURV7sQybsgybsgyr0QYUlE1g+Mw7ioQMe9lMQwhDaAyhMYTGEJqYkPnrj/t5XE/ft2UdW1yken7MRAyhMYTGEBpDaAyhKe9JbzvSX9WdLWYihtAYQuMLkcYQGkPUScxEDKExhMYQGkNoDKExhMYQmjsAAP//ZfIUZgXTZXQAAAAASUVORK5CYII="
|
noHover: true
|
||||||
|
source:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAg0lEQVR4nOzXwQmAMBAFURV7sQybsgybsgyr0QYUlE1g+Mw7ioQMe9lMQwhDaAyhMYTGEJqYkPnrj/t5XE/ft2UdW1yken7MRAyhMYTGEBpDaAyhKe9JbzvSX9WdLWYihtAYQuMLkcYQGkPUScxEDKExhMYQGkNoDKExhMYQmjsAAP//ZfIUZgXTZXQAAAAASUVORK5CYII="
|
||||||
width: 40
|
width: 40
|
||||||
height: 40
|
height: 40
|
||||||
color: Style.current.background
|
|
||||||
radius: width / 2
|
|
||||||
border.width: 1
|
border.width: 1
|
||||||
border.color: Style.current.borderSecondary
|
border.color: Style.current.borderSecondary
|
||||||
|
smooth: false
|
||||||
Image {
|
antialiasing: true
|
||||||
width: parent.width
|
|
||||||
height: parent.height
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
source: root.source
|
|
||||||
mipmap: true
|
|
||||||
smooth: false
|
|
||||||
antialiasing: true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit a474e0e1fedd213fa9590811faca198761660857
|
Subproject commit 5c34c8e3563b2d10a2f3d44ac0ee11da13f9a916
|
|
@ -1 +1 @@
|
||||||
Subproject commit b1ba8cba3c75de574046071de824031cde6e580b
|
Subproject commit db7042d4902dc19e5a286416ffa293e43b6249c5
|
|
@ -1 +1 @@
|
||||||
Subproject commit d8307a60cfc49998a48dcb7026b3a1ca17d55985
|
Subproject commit 55a08e9e4d259d77637420ec318cfbafeb8f9f6e
|
Loading…
Reference in New Issue