feat: add community requests, permissions, ENS and more
This commit is contained in:
parent
fb8380a861
commit
f9817d4f52
|
@ -28,6 +28,8 @@ proc handleChatEvents(self: ChatController) =
|
|||
if (evArgs.communities.len > 0):
|
||||
for community in evArgs.communities:
|
||||
self.view.addCommunityToList(community)
|
||||
if (evArgs.communityMembershipRequests.len > 0):
|
||||
self.view.addMembershipRequests(evArgs.communityMembershipRequests)
|
||||
|
||||
self.status.events.on("channelUpdate") do(e: Args):
|
||||
var evArgs = ChatUpdateArgs(e)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
proc handleSignals(self: ChatController) =
|
||||
self.status.events.on(SignalType.Message.event) do(e:Args):
|
||||
var data = MessageSignal(e)
|
||||
self.status.chat.update(data.chats, data.messages, data.emojiReactions, data.communities)
|
||||
self.status.chat.update(data.chats, data.messages, data.emojiReactions, data.communities, data.membershipRequests)
|
||||
|
||||
self.status.events.on(SignalType.DiscoverySummary.event) do(e:Args):
|
||||
## Handle mailserver peers being added and removed
|
||||
|
|
|
@ -17,7 +17,7 @@ import ../../status/chat/[chat, message]
|
|||
import ../../status/profile/profile
|
||||
import web3/[conversions, ethtypes]
|
||||
import ../../status/threads
|
||||
import views/[channels_list, message_list, chat_item, suggestions_list, reactions, stickers, groups, transactions, community_list, community_item]
|
||||
import views/[channels_list, message_list, chat_item, suggestions_list, reactions, stickers, groups, transactions, community_list, community_item, community_membership_request_list]
|
||||
import json_serialization
|
||||
import ../utils/image_utils
|
||||
|
||||
|
@ -48,6 +48,7 @@ QtObject:
|
|||
observedCommunity*: CommunityItemView
|
||||
communityList*: CommunityList
|
||||
joinedCommunityList*: CommunityList
|
||||
myCommunityRequests*: seq[CommunityMembershipRequest]
|
||||
replyTo: string
|
||||
channelOpenTime*: Table[string, int64]
|
||||
connected: bool
|
||||
|
@ -699,6 +700,32 @@ QtObject:
|
|||
|
||||
QtProperty[QVariant] transactions:
|
||||
read = getTransactions
|
||||
|
||||
proc pendingRequestsToJoinForCommunity*(self: ChatsView, communityId: string): seq[CommunityMembershipRequest] =
|
||||
result = self.status.chat.pendingRequestsToJoinForCommunity(communityId)
|
||||
|
||||
proc membershipRequestPushed*(self: ChatsView, communityName: string, pubKey: string) {.signal.}
|
||||
|
||||
proc addMembershipRequests*(self: ChatsView, membershipRequests: seq[CommunityMembershipRequest]) =
|
||||
var communityId: string
|
||||
var community: Community
|
||||
for request in membershipRequests:
|
||||
communityId = request.communityId
|
||||
community = self.joinedCommunityList.getCommunityById(communityId)
|
||||
if (community.id == ""):
|
||||
continue
|
||||
let alreadyPresentRequestIdx = community.membershipRequests.findIndexById(request.id)
|
||||
if (alreadyPresentRequestIdx == -1):
|
||||
community.membershipRequests.add(request)
|
||||
self.membershipRequestPushed(community.name, request.publicKey)
|
||||
else:
|
||||
community.membershipRequests[alreadyPresentRequestIdx] = request
|
||||
self.joinedCommunityList.replaceCommunity(community)
|
||||
|
||||
# Add to active community list
|
||||
if (communityId == self.activeCommunity.communityItem.id):
|
||||
self.activeCommunity.communityMembershipRequestList.addCommunityMembershipRequestItemToList(request)
|
||||
|
||||
proc communitiesChanged*(self: ChatsView) {.signal.}
|
||||
|
||||
proc getCommunitiesIfNotFetched*(self: ChatsView): CommunityList =
|
||||
|
@ -723,12 +750,45 @@ QtObject:
|
|||
self.joinedCommunityList.setNewData(communities)
|
||||
self.joinedCommunityList.fetched = true
|
||||
|
||||
# Also fetch requests
|
||||
self.myCommunityRequests = self.status.chat.myPendingRequestsToJoin()
|
||||
|
||||
return newQVariant(self.joinedCommunityList)
|
||||
|
||||
QtProperty[QVariant] joinedCommunities:
|
||||
read = getJoinedComunities
|
||||
notify = joinedCommunitiesChanged
|
||||
|
||||
proc activeCommunityChanged*(self: ChatsView) {.signal.}
|
||||
|
||||
proc setActiveCommunity*(self: ChatsView, communityId: string) {.slot.} =
|
||||
if(communityId == ""): return
|
||||
self.addMembershipRequests(self.pendingRequestsToJoinForCommunity(communityId))
|
||||
self.activeCommunity.setCommunityItem(self.joinedCommunityList.getCommunityById(communityId))
|
||||
self.activeCommunity.setActive(true)
|
||||
self.activeCommunityChanged()
|
||||
|
||||
proc getActiveCommunity*(self: ChatsView): QVariant {.slot.} =
|
||||
newQVariant(self.activeCommunity)
|
||||
|
||||
QtProperty[QVariant] activeCommunity:
|
||||
read = getActiveCommunity
|
||||
write = setActiveCommunity
|
||||
notify = activeCommunityChanged
|
||||
|
||||
proc joinCommunity*(self: ChatsView, communityId: string, setActive: bool = true): string {.slot.} =
|
||||
result = ""
|
||||
try:
|
||||
self.status.chat.joinCommunity(communityId)
|
||||
self.joinedCommunityList.addCommunityItemToList(self.communityList.getCommunityById(communityId))
|
||||
if (setActive):
|
||||
self.setActiveCommunity(communityId)
|
||||
except Exception as e:
|
||||
error "Error joining the community", msg = e.msg
|
||||
result = fmt"Error joining the community: {e.msg}"
|
||||
|
||||
proc membershipRequestChanged*(self: ChatsView, communityName: string, accepted: bool) {.signal.}
|
||||
|
||||
proc addCommunityToList*(self: ChatsView, community: Community) =
|
||||
let communityCheck = self.communityList.getCommunityById(community.id)
|
||||
if (communityCheck.id == ""):
|
||||
|
@ -742,16 +802,27 @@ QtObject:
|
|||
self.joinedCommunityList.addCommunityItemToList(community)
|
||||
else:
|
||||
self.joinedCommunityList.replaceCommunity(community)
|
||||
elif (community.isMember == true):
|
||||
discard self.joinCommunity(community.id, false)
|
||||
var i = 0
|
||||
for communityRequest in self.myCommunityRequests:
|
||||
if (communityRequest.communityId == community.id):
|
||||
self.membershipRequestChanged(community.name, true)
|
||||
self.myCommunityRequests.delete(i, i)
|
||||
break
|
||||
i = i + 1
|
||||
|
||||
proc createCommunity*(self: ChatsView, name: string, description: string, color: string, imagePath: string): string {.slot.} =
|
||||
proc isCommunityRequestPending*(self: ChatsView, communityId: string): bool {.slot.} =
|
||||
for communityRequest in self.myCommunityRequests:
|
||||
if (communityRequest.communityId == communityId):
|
||||
return true
|
||||
return false
|
||||
|
||||
proc createCommunity*(self: ChatsView, name: string, description: string, access: int, ensOnly: bool, imagePath: string, aX: int, aY: int, bX: int, bY: int): string {.slot.} =
|
||||
result = ""
|
||||
try:
|
||||
# TODO Change this to get it from the user choices
|
||||
let access = ord(CommunityAccessLevel.public)
|
||||
var image = image_utils.formatImagePath(imagePath)
|
||||
let tmpImagePath = image_resizer(image, 2000, TMPDIR)
|
||||
let community = self.status.chat.createCommunity(name, description, color, tmpImagePath, access)
|
||||
removeFile(tmpImagePath)
|
||||
let community = self.status.chat.createCommunity(name, description, access, ensOnly, image, aX, aY, bX, bY)
|
||||
|
||||
if (community.id == ""):
|
||||
return "Community was not created. Please try again later"
|
||||
|
@ -777,22 +848,6 @@ QtObject:
|
|||
error "Error creating the channel", msg = e.msg
|
||||
result = fmt"Error creating the channel: {e.msg}"
|
||||
|
||||
proc activeCommunityChanged*(self: ChatsView) {.signal.}
|
||||
|
||||
proc setActiveCommunity*(self: ChatsView, communityId: string) {.slot.} =
|
||||
if(communityId == ""): return
|
||||
self.activeCommunity.setCommunityItem(self.joinedCommunityList.getCommunityById(communityId))
|
||||
self.activeCommunity.setActive(true)
|
||||
self.activeCommunityChanged()
|
||||
|
||||
proc getActiveCommunity*(self: ChatsView): QVariant {.slot.} =
|
||||
newQVariant(self.activeCommunity)
|
||||
|
||||
QtProperty[QVariant] activeCommunity:
|
||||
read = getActiveCommunity
|
||||
write = setActiveCommunity
|
||||
notify = activeCommunityChanged
|
||||
|
||||
proc observedCommunityChanged*(self: ChatsView) {.signal.}
|
||||
|
||||
proc setObservedCommunity*(self: ChatsView, communityId: string) {.slot.} =
|
||||
|
@ -812,16 +867,6 @@ QtObject:
|
|||
write = setObservedCommunity
|
||||
notify = observedCommunityChanged
|
||||
|
||||
proc joinCommunity*(self: ChatsView, communityId: string): string {.slot.} =
|
||||
result = ""
|
||||
try:
|
||||
self.status.chat.joinCommunity(communityId)
|
||||
self.joinedCommunityList.addCommunityItemToList(self.communityList.getCommunityById(communityId))
|
||||
self.setActiveCommunity(communityId)
|
||||
except Exception as e:
|
||||
error "Error joining the community", msg = e.msg
|
||||
result = fmt"Error joining the community: {e.msg}"
|
||||
|
||||
proc leaveCommunity*(self: ChatsView, communityId: string): string {.slot.} =
|
||||
result = ""
|
||||
try:
|
||||
|
@ -867,6 +912,32 @@ QtObject:
|
|||
error "Error removing user from the community", msg = e.msg
|
||||
|
||||
|
||||
proc requestToJoinCommunity*(self: ChatsView, communityId: string, ensName: string) {.slot.} =
|
||||
try:
|
||||
let requests = self.status.chat.requestToJoinCommunity(communityId, ensName)
|
||||
for request in requests:
|
||||
self.myCommunityRequests.add(request)
|
||||
except Exception as e:
|
||||
error "Error requesting to join the community", msg = e.msg
|
||||
|
||||
proc acceptRequestToJoinCommunity*(self: ChatsView, requestId: string): string {.slot.} =
|
||||
try:
|
||||
self.status.chat.acceptRequestToJoinCommunity(requestId)
|
||||
self.activeCommunity.communityMembershipRequestList.removeCommunityMembershipRequestItemFromList(requestId)
|
||||
except Exception as e:
|
||||
error "Error accepting request to join the community", msg = e.msg
|
||||
return "Error accepting request to join the community"
|
||||
return ""
|
||||
|
||||
proc declineRequestToJoinCommunity*(self: ChatsView, requestId: string): string {.slot.} =
|
||||
try:
|
||||
self.status.chat.declineRequestToJoinCommunity(requestId)
|
||||
self.activeCommunity.communityMembershipRequestList.removeCommunityMembershipRequestItemFromList(requestId)
|
||||
except Exception as e:
|
||||
error "Error declining request to join the community", msg = e.msg
|
||||
return "Error declining request to join the community"
|
||||
return ""
|
||||
|
||||
method rowCount*(self: ChatsView, index: QModelIndex = nil): int =
|
||||
result = self.messageList.len
|
||||
|
||||
|
|
|
@ -3,10 +3,12 @@ import ../../../status/[chat/chat, status]
|
|||
import channels_list
|
||||
import ../../../eventemitter
|
||||
import community_members_list
|
||||
import community_membership_request_list
|
||||
|
||||
QtObject:
|
||||
type CommunityItemView* = ref object of QObject
|
||||
communityItem*: Community
|
||||
communityMembershipRequestList*: CommunityMembershipRequestList
|
||||
chats*: ChannelsList
|
||||
members*: CommunityMembersView
|
||||
status*: Status
|
||||
|
@ -25,6 +27,7 @@ QtObject:
|
|||
result.status = status
|
||||
result.active = false
|
||||
result.chats = newChannelsList(status)
|
||||
result.communityMembershipRequestList = newCommunityMembershipRequestList()
|
||||
result.members = newCommunityMembersView(status)
|
||||
result.setup
|
||||
|
||||
|
@ -32,6 +35,7 @@ QtObject:
|
|||
self.communityItem = communityItem
|
||||
self.chats.setChats(communityItem.chats)
|
||||
self.members.setMembers(communityItem.members)
|
||||
self.communityMembershipRequestList.setNewData(communityItem.membershipRequests)
|
||||
|
||||
proc activeChanged*(self: CommunityItemView) {.signal.}
|
||||
|
||||
|
@ -88,6 +92,31 @@ QtObject:
|
|||
QtProperty[bool] verified:
|
||||
read = verified
|
||||
|
||||
proc ensOnly*(self: CommunityItemView): bool {.slot.} = result = ?.self.communityItem.ensOnly
|
||||
|
||||
QtProperty[bool] ensOnly:
|
||||
read = ensOnly
|
||||
|
||||
proc canRequestAccess*(self: CommunityItemView): bool {.slot.} = result = ?.self.communityItem.canRequestAccess
|
||||
|
||||
QtProperty[bool] canRequestAccess:
|
||||
read = canRequestAccess
|
||||
|
||||
proc canManageUsers*(self: CommunityItemView): bool {.slot.} = result = ?.self.communityItem.canManageUsers
|
||||
|
||||
QtProperty[bool] canManageUsers:
|
||||
read = canManageUsers
|
||||
|
||||
proc canJoin*(self: CommunityItemView): bool {.slot.} = result = ?.self.communityItem.canJoin
|
||||
|
||||
QtProperty[bool] canJoin:
|
||||
read = canJoin
|
||||
|
||||
proc isMember*(self: CommunityItemView): bool {.slot.} = result = ?.self.communityItem.isMember
|
||||
|
||||
QtProperty[bool] isMember:
|
||||
read = isMember
|
||||
|
||||
proc nbMembers*(self: CommunityItemView): int {.slot.} = result = ?.self.communityItem.members.len
|
||||
|
||||
QtProperty[int] nbMembers:
|
||||
|
@ -104,4 +133,26 @@ QtObject:
|
|||
result = newQVariant(self.members)
|
||||
|
||||
QtProperty[QVariant] members:
|
||||
read = getMembers
|
||||
read = getMembers
|
||||
|
||||
proc getCommunityMembershipRequest*(self: CommunityItemView): QVariant {.slot.} =
|
||||
result = newQVariant(self.communityMembershipRequestList)
|
||||
|
||||
QtProperty[QVariant] communityMembershipRequests:
|
||||
read = getCommunityMembershipRequest
|
||||
|
||||
proc thumbnailImage*(self: CommunityItemView): string {.slot.} =
|
||||
if (self.communityItem.communityImage.isNil):
|
||||
return ""
|
||||
result = self.communityItem.communityImage.thumbnail
|
||||
|
||||
QtProperty[string] thumbnailImage:
|
||||
read = thumbnailImage
|
||||
|
||||
proc largeImage*(self: CommunityItemView): string {.slot.} =
|
||||
if (self.communityItem.communityImage.isNil):
|
||||
return ""
|
||||
result = self.communityItem.communityImage.large
|
||||
|
||||
QtProperty[string] largeImage:
|
||||
read = largeImage
|
|
@ -10,12 +10,18 @@ type
|
|||
Id = UserRole + 1,
|
||||
Name = UserRole + 2
|
||||
Description = UserRole + 3
|
||||
# Color = UserRole + 4
|
||||
Access = UserRole + 5
|
||||
Admin = UserRole + 6
|
||||
Joined = UserRole + 7
|
||||
Verified = UserRole + 8
|
||||
NumMembers = UserRole + 9
|
||||
Access = UserRole + 4
|
||||
Admin = UserRole + 5
|
||||
Joined = UserRole + 6
|
||||
Verified = UserRole + 7
|
||||
NumMembers = UserRole + 8
|
||||
ThumbnailImage = UserRole + 9
|
||||
LargeImage = UserRole + 10
|
||||
EnsOnly = UserRole + 11
|
||||
CanRequestAccess = UserRole + 12
|
||||
CanManageUsers = UserRole + 13
|
||||
CanJoin = UserRole + 14
|
||||
IsMember = UserRole + 15
|
||||
|
||||
QtObject:
|
||||
type
|
||||
|
@ -50,25 +56,44 @@ QtObject:
|
|||
of CommunityRoles.Name: result = newQVariant(communityItem.name)
|
||||
of CommunityRoles.Description: result = newQVariant(communityItem.description)
|
||||
of CommunityRoles.Id: result = newQVariant(communityItem.id)
|
||||
# of CommunityRoles.Color: result = newQVariant(communityItem.color)
|
||||
of CommunityRoles.Access: result = newQVariant(communityItem.access.int)
|
||||
of CommunityRoles.Admin: result = newQVariant(communityItem.admin.bool)
|
||||
of CommunityRoles.Joined: result = newQVariant(communityItem.joined.bool)
|
||||
of CommunityRoles.Verified: result = newQVariant(communityItem.verified.bool)
|
||||
of CommunityRoles.EnsOnly: result = newQVariant(communityItem.ensOnly.bool)
|
||||
of CommunityRoles.CanRequestAccess: result = newQVariant(communityItem.canRequestAccess.bool)
|
||||
of CommunityRoles.CanManageUsers: result = newQVariant(communityItem.canManageUsers.bool)
|
||||
of CommunityRoles.CanJoin: result = newQVariant(communityItem.canJoin.bool)
|
||||
of CommunityRoles.IsMember: result = newQVariant(communityItem.isMember.bool)
|
||||
of CommunityRoles.NumMembers: result = newQVariant(communityItem.members.len)
|
||||
|
||||
of CommunityRoles.ThumbnailImage:
|
||||
if (not communityItem.communityImage.isNil):
|
||||
result = newQVariant(communityItem.communityImage.thumbnail)
|
||||
else:
|
||||
result = newQVariant("")
|
||||
of CommunityRoles.LargeImage:
|
||||
if (not communityItem.communityImage.isNil):
|
||||
result = newQVariant(communityItem.communityImage.large)
|
||||
else:
|
||||
result = newQVariant("")
|
||||
|
||||
method roleNames(self: CommunityList): Table[int, string] =
|
||||
{
|
||||
CommunityRoles.Name.int:"name",
|
||||
CommunityRoles.Description.int:"description",
|
||||
CommunityRoles.Id.int: "id",
|
||||
# CommunityRoles.Color.int: "color",
|
||||
CommunityRoles.Access.int: "access",
|
||||
CommunityRoles.Admin.int: "admin",
|
||||
CommunityRoles.Verified.int: "verified",
|
||||
CommunityRoles.Joined.int: "joined",
|
||||
CommunityRoles.NumMembers.int: "nbMembers"
|
||||
CommunityRoles.EnsOnly.int: "ensOnly",
|
||||
CommunityRoles.CanRequestAccess.int: "canRequestAccess",
|
||||
CommunityRoles.CanManageUsers.int: "canManageUsers",
|
||||
CommunityRoles.CanJoin.int: "canJoin",
|
||||
CommunityRoles.IsMember.int: "isMember",
|
||||
CommunityRoles.NumMembers.int: "nbMembers",
|
||||
CommunityRoles.ThumbnailImage.int:"thumbnailImage",
|
||||
CommunityRoles.LargeImage.int:"largeImage"
|
||||
}.toTable
|
||||
|
||||
proc setNewData*(self: CommunityList, communityList: seq[Community]) =
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
import NimQml, Tables, chronicles
|
||||
import ../../../status/chat/[chat, message]
|
||||
import ../../../status/status
|
||||
import ../../../status/ens
|
||||
import ../../../status/accounts
|
||||
import strutils
|
||||
|
||||
type
|
||||
CommunityMembershipRequestRoles {.pure.} = enum
|
||||
Id = UserRole + 1,
|
||||
PublicKey = UserRole + 2
|
||||
ChatId = UserRole + 3
|
||||
CommunityId = UserRole + 4
|
||||
State = UserRole + 5
|
||||
Our = UserRole + 6
|
||||
|
||||
QtObject:
|
||||
type
|
||||
CommunityMembershipRequestList* = ref object of QAbstractListModel
|
||||
communityMembershipRequests*: seq[CommunityMembershipRequest]
|
||||
|
||||
proc setup(self: CommunityMembershipRequestList) = self.QAbstractListModel.setup
|
||||
|
||||
proc delete(self: CommunityMembershipRequestList) =
|
||||
self.communityMembershipRequests = @[]
|
||||
self.QAbstractListModel.delete
|
||||
|
||||
proc newCommunityMembershipRequestList*(): CommunityMembershipRequestList =
|
||||
new(result, delete)
|
||||
result.communityMembershipRequests = @[]
|
||||
result.setup()
|
||||
|
||||
method rowCount*(self: CommunityMembershipRequestList, index: QModelIndex = nil): int = self.communityMembershipRequests.len
|
||||
|
||||
method data(self: CommunityMembershipRequestList, index: QModelIndex, role: int): QVariant =
|
||||
if not index.isValid:
|
||||
return
|
||||
if index.row < 0 or index.row >= self.communityMembershipRequests.len:
|
||||
return
|
||||
|
||||
let communityMembershipRequestItem = self.communityMembershipRequests[index.row]
|
||||
let communityMembershipRequestItemRole = role.CommunityMembershipRequestRoles
|
||||
case communityMembershipRequestItemRole:
|
||||
of CommunityMembershipRequestRoles.Id: result = newQVariant(communityMembershipRequestItem.id.string)
|
||||
of CommunityMembershipRequestRoles.PublicKey: result = newQVariant(communityMembershipRequestItem.publicKey.string)
|
||||
of CommunityMembershipRequestRoles.ChatId: result = newQVariant(communityMembershipRequestItem.chatId.string)
|
||||
of CommunityMembershipRequestRoles.CommunityId: result = newQVariant(communityMembershipRequestItem.communityId.string)
|
||||
of CommunityMembershipRequestRoles.State: result = newQVariant(communityMembershipRequestItem.state.int)
|
||||
of CommunityMembershipRequestRoles.Our: result = newQVariant(communityMembershipRequestItem.our.string)
|
||||
|
||||
method roleNames(self: CommunityMembershipRequestList): Table[int, string] =
|
||||
{
|
||||
CommunityMembershipRequestRoles.Id.int: "id",
|
||||
CommunityMembershipRequestRoles.PublicKey.int: "publicKey",
|
||||
CommunityMembershipRequestRoles.ChatId.int: "chatId",
|
||||
CommunityMembershipRequestRoles.CommunityId.int: "communityId",
|
||||
CommunityMembershipRequestRoles.State.int: "state",
|
||||
CommunityMembershipRequestRoles.Our.int: "our"
|
||||
}.toTable
|
||||
|
||||
proc nbRequestsChanged*(self: CommunityMembershipRequestList) {.signal.}
|
||||
|
||||
proc nbRequests*(self: CommunityMembershipRequestList): int {.slot.} = result = self.communityMembershipRequests.len
|
||||
|
||||
QtProperty[int] nbRequests:
|
||||
read = nbRequests
|
||||
notify = nbRequestsChanged
|
||||
|
||||
proc setNewData*(self: CommunityMembershipRequestList, communityMembershipRequestList: seq[CommunityMembershipRequest]) =
|
||||
self.beginResetModel()
|
||||
self.communityMembershipRequests = communityMembershipRequestList
|
||||
self.endResetModel()
|
||||
self.nbRequestsChanged()
|
||||
|
||||
proc addCommunityMembershipRequestItemToList*(self: CommunityMembershipRequestList, communityMemberphipRequest: CommunityMembershipRequest) =
|
||||
self.beginInsertRows(newQModelIndex(), self.communityMembershipRequests.len, self.communityMembershipRequests.len)
|
||||
self.communityMembershipRequests.add(communityMemberphipRequest)
|
||||
self.endInsertRows()
|
||||
self.nbRequestsChanged()
|
||||
|
||||
proc removeCommunityMembershipRequestItemFromList*(self: CommunityMembershipRequestList, id: string) =
|
||||
let idx = self.communityMembershipRequests.findIndexById(id)
|
||||
self.beginRemoveRows(newQModelIndex(), idx, idx)
|
||||
self.communityMembershipRequests.delete(idx)
|
||||
self.endRemoveRows()
|
||||
self.nbRequestsChanged()
|
||||
|
||||
proc getCommunityMembershipRequestById*(self: CommunityMembershipRequestList, communityMembershipRequestId: string): CommunityMembershipRequest =
|
||||
for communityMembershipRequest in self.communityMembershipRequests:
|
||||
if communityMembershipRequest.id == communityMembershipRequestId:
|
||||
return communityMembershipRequest
|
|
@ -25,6 +25,7 @@ type
|
|||
contacts*: seq[Profile]
|
||||
emojiReactions*: seq[Reaction]
|
||||
communities*: seq[Community]
|
||||
communityMembershipRequests*: seq[CommunityMembershipRequest]
|
||||
|
||||
ChatIdArg* = ref object of Args
|
||||
chatId*: string
|
||||
|
@ -70,7 +71,7 @@ proc newChatModel*(events: EventEmitter): ChatModel =
|
|||
proc delete*(self: ChatModel) =
|
||||
discard
|
||||
|
||||
proc update*(self: ChatModel, chats: seq[Chat], messages: seq[Message], emojiReactions: seq[Reaction], communities: seq[Community]) =
|
||||
proc update*(self: ChatModel, chats: seq[Chat], messages: seq[Message], emojiReactions: seq[Reaction], communities: seq[Community], communityMembershipRequests: seq[CommunityMembershipRequest]) =
|
||||
for chat in chats:
|
||||
if chat.isActive:
|
||||
self.channels[chat.id] = chat
|
||||
|
@ -84,7 +85,7 @@ proc update*(self: ChatModel, chats: seq[Chat], messages: seq[Message], emojiRea
|
|||
if self.lastMessageTimestamps[chatId] > ts:
|
||||
self.lastMessageTimestamps[chatId] = ts
|
||||
|
||||
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats, contacts: @[], emojiReactions: emojiReactions, communities: communities))
|
||||
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats, contacts: @[], emojiReactions: emojiReactions, communities: communities, communityMembershipRequests: communityMembershipRequests))
|
||||
|
||||
proc hasChannel*(self: ChatModel, chatId: string): bool =
|
||||
self.channels.hasKey(chatId)
|
||||
|
@ -412,8 +413,8 @@ proc getAllComunities*(self: ChatModel): seq[Community] =
|
|||
proc getJoinedComunities*(self: ChatModel): seq[Community] =
|
||||
result = status_chat.getJoinedComunities()
|
||||
|
||||
proc createCommunity*(self: ChatModel, name: string, description: string, color: string, image: string, access: int): Community =
|
||||
result = status_chat.createCommunity(name, description, color, image, access)
|
||||
proc createCommunity*(self: ChatModel, name: string, description: string, access: int, ensOnly: bool, imageUrl: string, aX: int, aY: int, bX: int, bY: int): Community =
|
||||
result = status_chat.createCommunity(name, description, access, ensOnly, imageUrl, aX, aY, bX, bY)
|
||||
|
||||
proc createCommunityChannel*(self: ChatModel, communityId: string, name: string, description: string): Chat =
|
||||
result = status_chat.createCommunityChannel(communityId, name, description)
|
||||
|
@ -436,4 +437,19 @@ proc exportCommunity*(self: ChatModel, communityId: string): string =
|
|||
result = status_chat.exportCommunity(communityId)
|
||||
|
||||
proc importCommunity*(self: ChatModel, communityKey: string) =
|
||||
status_chat.importCommunity(communityKey)
|
||||
status_chat.importCommunity(communityKey)
|
||||
|
||||
proc requestToJoinCommunity*(self: ChatModel, communityKey: string, ensName: string): seq[CommunityMembershipRequest] =
|
||||
status_chat.requestToJoinCommunity(communityKey, ensName)
|
||||
|
||||
proc acceptRequestToJoinCommunity*(self: ChatModel, requestId: string) =
|
||||
status_chat.acceptRequestToJoinCommunity(requestId)
|
||||
|
||||
proc declineRequestToJoinCommunity*(self: ChatModel, requestId: string) =
|
||||
status_chat.declineRequestToJoinCommunity(requestId)
|
||||
|
||||
proc pendingRequestsToJoinForCommunity*(self: ChatModel, communityKey: string): seq[CommunityMembershipRequest] =
|
||||
result = status_chat.pendingRequestsToJoinForCommunity(communityKey)
|
||||
|
||||
proc myPendingRequestsToJoin*(self: ChatModel): seq[CommunityMembershipRequest] =
|
||||
result = status_chat.myPendingRequestsToJoin()
|
|
@ -1,5 +1,6 @@
|
|||
import strformat, json, sequtils
|
||||
from message import Message
|
||||
import ../libstatus/types
|
||||
|
||||
type ChatType* {.pure.}= enum
|
||||
Unknown = 0,
|
||||
|
@ -74,13 +75,22 @@ type Chat* = ref object
|
|||
membershipUpdateEvents*: seq[ChatMembershipEvent]
|
||||
hasMentions*: bool
|
||||
muted*: bool
|
||||
canPost*: bool
|
||||
ensName*: string
|
||||
|
||||
type CommunityAccessLevel* = enum
|
||||
unknown = 0
|
||||
public = 1
|
||||
invitationOnly = 2
|
||||
onRequest = 3
|
||||
unknown = 0
|
||||
public = 1
|
||||
invitationOnly = 2
|
||||
onRequest = 3
|
||||
|
||||
type CommunityMembershipRequest* = object
|
||||
id*: string
|
||||
publicKey*: string
|
||||
chatId*: string
|
||||
communityId*: string
|
||||
state*: int
|
||||
our*: string
|
||||
|
||||
type Community* = object
|
||||
id*: string
|
||||
|
@ -88,11 +98,17 @@ type Community* = object
|
|||
description*: string
|
||||
chats*: seq[Chat]
|
||||
members*: seq[string]
|
||||
# color*: string
|
||||
access*: int
|
||||
admin*: bool
|
||||
joined*: bool
|
||||
verified*: bool
|
||||
ensOnly*: bool
|
||||
canRequestAccess*: bool
|
||||
canManageUsers*: bool
|
||||
canJoin*: bool
|
||||
isMember*: bool
|
||||
communityImage*: IdentityImage
|
||||
membershipRequests*: seq[CommunityMembershipRequest]
|
||||
|
||||
proc `$`*(self: Chat): string =
|
||||
result = fmt"Chat(id:{self.id}, name:{self.name}, active:{self.isActive}, type:{self.chatType})"
|
||||
|
@ -134,6 +150,15 @@ proc findIndexById*(self: seq[Community], id: string): int =
|
|||
result = idx
|
||||
break
|
||||
|
||||
proc findIndexById*(self: seq[CommunityMembershipRequest], id: string): int =
|
||||
result = -1
|
||||
var idx = -1
|
||||
for item in self:
|
||||
inc idx
|
||||
if(item.id == id):
|
||||
result = idx
|
||||
break
|
||||
|
||||
proc isMember*(self: Chat, pubKey: string): bool =
|
||||
for member in self.members:
|
||||
if member.id == pubKey and member.joined: return true
|
||||
|
|
|
@ -41,6 +41,7 @@ proc unblockContact*(self: ContactModel, id: string): string =
|
|||
var contact = self.getContactByID(id)
|
||||
contact.systemTags.delete(contact.systemTags.find(":contact/blocked"))
|
||||
discard status_contacts.saveContact(contact.id, contact.ensVerified, contact.ensName, contact.ensVerifiedAt, contact.ensVerificationRetries,contact.alias, contact.identicon, contact.identityImage.thumbnail, contact.systemTags, contact.localNickname)
|
||||
discard status_contacts.saveContact(contact.id, contact.ensVerified, contact.ensName, contact.alias, contact.identicon, contact.systemTags, contact.localNickname)
|
||||
self.events.emit("contactUnblocked", Args())
|
||||
|
||||
proc getAllContacts*(): seq[Profile] =
|
||||
|
@ -64,9 +65,7 @@ proc addContact*(self: ContactModel, id: string, localNickname: string): string
|
|||
alias: alias,
|
||||
ensName: "",
|
||||
ensVerified: false,
|
||||
ensVerifiedAt: 0,
|
||||
appearance: 0,
|
||||
ensVerificationRetries: 0,
|
||||
systemTags: @[]
|
||||
)
|
||||
|
||||
|
@ -98,9 +97,7 @@ proc addContact*(self: ContactModel, id: string, localNickname: string): string
|
|||
alias: contact.alias,
|
||||
ensName: contact.ensName,
|
||||
ensVerified: contact.ensVerified,
|
||||
ensVerifiedAt: contact.ensVerifiedAt,
|
||||
appearance: 0,
|
||||
ensVerificationRetries: contact.ensVerificationRetries,
|
||||
systemTags: contact.systemTags,
|
||||
localNickname: nickname
|
||||
)
|
||||
|
|
|
@ -257,24 +257,18 @@ proc getJoinedComunities*(): seq[Community] =
|
|||
communities.add(community)
|
||||
return communities
|
||||
|
||||
proc createCommunity*(name: string, description: string, color: string, image: string, access: int): Community =
|
||||
proc createCommunity*(name: string, description: string, access: int, ensOnly: bool, imageUrl: string, aX: int, aY: int, bX: int, bY: int): Community =
|
||||
let rpcResult = callPrivateRPC("createCommunity".prefix, %*[{
|
||||
"permissions": {
|
||||
"access": access
|
||||
},
|
||||
"identity": {
|
||||
"display_name": name,
|
||||
"description": description#,
|
||||
# "color": color#,
|
||||
# TODO add images once it is supported by Status-Go
|
||||
# "images": [
|
||||
# {
|
||||
# "payload": image,
|
||||
# # TODO get that from an enum
|
||||
# "image_type": 1 # 1 is a raw payload
|
||||
# }
|
||||
# ]
|
||||
}
|
||||
# TODO this will need to be renamed membership (small m)
|
||||
"Membership": access,
|
||||
"name": name,
|
||||
"description": description,
|
||||
"ensOnly": ensOnly,
|
||||
"image": imageUrl,
|
||||
"imageAx": aX,
|
||||
"imageAy": aY,
|
||||
"imageBx": bX,
|
||||
"imageBy": bY
|
||||
}]).parseJSON()
|
||||
|
||||
if rpcResult{"result"}.kind != JNull:
|
||||
|
@ -321,4 +315,50 @@ proc importCommunity*(communityKey: string) =
|
|||
discard callPrivateRPC("importCommunity".prefix, %*[communityKey])
|
||||
|
||||
proc removeUserFromCommunity*(communityId: string, pubKey: string) =
|
||||
discard callPrivateRPC("removeUserFromCommunity".prefix, %*[communityId, pubKey])
|
||||
discard callPrivateRPC("removeUserFromCommunity".prefix, %*[communityId, pubKey])
|
||||
|
||||
proc requestToJoinCommunity*(communityId: string, ensName: string): seq[CommunityMembershipRequest] =
|
||||
let rpcResult = callPrivateRPC("requestToJoinCommunity".prefix, %*[{
|
||||
"communityId": communityId,
|
||||
"ensName": ensName
|
||||
}]).parseJSON()
|
||||
|
||||
var communityRequests: seq[CommunityMembershipRequest] = @[]
|
||||
|
||||
if rpcResult{"result"}{"requestsToJoinCommunity"}.kind != JNull:
|
||||
for jsonCommunityReqest in rpcResult["result"]["requestsToJoinCommunity"]:
|
||||
communityRequests.add(jsonCommunityReqest.toCommunityMembershipRequest())
|
||||
|
||||
return communityRequests
|
||||
|
||||
proc acceptRequestToJoinCommunity*(requestId: string) =
|
||||
discard callPrivateRPC("acceptRequestToJoinCommunity".prefix, %*[{
|
||||
"id": requestId
|
||||
}])
|
||||
|
||||
proc declineRequestToJoinCommunity*(requestId: string) =
|
||||
discard callPrivateRPC("declineRequestToJoinCommunity".prefix, %*[{
|
||||
"id": requestId
|
||||
}])
|
||||
|
||||
proc pendingRequestsToJoinForCommunity*(communityId: string): seq[CommunityMembershipRequest] =
|
||||
let rpcResult = callPrivateRPC("pendingRequestsToJoinForCommunity".prefix, %*[communityId]).parseJSON()
|
||||
|
||||
var communityRequests: seq[CommunityMembershipRequest] = @[]
|
||||
|
||||
if rpcResult{"result"}.kind != JNull:
|
||||
for jsonCommunityReqest in rpcResult["result"]:
|
||||
communityRequests.add(jsonCommunityReqest.toCommunityMembershipRequest())
|
||||
|
||||
return communityRequests
|
||||
|
||||
proc myPendingRequestsToJoin*(): seq[CommunityMembershipRequest] =
|
||||
let rpcResult = callPrivateRPC("myPendingRequestsToJoin".prefix).parseJSON()
|
||||
|
||||
var communityRequests: seq[CommunityMembershipRequest] = @[]
|
||||
|
||||
if rpcResult{"result"}.kind != JNull:
|
||||
for jsonCommunityReqest in rpcResult["result"]:
|
||||
communityRequests.add(jsonCommunityReqest.toCommunityMembershipRequest())
|
||||
|
||||
return communityRequests
|
|
@ -8,8 +8,6 @@ proc blockContact*(contact: Profile): string =
|
|||
{
|
||||
"id": contact.id,
|
||||
"ensVerified": contact.ensVerified,
|
||||
"ensVerifiedAt": contact.ensVerifiedAt,
|
||||
"ensVerificationRetries": contact.ensVerificationRetries,
|
||||
"alias": contact.alias,
|
||||
"identicon": contact.identicon,
|
||||
"systemTags": contact.systemTags
|
||||
|
@ -27,12 +25,11 @@ proc getContacts*(): JsonNode =
|
|||
return response["result"]
|
||||
|
||||
proc saveContact*(id: string, ensVerified: bool, ensName: string, ensVerifiedAt: int, ensVerificationRetries: int, alias: string, identicon: string, thumbnail: string, systemTags: seq[string], localNickname: string): string =
|
||||
proc saveContact*(id: string, ensVerified: bool, ensName: string, alias: string, identicon: string, systemTags: seq[string], localNickname: string): string =
|
||||
let payload = %* [{
|
||||
"id": id,
|
||||
"name": ensName,
|
||||
"ensVerified": ensVerified,
|
||||
"ensVerifiedAt": ensVerifiedAt,
|
||||
"ensVerificationRetries": ensVerificationRetries,
|
||||
"alias": alias,
|
||||
"identicon": identicon,
|
||||
"images": {"thumbnail": {"Payload": thumbnail.partition(",")[2]}},
|
||||
|
|
|
@ -5,7 +5,7 @@ type Profile* = ref object
|
|||
id*, alias*, username*, identicon*, address*, ensName*, localNickname*: string
|
||||
ensVerified*: bool
|
||||
identityImage*: IdentityImage
|
||||
ensVerifiedAt*, ensVerificationRetries*, appearance*: int
|
||||
appearance*: int
|
||||
systemTags*: seq[string]
|
||||
|
||||
proc isContact*(self: Profile): bool =
|
||||
|
@ -22,9 +22,7 @@ proc toProfileModel*(account: Account): Profile =
|
|||
alias: account.name,
|
||||
ensName: "",
|
||||
ensVerified: false,
|
||||
ensVerifiedAt: 0,
|
||||
appearance: 0,
|
||||
ensVerificationRetries: 0,
|
||||
systemTags: @[]
|
||||
)
|
||||
|
||||
|
@ -43,8 +41,6 @@ proc toProfileModel*(profile: JsonNode): Profile =
|
|||
ensName: "",
|
||||
ensVerified: profile["ensVerified"].getBool,
|
||||
appearance: 0,
|
||||
ensVerifiedAt: profile["ensVerifiedAt"].getInt,
|
||||
ensVerificationRetries: profile["ensVerificationRetries"].getInt,
|
||||
systemTags: systemTags
|
||||
)
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ proc toReaction*(jsonReaction: JsonNode): Reaction
|
|||
|
||||
proc toCommunity*(jsonCommunity: JsonNode): Community
|
||||
|
||||
proc toCommunityMembershipRequest*(jsonCommunityMembershipRequest: JsonNode): CommunityMembershipRequest
|
||||
|
||||
proc fromEvent*(event: JsonNode): Signal =
|
||||
var signal:MessageSignal = MessageSignal()
|
||||
signal.messages = @[]
|
||||
|
@ -61,6 +63,11 @@ proc fromEvent*(event: JsonNode): Signal =
|
|||
for jsonCommunity in event["event"]["communities"]:
|
||||
signal.communities.add(jsonCommunity.toCommunity)
|
||||
|
||||
if event["event"]{"requestsToJoinCommunity"} != nil:
|
||||
debug "requests", event = event["event"]["requestsToJoinCommunity"]
|
||||
for jsonCommunity in event["event"]["requestsToJoinCommunity"]:
|
||||
signal.membershipRequests.add(jsonCommunity.toCommunityMembershipRequest)
|
||||
|
||||
result = signal
|
||||
|
||||
proc toChatMember*(jsonMember: JsonNode): ChatMember =
|
||||
|
@ -164,32 +171,53 @@ proc toChat*(jsonChat: JsonNode): Chat =
|
|||
proc toCommunity*(jsonCommunity: JsonNode): Community =
|
||||
result = Community(
|
||||
id: jsonCommunity{"id"}.getStr,
|
||||
name: jsonCommunity{"description"}{"identity"}{"display_name"}.getStr,
|
||||
description: jsonCommunity{"description"}{"identity"}{"description"}.getStr,
|
||||
# color: jsonCommunity{"description"}{"identity"}{"color"}.getStr,
|
||||
access: jsonCommunity{"description"}{"permissions"}{"access"}.getInt,
|
||||
name: jsonCommunity{"name"}.getStr,
|
||||
description: jsonCommunity{"description"}.getStr,
|
||||
access: jsonCommunity{"permissions"}{"access"}.getInt,
|
||||
admin: jsonCommunity{"admin"}.getBool,
|
||||
joined: jsonCommunity{"joined"}.getBool,
|
||||
verified: jsonCommunity{"verified"}.getBool,
|
||||
ensOnly: jsonCommunity{"permissions"}{"ens_only"}.getBool,
|
||||
canRequestAccess: jsonCommunity{"canRequestAccess"}.getBool,
|
||||
canManageUsers: jsonCommunity{"canManageUsers"}.getBool,
|
||||
canJoin: jsonCommunity{"canJoin"}.getBool,
|
||||
isMember: jsonCommunity{"isMember"}.getBool,
|
||||
chats: newSeq[Chat](),
|
||||
members: newSeq[string]()
|
||||
members: newSeq[string](),
|
||||
communityImage: IdentityImage()
|
||||
)
|
||||
|
||||
if jsonCommunity["description"].hasKey("chats") and jsonCommunity["description"]["chats"].kind != JNull:
|
||||
for chatId, chat in jsonCommunity{"description"}{"chats"}:
|
||||
if jsonCommunity.hasKey("images") and jsonCommunity["images"].kind != JNull:
|
||||
if jsonCommunity["images"].hasKey("thumbnail"):
|
||||
result.communityImage.thumbnail = jsonCommunity["images"]["thumbnail"]["uri"].str
|
||||
if jsonCommunity["images"].hasKey("large"):
|
||||
result.communityImage.large = jsonCommunity["images"]["large"]["uri"].str
|
||||
|
||||
if jsonCommunity.hasKey("chats") and jsonCommunity["chats"].kind != JNull:
|
||||
for chatId, chat in jsonCommunity{"chats"}:
|
||||
result.chats.add(Chat(
|
||||
id: result.id & chatId,
|
||||
name: chat{"identity"}{"display_name"}.getStr,
|
||||
description: chat{"identity"}{"description"}.getStr,
|
||||
name: chat{"name"}.getStr,
|
||||
canPost: chat{"canPost"}.getBool,
|
||||
# TODO get this from access
|
||||
chatType: ChatType.Public#chat{"permissions"}{"access"}.getInt,
|
||||
chatType: ChatType.Public#chat{"permissions"}{"access"}.getInt,
|
||||
))
|
||||
|
||||
if jsonCommunity["description"].hasKey("members") and jsonCommunity["description"]["members"].kind != JNull:
|
||||
if jsonCommunity.hasKey("members") and jsonCommunity["members"].kind != JNull:
|
||||
# memberInfo is empty for now
|
||||
for memberPubKey, memeberInfo in jsonCommunity{"description"}{"members"}:
|
||||
for memberPubKey, memeberInfo in jsonCommunity{"members"}:
|
||||
result.members.add(memberPubKey)
|
||||
|
||||
proc toCommunityMembershipRequest*(jsonCommunityMembershipRequest: JsonNode): CommunityMembershipRequest =
|
||||
result = CommunityMembershipRequest(
|
||||
id: jsonCommunityMembershipRequest{"id"}.getStr,
|
||||
publicKey: jsonCommunityMembershipRequest{"publicKey"}.getStr,
|
||||
chatId: jsonCommunityMembershipRequest{"chatId"}.getStr,
|
||||
state: jsonCommunityMembershipRequest{"state"}.getInt,
|
||||
communityId: jsonCommunityMembershipRequest{"communityId"}.getStr,
|
||||
our: jsonCommunityMembershipRequest{"our"}.getStr,
|
||||
)
|
||||
|
||||
proc toTextItem*(jsonText: JsonNode): TextItem =
|
||||
result = TextItem(
|
||||
literal: jsonText{"literal"}.getStr,
|
||||
|
|
|
@ -34,6 +34,7 @@ type MessageSignal* = ref object of Signal
|
|||
installations*: seq[Installation]
|
||||
emojiReactions*: seq[Reaction]
|
||||
communities*: seq[Community]
|
||||
membershipRequests*: seq[CommunityMembershipRequest]
|
||||
|
||||
type MailserverRequestCompletedSignal* = ref object of Signal
|
||||
requestID*: string
|
||||
|
|
|
@ -201,6 +201,21 @@ ScrollView {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMembershipRequestChanged: function (communityName, accepted) {
|
||||
systemTray.showMessage("Status",
|
||||
accepted ? qsTr("You have been accepted into the ‘%1’ community").arg(communityName) :
|
||||
qsTr("Your request to join the ‘%1’ community was declined").arg(communityName),
|
||||
SystemTrayIcon.NoIcon,
|
||||
Constants.notificationPopupTTL)
|
||||
}
|
||||
|
||||
onMembershipRequestPushed: function (communityName, pubKey) {
|
||||
systemTray.showMessage(qsTr("New membership request"),
|
||||
qsTr("%1 asks to join ‘%2’").arg(Utils.getDisplayName(pubKey)).arg(communityName),
|
||||
SystemTrayIcon.NoIcon,
|
||||
Constants.notificationPopupTTL)
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
@ -237,52 +252,12 @@ ScrollView {
|
|||
icon: StandardIcon.Critical
|
||||
}
|
||||
|
||||
DelegateModel {
|
||||
DelegateModelGeneralized {
|
||||
id: messageListDelegate
|
||||
property var lessThan: [
|
||||
function(left, right) { return left.clock > right.clock }
|
||||
]
|
||||
|
||||
property int sortOrder: 0
|
||||
onSortOrderChanged: items.setGroups(0, items.count, "unsorted")
|
||||
|
||||
function insertPosition(lessThan, item) {
|
||||
var lower = 0
|
||||
var upper = items.count
|
||||
while (lower < upper) {
|
||||
var middle = Math.floor(lower + (upper - lower) / 2)
|
||||
var result = lessThan(item.model, items.get(middle).model);
|
||||
if (result) {
|
||||
upper = middle
|
||||
} else {
|
||||
lower = middle + 1
|
||||
}
|
||||
}
|
||||
return lower
|
||||
}
|
||||
|
||||
function sort(lessThan) {
|
||||
while (unsortedItems.count > 0) {
|
||||
var item = unsortedItems.get(0)
|
||||
var index = insertPosition(lessThan, item)
|
||||
item.groups = "items"
|
||||
items.move(item.itemsIndex, index)
|
||||
}
|
||||
}
|
||||
|
||||
items.includeByDefault: false
|
||||
groups: DelegateModelGroup {
|
||||
id: unsortedItems
|
||||
name: "unsorted"
|
||||
includeByDefault: true
|
||||
onChanged: {
|
||||
if (messageListDelegate.sortOrder == messageListDelegate.lessThan.length)
|
||||
setGroups(0, count, "items")
|
||||
else {
|
||||
messageListDelegate.sort(messageListDelegate.lessThan[messageListDelegate.sortOrder])
|
||||
}
|
||||
}
|
||||
}
|
||||
model: messageList
|
||||
|
||||
delegate: Message {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtGraphicalEffects 1.13
|
||||
import QtQuick.Layouts 1.13
|
||||
|
||||
import "../../../imports"
|
||||
|
@ -86,10 +87,74 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
property int nbRequests: chatsModel.activeCommunity.communityMembershipRequests.nbRequests
|
||||
|
||||
id: membershipRequestsBtn
|
||||
visible: nbRequests > 0
|
||||
width: parent.width
|
||||
height: visible ? 52 : 0
|
||||
color: Style.current.secondaryBackground
|
||||
anchors.top: communityHeader.bottom
|
||||
anchors.topMargin: visible ? Style.current.halfPadding : 0
|
||||
|
||||
StyledText {
|
||||
text: qsTr("Membership requests")
|
||||
font.pixelSize: 15
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: badge
|
||||
anchors.right: caret.left
|
||||
anchors.rightMargin: Style.current.padding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: Style.current.blue
|
||||
width: 22
|
||||
height: 22
|
||||
radius: width / 2
|
||||
Text {
|
||||
font.pixelSize: 12
|
||||
color: Style.current.white
|
||||
anchors.centerIn: parent
|
||||
text: membershipRequestsBtn.nbRequests.toString()
|
||||
}
|
||||
}
|
||||
|
||||
SVGImage {
|
||||
id: caret
|
||||
source: "../../img/caret.svg"
|
||||
fillMode: Image.PreserveAspectFit
|
||||
rotation: -90
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 13
|
||||
|
||||
|
||||
ColorOverlay {
|
||||
anchors.fill: parent
|
||||
source: parent
|
||||
color: Style.current.darkGrey
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: membershipRequestPopup.open()
|
||||
}
|
||||
}
|
||||
|
||||
MembershipRequestsPopup {
|
||||
id: membershipRequestPopup
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: chatGroupsContainer
|
||||
anchors.top: communityHeader.bottom
|
||||
anchors.top: membershipRequestsBtn.bottom
|
||||
anchors.topMargin: Style.current.padding
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
|
|
|
@ -39,7 +39,7 @@ ModalPopup {
|
|||
|
||||
ListView {
|
||||
anchors.fill: parent
|
||||
model: chatsModel.communities
|
||||
model: communitiesDelegateModel
|
||||
spacing: 4
|
||||
clip: true
|
||||
id: communitiesList
|
||||
|
@ -50,7 +50,7 @@ ModalPopup {
|
|||
width: parent.width
|
||||
height: childrenRect.height + Style.current.halfPadding
|
||||
StyledText {
|
||||
text: section
|
||||
text: section.toUpperCase()
|
||||
}
|
||||
Separator {
|
||||
anchors.left: popup.left
|
||||
|
@ -58,8 +58,19 @@ ModalPopup {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DelegateModelGeneralized {
|
||||
id: communitiesDelegateModel
|
||||
lessThan: [
|
||||
function(left, right) {
|
||||
return left.name.toLowerCase() < right.name.toLowerCase()
|
||||
}
|
||||
]
|
||||
|
||||
model: chatsModel.communities
|
||||
delegate: Item {
|
||||
// TODO add the serach for the name and category once they exist
|
||||
// TODO add the search for the name and category once they exist
|
||||
visible: {
|
||||
if (!searchBox.text) {
|
||||
return true
|
||||
|
@ -74,8 +85,7 @@ ModalPopup {
|
|||
id: communityImage
|
||||
width: 40
|
||||
height: 40
|
||||
// TODO get the real image once it's available
|
||||
source: "../../../img/ens-header-dark@2x.png"
|
||||
source: thumbnailImage
|
||||
}
|
||||
|
||||
StyledText {
|
||||
|
@ -100,11 +110,9 @@ ModalPopup {
|
|||
|
||||
StyledText {
|
||||
id: communityMembers
|
||||
text: nbMembers === 1 ?
|
||||
//% "1 member"
|
||||
qsTrId("1-member") :
|
||||
//% "%1 members"
|
||||
qsTrId("-1-members").arg(nbMembers)
|
||||
text: nbMembers === 1 ?
|
||||
qsTr("1 member") :
|
||||
qsTr("%1 members").arg(nbMembers)
|
||||
anchors.left: communityDesc.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: communityDesc.bottom
|
||||
|
@ -117,7 +125,7 @@ ModalPopup {
|
|||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (joined) {
|
||||
if (joined && isMember) {
|
||||
chatsModel.setActiveCommunity(id)
|
||||
} else {
|
||||
chatsModel.setObservedCommunity(id)
|
||||
|
|
|
@ -10,7 +10,7 @@ Rectangle {
|
|||
property string name: "channelName"
|
||||
property string description: "channel description"
|
||||
property string unviewedMessagesCount: "0"
|
||||
property string image: "../../../img/ens-header-dark@2x.png"
|
||||
property string image
|
||||
property bool hasMentions: false
|
||||
property string searchStr: ""
|
||||
property bool isCompact: appSettings.useCompactMode
|
||||
|
|
|
@ -11,9 +11,11 @@ ModalPopup {
|
|||
property string name: community.name
|
||||
property string description: community.description
|
||||
property int access: community.access
|
||||
// TODO get the real image once it's available
|
||||
property string source: "../../../img/ens-header-dark@2x.png"
|
||||
property string source: community.thumbnailImage
|
||||
property int nbMembers: community.nbMembers
|
||||
property bool ensOnly: community.ensOnly
|
||||
property bool canJoin: community.canJoin
|
||||
property bool canRequestAccess: community.canRequestAccess
|
||||
|
||||
id: popup
|
||||
|
||||
|
@ -41,7 +43,7 @@ ModalPopup {
|
|||
}
|
||||
|
||||
StyledText {
|
||||
// TODO get this from access property
|
||||
id: accessText
|
||||
text: {
|
||||
switch(access) {
|
||||
//% "Public community"
|
||||
|
@ -61,6 +63,17 @@ ModalPopup {
|
|||
font.weight: Font.Thin
|
||||
color: Style.current.secondaryText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: popup.ensOnly
|
||||
text: qsTr(" - ENS Only")
|
||||
anchors.left: accessText.right
|
||||
anchors.verticalCenter: accessText.verticalCenter
|
||||
anchors.topMargin: 2
|
||||
font.pixelSize: 15
|
||||
font.weight: Font.Thin
|
||||
color: Style.current.secondaryText
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
|
@ -168,11 +181,55 @@ ModalPopup {
|
|||
}
|
||||
|
||||
StatusButton {
|
||||
//% "Join ‘%1’"
|
||||
text: qsTrId("join---1-").arg(popup.name)
|
||||
property bool isPendingRequest: {
|
||||
if (access !== Constants.communityChatOnRequestAccess) {
|
||||
return false
|
||||
}
|
||||
return chatsModel.isCommunityRequestPending(communityId)
|
||||
}
|
||||
text: {
|
||||
if (ensOnly && !profileModel.profile.ensVerified) {
|
||||
return qsTr("Membership requires an ENS username")
|
||||
}
|
||||
if (canJoin) {
|
||||
return qsTr("Join ‘%1’").arg(popup.name);
|
||||
}
|
||||
if (isPendingRequest) {
|
||||
return qsTr("Pending")
|
||||
}
|
||||
switch(access) {
|
||||
case Constants.communityChatPublicAccess: return qsTr("Join ‘%1’").arg(popup.name);
|
||||
case Constants.communityChatInvitationOnlyAccess: return qsTr("You need to be invited");
|
||||
case Constants.communityChatOnRequestAccess: return qsTr("Request to join ‘%1’").arg(popup.name);
|
||||
default: return qsTr("Unknown community");
|
||||
}
|
||||
}
|
||||
enabled: {
|
||||
if (ensOnly && !profileModel.profile.ensVerified) {
|
||||
return false
|
||||
}
|
||||
if (canJoin) {
|
||||
return true
|
||||
}
|
||||
if (access === Constants.communityChatInvitationOnlyAccess || isPendingRequest) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
anchors.right: parent.right
|
||||
onClicked: {
|
||||
const error = chatsModel.joinCommunity(popup.communityId)
|
||||
let error
|
||||
if (access === Constants.communityChatOnRequestAccess) {
|
||||
error = chatsModel.requestToJoinCommunity(popup.communityId,
|
||||
profileModel.profile.ensVerified ? profileModel.profile.username : "")
|
||||
if (!error) {
|
||||
enabled = false
|
||||
text = qsTr("Pending")
|
||||
}
|
||||
} else {
|
||||
error = chatsModel.joinCommunity(popup.communityId)
|
||||
}
|
||||
|
||||
if (error) {
|
||||
joiningError.text = error
|
||||
|
|
|
@ -25,6 +25,7 @@ Item {
|
|||
name: model.name
|
||||
description: model.description
|
||||
searchStr: root.searchStr
|
||||
image: model.thumbnailImage
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,13 @@ ModalPopup {
|
|||
property string colorValidationError: ""
|
||||
property string selectedImageValidationError: ""
|
||||
property string selectedImage: ""
|
||||
property var imageDimensions: ({
|
||||
aX: 0,
|
||||
aY: 0,
|
||||
bY: 1,
|
||||
bY: 1
|
||||
})
|
||||
|
||||
property QtObject community: chatsModel.activeCommunity
|
||||
|
||||
property bool isEdit: false
|
||||
|
@ -51,13 +58,11 @@ ModalPopup {
|
|||
selectedImageValidationError = qsTrId("you-need-to-select-an-image")
|
||||
}
|
||||
|
||||
if (colorPicker.text === "") {
|
||||
//% "You need to enter a color"
|
||||
colorValidationError = qsTrId("you-need-to-enter-a-color")
|
||||
} else if (!Utils.isHexColor(colorPicker.text)) {
|
||||
//% "This field needs to be an hexadecimal color (eg: #4360DF)"
|
||||
colorValidationError = qsTrId("this-field-needs-to-be-an-hexadecimal-color--eg---4360df-")
|
||||
}
|
||||
// if (colorPicker.text === "") {
|
||||
// colorValidationError = qsTr("You need to enter a color")
|
||||
// } else if (!Utils.isHexColor(colorPicker.text)) {
|
||||
// colorValidationError = qsTr("This field needs to be an hexadecimal color (eg: #4360DF)")
|
||||
// }
|
||||
|
||||
return !nameValidationError && !descriptionTextArea.validationError && !colorValidationError
|
||||
}
|
||||
|
@ -73,8 +78,10 @@ ModalPopup {
|
|||
|
||||
id: scrollView
|
||||
anchors.fill: parent
|
||||
rightPadding: Style.current.padding
|
||||
anchors.rightMargin: - Style.current.halfPadding
|
||||
rightPadding: Style.current.bigPadding
|
||||
anchors.rightMargin: - Style.current.bigPadding
|
||||
leftPadding: Style.current.bigPadding
|
||||
anchors.leftMargin: - Style.current.bigPadding
|
||||
contentHeight: content.height
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
|
||||
|
@ -154,7 +161,8 @@ ModalPopup {
|
|||
qsTrId("image-files----jpg---jpeg---png-")
|
||||
]
|
||||
onAccepted: {
|
||||
selectedImage = imageDialog.fileUrls[0]
|
||||
popup.selectedImage = imageDialog.fileUrls[0]
|
||||
imageCropperModal.open()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,82 +235,108 @@ ModalPopup {
|
|||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: imageDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
Input {
|
||||
id: colorPicker
|
||||
//% "Community color"
|
||||
label: qsTrId("community-color")
|
||||
//% "Pick a color"
|
||||
placeholderText: qsTrId("pick-a-color")
|
||||
anchors.top: addImageButton.bottom
|
||||
anchors.topMargin: Style.current.smallPadding
|
||||
validationError: popup.colorValidationError
|
||||
|
||||
StatusIconButton {
|
||||
icon.name: "caret"
|
||||
iconRotation: -90
|
||||
iconColor: Style.current.textColor
|
||||
icon.width: 13
|
||||
icon.height: 7
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.smallPadding
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: colorPicker.textField.height / 2 - height / 2 + Style.current.bigPadding
|
||||
onClicked: colorDialog.open()
|
||||
}
|
||||
|
||||
ColorDialog {
|
||||
id: colorDialog
|
||||
//% "Please choose a color"
|
||||
title: qsTrId("please-choose-a-color")
|
||||
onAccepted: {
|
||||
colorPicker.text = colorDialog.color
|
||||
ImageCropperModal {
|
||||
id: imageCropperModal
|
||||
selectedImage: popup.selectedImage
|
||||
onCropFinished: {
|
||||
imageDimensions.aX = aX
|
||||
imageDimensions.aY = aY
|
||||
imageDimensions.bX = bX
|
||||
imageDimensions.bY = bY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO re-add color picker when status-go supports it
|
||||
// Input {
|
||||
// id: colorPicker
|
||||
// label: qsTr("Community color")
|
||||
// placeholderText: qsTr("Pick a color")
|
||||
// anchors.top: addImageButton.bottom
|
||||
// anchors.topMargin: Style.current.smallPadding
|
||||
// validationError: popup.colorValidationError
|
||||
|
||||
// StatusIconButton {
|
||||
// icon.name: "caret"
|
||||
// iconRotation: -90
|
||||
// iconColor: Style.current.textColor
|
||||
// icon.width: 13
|
||||
// icon.height: 7
|
||||
// anchors.right: parent.right
|
||||
// anchors.rightMargin: Style.current.smallPadding
|
||||
// anchors.top: parent.top
|
||||
// anchors.topMargin: colorPicker.textField.height / 2 - height / 2 + Style.current.bigPadding
|
||||
// onClicked: colorDialog.open()
|
||||
// }
|
||||
|
||||
// ColorDialog {
|
||||
// id: colorDialog
|
||||
// title: qsTr("Please choose a color")
|
||||
// onAccepted: {
|
||||
// colorPicker.text = colorDialog.color
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
Separator {
|
||||
id: separator1
|
||||
anchors.top: colorPicker.bottom
|
||||
anchors.top: addImageButton.bottom
|
||||
anchors.topMargin: isEdit ? 0 : Style.current.bigPadding
|
||||
visible: !isEdit
|
||||
}
|
||||
|
||||
Item {
|
||||
visible: !isEdit
|
||||
id: privateSwitcher
|
||||
height: visible ? privateSwitch.height : 0
|
||||
width: parent.width
|
||||
StatusSettingsLineButton {
|
||||
id: membershipRequirementSetting
|
||||
anchors.top: separator1.bottom
|
||||
anchors.topMargin: isEdit ? 0 : Style.current.smallPadding * 2
|
||||
|
||||
StyledText {
|
||||
//% "Private community"
|
||||
text: qsTrId("private-community")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.topMargin: Style.current.halfPadding
|
||||
text: qsTr("Membership requirement")
|
||||
currentValue: {
|
||||
switch (membershipRequirementSettingPopup.checkedMembership) {
|
||||
case Constants.communityChatInvitationOnlyAccess: return qsTr("Require invite from another member")
|
||||
case Constants.communityChatOnRequestAccess: return qsTr("Require approval")
|
||||
default: return qsTr("No requirement")
|
||||
}
|
||||
}
|
||||
|
||||
StatusSwitch {
|
||||
id: privateSwitch
|
||||
anchors.right: parent.right
|
||||
onClicked: {
|
||||
membershipRequirementSettingPopup.open()
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: !isEdit
|
||||
height: visible ? 50 : 0
|
||||
height: visible ? implicitHeight : 0
|
||||
id: privateExplanation
|
||||
anchors.top: privateSwitcher.bottom
|
||||
anchors.top: membershipRequirementSetting.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
anchors.topMargin: isEdit ? 0 : Style.current.smallPadding * 2
|
||||
anchors.topMargin: isEdit ? 0 : Style.current.halfPadding
|
||||
width: parent.width
|
||||
text: privateSwitch.checked ?
|
||||
//% "Only members with an invite link will be able to join your community. Private communities are not listed inside Status"
|
||||
qsTrId("only-members-with-an-invite-link-will-be-able-to-join-your-community--private-communities-are-not-listed-inside-status") :
|
||||
//% "Your community will be public for anyone to join. Public communities are listed inside Status for easy discovery"
|
||||
qsTrId("your-community-will-be-public-for-anyone-to-join--public-communities-are-listed-inside-status-for-easy-discovery")
|
||||
text: qsTr("You can require new members to meet certain criteria before they can join. This can be changed at any time")
|
||||
}
|
||||
|
||||
StatusSettingsLineButton {
|
||||
id: ensOnlySwitch
|
||||
anchors.top: privateExplanation.bottom
|
||||
anchors.topMargin: Style.current.padding
|
||||
text: qsTr("Require ENS username")
|
||||
isSwitch: true
|
||||
onClicked: switchChecked = checked
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: !isEdit
|
||||
height: visible ? implicitHeight : 0
|
||||
id: ensExplanation
|
||||
anchors.top: ensOnlySwitch.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
anchors.topMargin: isEdit ? 0 : Style.current.halfPadding
|
||||
width: parent.width
|
||||
text: qsTr("Your community requires an ENS username to be able to join")
|
||||
}
|
||||
}
|
||||
|
||||
MembershipRequirementPopup {
|
||||
id: membershipRequirementSettingPopup
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -325,8 +359,13 @@ ModalPopup {
|
|||
} else {
|
||||
error = chatsModel.createCommunity(Utils.filterXSS(nameInput.text),
|
||||
Utils.filterXSS(descriptionTextArea.text),
|
||||
colorPicker.text,
|
||||
popup.selectedImage)
|
||||
membershipRequirementSettingPopup.checkedMembership,
|
||||
ensOnlySwitch.switchChecked,
|
||||
popup.selectedImage,
|
||||
imageDimensions.aX,
|
||||
imageDimensions.aY,
|
||||
imageDimensions.bX,
|
||||
imageDimensions.bY)
|
||||
}
|
||||
|
||||
if (error) {
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtGraphicalEffects 1.13
|
||||
import "../../../../shared"
|
||||
import "../../../../shared/status"
|
||||
import "../../../../imports"
|
||||
import "."
|
||||
|
||||
Item {
|
||||
property string text
|
||||
property string description
|
||||
property var buttonGroup
|
||||
property bool checked: false
|
||||
property bool hideSeparator: false
|
||||
signal radioCheckedChanged(bool checked)
|
||||
|
||||
id: root
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
StatusRadioButtonRow {
|
||||
id: radioBtn
|
||||
text: root.text
|
||||
buttonGroup: root.buttonGroup
|
||||
checked: root.checked
|
||||
onRadioCheckedChanged: {
|
||||
root.radioCheckedChanged(checked)
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: radioDesc
|
||||
text: root.description
|
||||
anchors.top: radioBtn.bottom
|
||||
anchors.topMargin: Style.current.halfPadding
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 100
|
||||
font.pixelSize: 13
|
||||
color: Style.current.secondaryText
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Separator {
|
||||
visible: !root.hideSeparator
|
||||
anchors.top: radioDesc.bottom
|
||||
anchors.topMargin: visible ? Style.current.halfPadding : 0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
import QtQuick 2.12
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
import "../../../../shared/status"
|
||||
|
||||
ModalPopup {
|
||||
property alias membershipRequestList: membershipRequestList
|
||||
|
||||
id: popup
|
||||
|
||||
onOpened: {
|
||||
errorText.text = ""
|
||||
}
|
||||
|
||||
header: Item {
|
||||
height: 60
|
||||
width: parent.width
|
||||
|
||||
StyledText {
|
||||
id: titleText
|
||||
text: qsTr("Membership requests")
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 2
|
||||
anchors.left: parent.left
|
||||
font.bold: true
|
||||
font.pixelSize: 17
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: nbRequestsText
|
||||
text: membershipRequestList.count
|
||||
width: 160
|
||||
anchors.left: titleText.left
|
||||
anchors.top: titleText.bottom
|
||||
anchors.topMargin: 2
|
||||
font.pixelSize: 15
|
||||
color: Style.current.darkGrey
|
||||
}
|
||||
|
||||
Separator {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: -Style.current.padding
|
||||
anchors.leftMargin: -Style.current.padding
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
StyledText {
|
||||
id: errorText
|
||||
visible: !!text
|
||||
height: visible ? implicitHeight : 0
|
||||
color: Style.current.danger
|
||||
anchors.top: parent.top
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.Wrap
|
||||
anchors.topMargin: visible ? Style.current.smallPadding : 0
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: membershipRequestList
|
||||
model: chatsModel.activeCommunity.communityMembershipRequests
|
||||
anchors.top: errorText.bottom
|
||||
anchors.topMargin: Style.current.smallPadding
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: -Style.current.xlPadding
|
||||
anchors.leftMargin: -Style.current.xlPadding
|
||||
height: parent.height
|
||||
|
||||
delegate: Item {
|
||||
property int contactIndex: profileModel.contacts.list.getContactIndexByPubkey(publicKey)
|
||||
property string identicon: utilsModel.generateIdenticon(publicKey)
|
||||
property string profileImage: contactIndex === -1 ? identicon :
|
||||
profileModel.contacts.list.rowData(contactIndex, 'thumbnailImage') || identicon
|
||||
property string displayName: Utils.getDisplayName(publicKey, contactIndex)
|
||||
|
||||
id: requestLine
|
||||
height: 52
|
||||
width: parent.width
|
||||
|
||||
StatusImageIdenticon {
|
||||
id: accountImage
|
||||
width: 36
|
||||
height: 36
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
source: requestLine.profileImage
|
||||
anchors.leftMargin: Style.current.padding
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: requestLine.displayName
|
||||
anchors.left: accountImage.right
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.right: thumbsUp.left
|
||||
anchors.rightMargin: Style.current.padding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pixelSize: 15
|
||||
color: Style.current.darkGrey
|
||||
}
|
||||
|
||||
SVGImage {
|
||||
id: thumbsUp
|
||||
source: "../../../img/thumbsUp.svg"
|
||||
fillMode: Image.PreserveAspectFit
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: thumbsDown.left
|
||||
anchors.rightMargin: Style.current.padding
|
||||
width: 28
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
errorText.text = ""
|
||||
const error = chatsModel.acceptRequestToJoinCommunity(id)
|
||||
if (error) {
|
||||
errorText.text = error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SVGImage {
|
||||
id: thumbsDown
|
||||
source: "../../../img/thumbsDown.svg"
|
||||
fillMode: Image.PreserveAspectFit
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding
|
||||
width: 28
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
errorText.text = ""
|
||||
const error = chatsModel.declineRequestToJoinCommunity(id)
|
||||
if (error) {
|
||||
errorText.text = error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
footer: StatusRoundButton {
|
||||
id: btnBack
|
||||
anchors.left: parent.left
|
||||
icon.name: "arrow-right"
|
||||
icon.width: 20
|
||||
icon.height: 16
|
||||
rotation: 180
|
||||
onClicked: popup.close()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.3
|
||||
import QtGraphicalEffects 1.13
|
||||
import QtQuick.Dialogs 1.3
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
import "../../../../shared/status"
|
||||
|
||||
ModalPopup {
|
||||
property int checkedMembership: Constants.communityChatPublicAccess
|
||||
|
||||
id: popup
|
||||
height: 600
|
||||
|
||||
title: qsTr("Membership requirement")
|
||||
|
||||
ScrollView {
|
||||
property ScrollBar vScrollBar: ScrollBar.vertical
|
||||
|
||||
id: scrollView
|
||||
anchors.fill: parent
|
||||
rightPadding: Style.current.bigPadding
|
||||
anchors.rightMargin: - Style.current.bigPadding
|
||||
leftPadding: Style.current.bigPadding
|
||||
anchors.leftMargin: - Style.current.bigPadding
|
||||
contentHeight: content.height
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
clip: true
|
||||
|
||||
ButtonGroup {
|
||||
id: membershipRequirementGroup
|
||||
}
|
||||
|
||||
Column {
|
||||
id: content
|
||||
width: parent.width
|
||||
spacing: Style.current.padding
|
||||
|
||||
MembershipRadioButton {
|
||||
text: qsTr("Require approval")
|
||||
description: qsTr("Your community is free to join, but new members are required to be approved by the community creator first")
|
||||
buttonGroup: membershipRequirementGroup
|
||||
checked: popup.checkedMembership === Constants.communityChatOnRequestAccess
|
||||
onRadioCheckedChanged: {
|
||||
if (checked) {
|
||||
popup.checkedMembership = Constants.communityChatOnRequestAccess
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MembershipRadioButton {
|
||||
text: qsTr("Require invite from another member")
|
||||
description: qsTr("Your community can only be joined by an invitation from existing community members")
|
||||
buttonGroup: membershipRequirementGroup
|
||||
checked: popup.checkedMembership === Constants.communityChatInvitationOnlyAccess
|
||||
onRadioCheckedChanged: {
|
||||
if (checked) {
|
||||
popup.checkedMembership = Constants.communityChatInvitationOnlyAccess
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This should be a check box
|
||||
// MembershipRadioButton {
|
||||
// text: qsTr("Require ENS username")
|
||||
// description: qsTr("Your community requires an ENS username to be able to join")
|
||||
// buttonGroup: membershipRequirementGroup
|
||||
// }
|
||||
|
||||
MembershipRadioButton {
|
||||
text: qsTr("No requirement")
|
||||
description: qsTr("Your community is free for anyone to join")
|
||||
buttonGroup: membershipRequirementGroup
|
||||
hideSeparator: true
|
||||
checked: popup.checkedMembership === Constants.communityChatPublicAccess
|
||||
onRadioCheckedChanged: {
|
||||
if (checked) {
|
||||
popup.checkedMembership = Constants.communityChatPublicAccess
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: StatusIconButton {
|
||||
id: backButton
|
||||
icon.name: "leave_chat"
|
||||
width: 44
|
||||
height: 44
|
||||
iconColor: Style.current.primary
|
||||
highlighted: true
|
||||
icon.color: Style.current.primary
|
||||
icon.width: 28
|
||||
icon.height: 28
|
||||
radius: width / 2
|
||||
onClicked: {
|
||||
popup.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -54,51 +54,12 @@ ModalPopup {
|
|||
color: Style.current.danger
|
||||
}
|
||||
|
||||
ModalPopup {
|
||||
ImageCropperModal {
|
||||
id: cropImageModal
|
||||
width: image.width + 50
|
||||
height: image.height + 170
|
||||
//% "Crop your image (optional)"
|
||||
title: qsTrId("crop-your-image--optional-")
|
||||
|
||||
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
|
||||
//% "Finish"
|
||||
text: qsTrId("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()
|
||||
}
|
||||
selectedImage: popup.selectedImage
|
||||
onCropFinished: {
|
||||
uploadError = profileModel.uploadNewProfilePic(selectedImage, aX, aY, bX, bY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,52 +111,12 @@ ScrollView {
|
|||
section.criteria: ViewSection.FullString
|
||||
}
|
||||
|
||||
DelegateModel {
|
||||
DelegateModelGeneralized {
|
||||
id: messageListDelegate
|
||||
property var moreThan: [
|
||||
lessThan: [
|
||||
function(left, right) { return left.clock > right.clock }
|
||||
]
|
||||
|
||||
property int sortOrder: 0
|
||||
onSortOrderChanged: items.setGroups(0, items.count, "unsorted")
|
||||
|
||||
function insertPosition(moreThan, item) {
|
||||
var lower = 0
|
||||
var upper = items.count
|
||||
while (lower < upper) {
|
||||
var middle = Math.floor(lower + (upper - lower) / 2)
|
||||
var result = moreThan(item.model, items.get(middle).model);
|
||||
if (result) {
|
||||
upper = middle
|
||||
} else {
|
||||
lower = middle + 1
|
||||
}
|
||||
}
|
||||
return lower
|
||||
}
|
||||
|
||||
function sort(moreThan) {
|
||||
while (unsortedItems.count > 0) {
|
||||
var item = unsortedItems.get(0)
|
||||
var index = insertPosition(moreThan, item)
|
||||
item.groups = "items"
|
||||
items.move(item.itemsIndex, index)
|
||||
}
|
||||
}
|
||||
|
||||
items.includeByDefault: false
|
||||
groups: DelegateModelGroup {
|
||||
id: unsortedItems
|
||||
name: "unsorted"
|
||||
includeByDefault: true
|
||||
onChanged: {
|
||||
if (messageListDelegate.sortOrder == messageListDelegate.moreThan.length)
|
||||
setGroups(0, count, "items")
|
||||
else {
|
||||
messageListDelegate.sort(messageListDelegate.moreThan[messageListDelegate.sortOrder])
|
||||
}
|
||||
}
|
||||
}
|
||||
model: chatsModel.messageList
|
||||
|
||||
delegate: Message {
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<svg width="28" height="40" viewBox="0 0 28 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="14" cy="20" r="14" fill="#FF2D55"/>
|
||||
<path d="M21.9468 17.4414C21.9468 14.856 20.2915 12.7393 18.1675 12.7393H15.7578C14.769 12.2339 13.5972 11.9409 12.3081 11.9409H11.2827C10.3306 11.9409 9.51758 11.9995 8.99023 12.1313C7.91357 12.395 7.23242 13.1641 7.23242 14.1235C7.23242 14.292 7.26172 14.4531 7.30566 14.6069C6.80029 15.0024 6.51465 15.5737 6.51465 16.2036C6.51465 16.4966 6.57324 16.7822 6.67578 17.0239C6.33887 17.3828 6.14111 17.9028 6.14111 18.4375C6.14111 18.7891 6.229 19.1479 6.37549 19.4263C6.17041 19.7339 6.05322 20.1514 6.05322 20.6128C6.05322 21.7847 6.94678 22.6855 8.104 22.6855H10.8286C10.9824 22.6855 11.085 22.7588 11.0776 22.8979C11.041 23.6523 9.85449 25.4688 9.85449 26.9556C9.85449 28.0469 10.6235 28.8452 11.6855 28.8452C12.4546 28.8452 12.9819 28.4424 13.4873 27.4829C14.4028 25.7251 15.5015 24.187 17.1348 22.1729H18.4458C20.438 22.1729 21.9468 20.0708 21.9468 17.4414ZM17.3838 17.5C17.3838 19.0527 17.0396 20.0415 16.0435 21.3745C14.9375 22.854 13.3994 24.6411 12.2935 26.853C12.0591 27.3145 11.8979 27.4243 11.6709 27.4243C11.3926 27.4243 11.2095 27.2266 11.2095 26.875C11.2095 25.791 12.4473 23.9746 12.4473 22.7808C12.4473 21.9019 11.7368 21.3306 10.77 21.3306H8.14062C7.71582 21.3306 7.40088 21.0156 7.40088 20.5908C7.40088 20.2905 7.50342 20.0928 7.75244 19.8438C7.94287 19.646 7.97217 19.3677 7.80371 19.1772C7.59131 18.877 7.50342 18.6865 7.50342 18.4375C7.50342 18.1372 7.64258 17.8809 7.93555 17.6611C8.18457 17.4854 8.27246 17.1851 8.12598 16.8921C7.97217 16.5991 7.8916 16.4453 7.8916 16.2183C7.8916 15.8667 8.11865 15.5957 8.57275 15.354C8.81445 15.2222 8.88037 14.9658 8.77783 14.7314C8.62402 14.3579 8.60205 14.292 8.60938 14.1309C8.60938 13.8159 8.83643 13.5669 9.32715 13.4424C9.75928 13.3325 10.4478 13.2886 11.356 13.2959L12.3008 13.3032C15.333 13.3325 17.3838 15.0317 17.3838 17.5ZM20.6211 17.4414C20.6211 19.3018 19.6616 20.7886 18.563 20.8325C18.3726 20.8398 18.1821 20.8398 17.9917 20.8398C18.5117 19.8218 18.7388 18.7671 18.7388 17.5C18.7388 16.1523 18.27 14.9658 17.4204 14.0356C17.6914 14.0356 17.9697 14.043 18.248 14.043C19.5298 14.0942 20.6211 15.5957 20.6211 17.4414Z" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,4 @@
|
|||
<svg width="28" height="40" viewBox="0 0 28 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="14" cy="20" r="14" fill="#4EBC60"/>
|
||||
<path d="M6.05322 22.4878C6.05322 25.0732 7.7085 27.1899 9.83252 27.1899H12.2422C13.231 27.6953 14.4028 27.9883 15.6919 27.9883H16.7173C17.6694 27.9883 18.4824 27.9297 19.0098 27.7979C20.0864 27.5269 20.7676 26.7651 20.7676 25.8057C20.7676 25.6299 20.7456 25.4761 20.6943 25.3149C21.1997 24.9268 21.4854 24.3481 21.4854 23.7183C21.4854 23.4253 21.4268 23.1396 21.3242 22.8979C21.6611 22.5391 21.8589 22.0264 21.8589 21.4917C21.8589 21.1328 21.771 20.7812 21.6245 20.5029C21.8296 20.188 21.9468 19.7705 21.9468 19.3164C21.9468 18.1445 21.0532 17.2437 19.896 17.2437H17.1714C17.0176 17.2437 16.915 17.1704 16.9224 17.0312C16.959 16.2695 18.1455 14.4604 18.1455 12.9736C18.1455 11.8823 17.3765 11.084 16.3145 11.084C15.5454 11.084 15.0181 11.4868 14.5127 12.4463C13.6045 14.2041 12.4985 15.7422 10.8652 17.749H9.5542C7.56201 17.749 6.05322 19.8584 6.05322 22.4878ZM10.6162 22.4292C10.6162 20.8765 10.9604 19.8804 11.9565 18.5547C13.0625 17.0679 14.6006 15.2881 15.7065 13.0762C15.9409 12.6147 16.1021 12.4976 16.3364 12.4976C16.6147 12.4976 16.7979 12.7026 16.7979 13.0542C16.7979 14.1382 15.5527 15.9546 15.5527 17.1411C15.5527 18.0273 16.2632 18.5913 17.23 18.5913H19.8594C20.2842 18.5913 20.5991 18.9136 20.5991 19.3384C20.5991 19.6387 20.5039 19.8364 20.2476 20.0854C20.0571 20.2759 20.0278 20.5542 20.1963 20.752C20.4087 21.0449 20.4966 21.2427 20.4966 21.4917C20.4966 21.792 20.3574 22.0483 20.0645 22.2607C19.8154 22.4438 19.7275 22.7368 19.874 23.0298C20.0278 23.3301 20.1084 23.4766 20.1084 23.7109C20.1084 24.0625 19.8813 24.3262 19.4272 24.5679C19.1855 24.6997 19.1196 24.9634 19.2222 25.1904C19.376 25.564 19.3979 25.6372 19.3906 25.7983C19.3906 26.1133 19.1636 26.3623 18.6729 26.4868C18.2407 26.5894 17.5522 26.6406 16.644 26.6333L15.6992 26.626C12.667 26.5967 10.6162 24.8901 10.6162 22.4292ZM7.38623 22.4878C7.38623 20.6274 8.33838 19.1406 9.43701 19.0894C9.62744 19.0894 9.81787 19.0894 10.0083 19.0894C9.48828 20.1001 9.26123 21.1621 9.26123 22.4292C9.26123 23.7769 9.72998 24.9634 10.5869 25.8862C10.3159 25.8862 10.0303 25.8862 9.75195 25.8862C8.47021 25.835 7.38623 24.3262 7.38623 22.4878Z" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -30,6 +30,24 @@ QtObject {
|
|||
(!startsWith0x(value) && value.length === 64))
|
||||
}
|
||||
|
||||
function getDisplayName(publicKey, contactIndex) {
|
||||
if (contactIndex === undefined) {
|
||||
contactIndex = profileModel.contacts.list.getContactIndexByPubkey(publicKey)
|
||||
}
|
||||
|
||||
if (contactIndex === -1) {
|
||||
return utilsModel.generateAlias(publicKey)
|
||||
}
|
||||
const ensVerified = profileModel.contacts.list.rowData(contactIndex, 'ensVerified')
|
||||
if (!ensVerified) {
|
||||
const nickname = profileModel.contacts.list.rowData(contactIndex, 'localNickname')
|
||||
if (nickname) {
|
||||
return nickname
|
||||
}
|
||||
}
|
||||
return profileModel.contacts.list.rowData(contactIndex, 'name')
|
||||
}
|
||||
|
||||
function isMnemonic(value) {
|
||||
if(!value.match(/^([a-z\s]+)$/)){
|
||||
return false;
|
||||
|
|
|
@ -136,6 +136,9 @@ DISTFILES += \
|
|||
app/AppLayouts/Chat/CommunityComponents/CreateCommunityPopup.qml \
|
||||
app/AppLayouts/Chat/CommunityComponents/ImportCommunityPopup.qml \
|
||||
app/AppLayouts/Chat/CommunityComponents/InviteFriendsToCommunityPopup.qml \
|
||||
app/AppLayouts/Chat/CommunityComponents/MembershipRadioButton.qml \
|
||||
app/AppLayouts/Chat/CommunityComponents/MembershipRequestsPopup.qml \
|
||||
app/AppLayouts/Chat/CommunityComponents/MembershipRequirementPopup.qml \
|
||||
app/AppLayouts/Chat/ContactsColumn/AddChat.qml \
|
||||
app/AppLayouts/Chat/ContactsColumn/ClosedEmptyView.qml \
|
||||
app/AppLayouts/Chat/components/ChooseBrowserPopup.qml \
|
||||
|
@ -354,9 +357,11 @@ DISTFILES += \
|
|||
shared/AddButton.qml \
|
||||
shared/Address.qml \
|
||||
shared/CropCornerRectangle.qml \
|
||||
shared/DelegateModelGeneralized.qml \
|
||||
shared/FormGroup.qml \
|
||||
shared/IconButton.qml \
|
||||
shared/ImageCropper.qml \
|
||||
shared/ImageCropperModal.qml \
|
||||
shared/Input.qml \
|
||||
shared/LabelValueRow.qml \
|
||||
shared/ModalPopup.qml \
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
import QtQuick 2.13
|
||||
import QtQml.Models 2.3
|
||||
import "../imports"
|
||||
|
||||
DelegateModel {
|
||||
id: delegateModel
|
||||
property var lessThan
|
||||
|
||||
property int sortOrder: 0
|
||||
onSortOrderChanged: items.setGroups(0, items.count, "unsorted")
|
||||
|
||||
function insertPosition(lessThan, item) {
|
||||
var lower = 0
|
||||
var upper = items.count
|
||||
while (lower < upper) {
|
||||
var middle = Math.floor(lower + (upper - lower) / 2)
|
||||
var result = lessThan(item.model, items.get(middle).model);
|
||||
if (result) {
|
||||
upper = middle
|
||||
} else {
|
||||
lower = middle + 1
|
||||
}
|
||||
}
|
||||
return lower
|
||||
}
|
||||
|
||||
function sort(lessThan) {
|
||||
while (unsortedItems.count > 0) {
|
||||
var item = unsortedItems.get(0)
|
||||
var index = insertPosition(lessThan, item)
|
||||
|
||||
item.groups = "items"
|
||||
items.move(item.itemsIndex, index)
|
||||
}
|
||||
}
|
||||
|
||||
items.includeByDefault: false
|
||||
groups: DelegateModelGroup {
|
||||
id: unsortedItems
|
||||
name: "unsorted"
|
||||
|
||||
includeByDefault: true
|
||||
onChanged: {
|
||||
if (delegateModel.sortOrder === delegateModel.lessThan.length)
|
||||
setGroups(0, count, "items")
|
||||
else
|
||||
delegateModel.sort(delegateModel.lessThan[delegateModel.sortOrder])
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import "../imports"
|
||||
import "./status"
|
||||
|
||||
ModalPopup {
|
||||
property string selectedImage
|
||||
signal cropFinished(aX: int, aY: int, bX: int, bY: int)
|
||||
|
||||
id: cropImageModal
|
||||
width: image.width + 50
|
||||
height: image.height + 170
|
||||
title: qsTr("Crop your image (optional)")
|
||||
|
||||
Image {
|
||||
id: image
|
||||
width: 400
|
||||
source: cropImageModal.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: doneBtn
|
||||
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)
|
||||
|
||||
cropImageModal.cropFinished(aX, aY, bX, bY)
|
||||
cropImageModal.close()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +1 @@
|
|||
Subproject commit 363ab0a2ab8dce98193c899c2c202cc885a45143
|
||||
Subproject commit 09942bf200bd90bf3f36c5db2a911644fb6822e7
|
Loading…
Reference in New Issue