feat: channel permissions

Closes: #10996, #10995, #10993, #10997, #9537
This commit is contained in:
Pascal Precht 2023-06-08 13:01:01 +02:00 committed by Follow the white rabbit
parent 3e0a01883f
commit b3329d790e
35 changed files with 1104 additions and 339 deletions

View File

@ -127,3 +127,9 @@ method onMadeActive*(self: AccessInterface) {.base.} =
method onMadeInactive*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method onUpdateViewOnlyPermissionsSatisfied*(self: AccessInterface, value: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method onUpdateViewAndPostPermissionsSatisfied*(self: AccessInterface, value: bool) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -400,3 +400,9 @@ method amIChatAdmin*(self: Module): bool =
else:
let communityDto = self.controller.getCommunityDetails()
return communityDto.memberRole == MemberRole.Owner or communityDto.memberRole == MemberRole.Admin
method onUpdateViewOnlyPermissionsSatisfied*(self: Module, value: bool) =
self.view.setViewOnlyPermissionsSatisfied(value)
method onUpdateViewAndPostPermissionsSatisfied*(self: Module, value: bool) =
self.view.setViewAndPostPermissionsSatisfied(value)

View File

@ -13,6 +13,8 @@ QtObject:
pinnedMessagesModelVariant: QVariant
chatDetails: ChatDetails
chatDetailsVariant: QVariant
viewOnlyPermissionsSatisfied: bool
viewAndPostPermissionsSatisfied: bool
proc chatDetailsChanged*(self:View) {.signal.}
@ -31,6 +33,8 @@ QtObject:
result.pinnedMessagesModelVariant = newQVariant(result.pinnedMessagesModel)
result.chatDetails = newChatDetails()
result.chatDetailsVariant = newQVariant(result.chatDetails)
result.viewOnlyPermissionsSatisfied = false
result.viewAndPostPermissionsSatisfied = false
proc load*(self: View, id: string, `type`: int, belongsToCommunity, isUsersListAvailable: bool,
name, icon: string, color, description, emoji: string, hasUnreadMessages: bool,
@ -150,3 +154,28 @@ QtObject:
proc updateChatBlocked*(self: View, blocked: bool) =
self.chatDetails.setBlocked(blocked)
proc viewOnlyPermissionsSatisfiedChanged(self: View) {.signal.}
proc setViewOnlyPermissionsSatisfied*(self: View, value: bool) =
self.viewOnlyPermissionsSatisfied = value
self.viewOnlyPermissionsSatisfiedChanged()
proc getViewOnlyPermissionsSatisfied*(self: View): bool {.slot.} =
return self.viewOnlyPermissionsSatisfied
QtProperty[bool] viewOnlyPermissionsSatisfied:
read = getViewOnlyPermissionsSatisfied
notify = viewOnlyPermissionsSatisfiedChanged
proc viewAndPostPermissionsSatisfiedChanged(self: View) {.signal.}
proc setViewAndPostPermissionsSatisfied*(self: View, value: bool) =
self.viewAndPostPermissionsSatisfied = value
self.viewAndPostPermissionsSatisfiedChanged()
proc getViewAndPostPermissionsSatisfied*(self: View): bool {.slot.} =
return self.viewAndPostPermissionsSatisfied
QtProperty[bool] viewAndPostPermissionsSatisfied:
read = getViewAndPostPermissionsSatisfied
notify = viewAndPostPermissionsSatisfiedChanged

View File

@ -107,6 +107,21 @@ proc authenticateToRequestToJoinCommunity*(self: Controller, communityId: string
self.tmpRequestToJoinEnsName = ensName
self.authenticate()
proc getMySectionId*(self: Controller): string =
return self.sectionId
proc asyncCheckPermissionsToJoin*(self: Controller) =
self.communityService.asyncCheckPermissionsToJoin(self.getMySectionId())
proc asyncCheckAllChannelsPermissions*(self: Controller) =
self.communityService.asyncCheckAllChannelsPermissions(self.getMySectionId())
proc asyncCheckChannelPermissions*(self: Controller, communityId: string, chatId: string) =
self.communityService.asyncCheckChannelPermissions(communityId, chatId)
proc asyncCheckPermissions*(self: Controller) =
self.asyncCheckPermissionsToJoin()
self.asyncCheckAllChannelsPermissions()
proc init*(self: Controller) =
self.events.on(SIGNAL_SENDING_SUCCESS) do(e:Args):
@ -257,7 +272,7 @@ proc init*(self: Controller) =
let args = CommunityTokenPermissionArgs(e)
if (args.communityId == self.sectionId):
self.delegate.onCommunityTokenPermissionCreated(args.communityId, args.tokenPermission)
self.communityService.asyncCheckPermissionsToJoin(self.sectionId)
self.asyncCheckPermissions()
self.events.on(SIGNAL_COMMUNITY_TOKEN_PERMISSION_CREATION_FAILED) do(e: Args):
let args = CommunityTokenPermissionArgs(e)
@ -268,7 +283,7 @@ proc init*(self: Controller) =
let args = CommunityTokenPermissionArgs(e)
if (args.communityId == self.sectionId):
self.delegate.onCommunityTokenPermissionUpdated(args.communityId, args.tokenPermission)
self.communityService.asyncCheckPermissionsToJoin(self.sectionId)
self.asyncCheckPermissions()
self.events.on(SIGNAL_COMMUNITY_TOKEN_PERMISSION_UPDATE_FAILED) do(e: Args):
@ -280,7 +295,7 @@ proc init*(self: Controller) =
let args = CommunityTokenPermissionRemovedArgs(e)
if (args.communityId == self.sectionId):
self.delegate.onCommunityTokenPermissionDeleted(args.communityId, args.permissionId)
self.communityService.asyncCheckPermissionsToJoin(self.sectionId)
self.asyncCheckPermissions()
self.events.on(SIGNAL_COMMUNITY_TOKEN_PERMISSION_DELETION_FAILED) do(e: Args):
let args = CommunityTokenPermissionArgs(e)
@ -292,6 +307,16 @@ proc init*(self: Controller) =
if (args.communityId == self.sectionId):
self.delegate.onCommunityCheckPermissionsToJoinResponse(args.checkPermissionsToJoinResponse)
self.events.on(SIGNAL_CHECK_CHANNEL_PERMISSIONS_RESPONSE) do(e: Args):
let args = CheckChannelPermissionsResponseArgs(e)
if args.communityId == self.sectionId:
self.delegate.onCommunityCheckChannelPermissionsResponse(args.chatId, args.checkChannelPermissionsResponse)
self.events.on(SIGNAL_CHECK_ALL_CHANNELS_PERMISSIONS_RESPONSE) do(e: Args):
let args = CheckAllChannelsPermissionsResponseArgs(e)
if args.communityId == self.sectionId:
self.delegate.onCommunityCheckAllChannelsPermissionsResponse(args.checkAllChannelsPermissionsResponse)
self.events.on(SIGNAL_COMMUNITY_TOKEN_METADATA_ADDED) do(e: Args):
let args = CommunityTokenMetadataArgs(e)
if (args.communityId == self.sectionId):
@ -299,9 +324,11 @@ proc init*(self: Controller) =
self.events.on(SIGNAL_OWNED_COLLECTIBLES_UPDATE_FINISHED) do(e: Args):
self.delegate.onOwnedCollectiblesUpdated()
self.asyncCheckPermissions()
self.events.on(SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT) do(e: Args):
self.delegate.onWalletAccountTokensRebuilt()
self.asyncCheckPermissions()
self.events.on(SIGNAL_COMMUNITY_KICKED) do (e: Args):
let args = CommunityArgs(e)
@ -366,9 +393,6 @@ proc init*(self: Controller) =
self.events.on(SIGNAL_MAILSERVER_HISTORY_REQUEST_COMPLETED) do(e:Args):
self.delegate.setLoadingHistoryMessagesInProgress(false)
proc getMySectionId*(self: Controller): string =
return self.sectionId
proc isCommunity*(self: Controller): bool =
return self.isCommunitySection
@ -633,6 +657,12 @@ proc getColorHash*(self: Controller, pubkey: string): ColorHashDto =
proc getColorId*(self: Controller, pubkey: string): int =
procs_from_visual_identity_service.colorIdOf(pubkey)
proc checkChatHasPermissions*(self: Controller, communityId: string, chatId: string): bool =
return self.communityService.checkChatHasPermissions(communityId, chatId)
proc checkChatIsLocked*(self: Controller, communityId: string, chatId: string): bool =
return self.communityService.checkChatIsLocked(communityId, chatId)
proc createOrEditCommunityTokenPermission*(self: Controller, communityId: string, tokenPermission: CommunityTokenPermissionDto) =
self.communityService.createOrEditCommunityTokenPermission(communityId, tokenPermission)
@ -670,5 +700,3 @@ proc getContractAddressesForToken*(self: Controller, symbol: string): Table[int,
proc getCommunityTokenList*(self: Controller): seq[CommunityTokenDto] =
return self.communityTokensService.getCommunityTokens(self.getMySectionId())
proc asyncCheckPermissionsToJoin*(self: Controller) =
self.communityService.asyncCheckPermissionsToJoin(self.getMySectionId())

View File

@ -343,7 +343,7 @@ method switchToChannel*(self: AccessInterface, channelName: string) {.base.} =
method joinSpectatedCommunity*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method createOrEditCommunityTokenPermission*(self: AccessInterface, communityId: string, permissionId: string, permissionType: int, tokenCriteriaJson: string, isPrivate: bool) {.base.} =
method createOrEditCommunityTokenPermission*(self: AccessInterface, communityId: string, permissionId: string, permissionType: int, tokenCriteriaJson: string, channelIDs: seq[string], isPrivate: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method deleteCommunityTokenPermission*(self: AccessInterface, communityId: string, permissionId: string) {.base.} =
@ -398,4 +398,11 @@ method onOwnedcollectiblesUpdated*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method onCommunityCheckPermissionsToJoinResponse*(self: AccessInterface, checkPermissionsToJoinResponse: CheckPermissionsToJoinResponseDto) {.base.} =
raise newException(ValueError, "No implementation available")
method onCommunityCheckChannelPermissionsResponse*(self: AccessInterface, chatId: string, checkChannelPermissionsResponse: CheckChannelPermissionsResponseDto) {.base.} =
raise newException(ValueError, "No implementation available")
method onCommunityCheckAllChannelsPermissionsResponse*(self: AccessInterface, checkAllChannelsPermissionsResponse: CheckAllChannelsPermissionsResponseDto) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -33,6 +33,8 @@ type
trustStatus: TrustStatus
onlineStatus: OnlineStatus
loaderActive: bool
locked: bool
requiresPermissions: bool
proc initItem*(
id,
@ -58,7 +60,9 @@ proc initItem*(
categoryOpened: bool = true,
trustStatus: TrustStatus = TrustStatus.Unknown,
onlineStatus = OnlineStatus.Inactive,
loaderActive = false
loaderActive = false,
locked = false,
requiresPermissions = false
): Item =
result = Item()
result.id = id
@ -86,6 +90,8 @@ proc initItem*(
result.trustStatus = trustStatus
result.onlineStatus = onlineStatus
result.loaderActive = loaderActive
result.locked = locked
result.requiresPermissions = requiresPermissions
proc `$`*(self: Item): string =
result = fmt"""chat_section/Item(
@ -112,6 +118,8 @@ proc `$`*(self: Item): string =
trustStatus: {$self.trustStatus},
onlineStatus: {$self.onlineStatus},
loaderActive: {$self.loaderActive},
locked: {$self.locked},
requiresPermissions: {$self.requiresPermissions},
]"""
proc toJsonNode*(self: Item): JsonNode =
@ -138,7 +146,9 @@ proc toJsonNode*(self: Item): JsonNode =
"categoryOpened": self.categoryOpened,
"trustStatus": self.trustStatus,
"onlineStatus": self.onlineStatus,
"loaderActive": self.loaderActive
"loaderActive": self.loaderActive,
"locked": self.locked,
"requiresPermissions": self.requiresPermissions
}
proc delete*(self: Item) =
@ -278,3 +288,15 @@ proc `loaderActive=`*(self: var Item, value: bool) =
proc isCategory*(self: Item): bool =
self.`type` == CATEGORY_TYPE
proc isLocked*(self: Item): bool =
self.locked
proc `locked=`*(self: Item, value: bool) =
self.locked = value
proc requiresPermissions*(self: Item): bool =
self.requiresPermissions
proc `requiresPermissions=`*(self: Item, value: bool) =
self.requiresPermissions = value

View File

@ -31,6 +31,8 @@ type
OnlineStatus
IsCategory
LoaderActive
Locked
RequiresPermissions
QtObject:
type
@ -99,6 +101,8 @@ QtObject:
ModelRole.OnlineStatus.int:"onlineStatus",
ModelRole.IsCategory.int:"isCategory",
ModelRole.LoaderActive.int:"loaderActive",
ModelRole.Locked.int:"locked",
ModelRole.RequiresPermissions.int:"requiresPermissions",
}.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant =
@ -162,6 +166,10 @@ QtObject:
result = newQVariant(item.isCategory)
of ModelRole.LoaderActive:
result = newQVariant(item.loaderActive)
of ModelRole.Locked:
result = newQVariant(item.isLocked)
of ModelRole.RequiresPermissions:
result = newQVariant(item.requiresPermissions)
proc getItemIdxById(items: seq[Item], id: string): int =
var idx = 0
@ -274,6 +282,22 @@ QtObject:
else:
self.dataChanged(index, index, @[ModelRole.Active.int])
proc setItemLocked*(self: Model, id: string, locked: bool) =
let index = self.getItemIdxById(id)
if index == -1:
return
self.items[index].locked = locked
let modelIndex = self.createIndex(index, 0, nil)
self.dataChanged(modelIndex, modelIndex, @[ModelRole.Locked.int])
proc setItemPermissionsRequired*(self: Model, id: string, value: bool) =
let index = self.getItemIdxById(id)
if index == -1:
return
self.items[index].requiresPermissions = value
let modelIndex = self.createIndex(index, 0, nil)
self.dataChanged(modelIndex, modelIndex, @[ModelRole.RequiresPermissions.int])
proc changeMutedOnItemById*(self: Model, id: string, muted: bool) =
let index = self.getItemIdxById(id)
if index == -1:
@ -545,4 +569,4 @@ QtObject:
self.items[index].loaderActive = false
self.items[index].active = false
let modelIndex = self.createIndex(index, 0, nil)
self.dataChanged(modelIndex, modelIndex, @[ModelRole.Active.int, ModelRole.LoaderActive.int])
self.dataChanged(modelIndex, modelIndex, @[ModelRole.Active.int, ModelRole.LoaderActive.int])

View File

@ -13,6 +13,8 @@ import ../../shared_models/token_criteria_item
import ../../shared_models/token_criteria_model
import ../../shared_models/token_list_item
import ../../shared_models/token_list_model
import ../../shared_models/token_permission_chat_list_item
import ../../shared_models/token_permission_chat_list_model
import chat_content/module as chat_content_module
import chat_content/users/module as users_module
@ -69,6 +71,8 @@ proc buildChatSectionUI(self: Module,
gifService: gif_service.Service,
mailserversService: mailservers_service.Service)
proc reevaluateRequiresTokenPermissionToJoin(self: Module)
proc addOrUpdateChat(self: Module,
chat: ChatDto,
channelGroup: ChannelGroupDto,
@ -291,9 +295,17 @@ proc rebuildCommunityTokenPermissionsModel(self: Module) =
tokenPermissionsItem.getType() == TokenPermissionType.BecomeAdmin.int)
self.view.tokenPermissionsModel().setItems(tokenPermissionsItems)
self.view.setRequiresTokenPermissionToJoin(len(memberPermissions) > 0 or len(adminPermissions) > 0)
self.reevaluateRequiresTokenPermissionToJoin()
proc initCommunityTokenPermissionsModel(self: Module) =
proc reevaluateRequiresTokenPermissionToJoin(self: Module) =
let community = self.controller.getMyCommunity()
var hasBecomeMemberOrBecomeAdminPermissions = false
for id, tokenPermission in community.tokenPermissions:
if tokenPermission.`type` == TokenPermissionType.BecomeMember or tokenPermission.`type` == TokenPermissionType.BecomeAdmin:
hasBecomeMemberOrBecomeAdminPermissions = true
self.view.setRequiresTokenPermissionToJoin(hasBecomeMemberOrBecomeAdminPermissions)
proc initCommunityTokenPermissionsModel(self: Module, channelGroup: ChannelGroupDto) =
self.rebuildCommunityTokenPermissionsModel()
proc buildTokenList(self: Module) =
@ -390,14 +402,19 @@ method onChatsLoaded*(
self.usersModule.load()
let community = self.controller.getMyCommunity()
self.view.setAmIMember(community.joined)
self.initCommunityTokenPermissionsModel()
self.initCommunityTokenPermissionsModel(channelGroup)
self.controller.asyncCheckPermissionsToJoin()
let activeChatId = self.controller.getActiveChatId()
let isCurrentSectionActive = self.controller.getIsCurrentSectionActive()
for chatId, cModule in self.chatContentModules:
if isCurrentSectionActive and chatId == activeChatId:
cModule.onMadeActive()
if isCurrentSectionActive:
for chatId, cModule in self.chatContentModules:
if chatId == activeChatId:
cModule.onMadeActive()
if(self.controller.isCommunity()):
let community = self.controller.getMyCommunity()
self.controller.asyncCheckChannelPermissions(community.id, activeChatId)
self.view.chatsLoaded()
@ -475,6 +492,10 @@ method activeItemSet*(self: Module, itemId: string) =
# notify parent module about active chat/channel
self.delegate.onActiveChatChange(mySectionId, activeChatId)
self.delegate.onDeactivateChatLoader(deactivateSectionId, deactivateChatId)
if self.controller.isCommunity():
self.controller.asyncCheckChannelPermissions(mySectionId, activeChatId)
method getModuleAsVariant*(self: Module): QVariant =
return self.viewVariant
@ -496,6 +517,16 @@ proc updateParentBadgeNotifications(self: Module) =
unviewedMentionsCount
)
proc updateChatLocked(self: Module, chatId: string) =
let communityId = self.controller.getMySectionId()
let locked = self.controller.checkChatIsLocked(communityId, chatId)
self.view.chatsModel().setItemLocked(chatId, locked)
proc updateChatRequiresPermissions(self: Module, chatId: string) =
let communityId = self.controller.getMySectionId
let requiresPermissions = self.controller.checkChatHasPermissions(communityId, chatId)
self.view.chatsModel().setItemPermissionsRequired(chatId, requiresPermissions)
proc updateBadgeNotifications(self: Module, chat: ChatDto, hasUnreadMessages: bool, unviewedMentionsCount: int) =
let chatId = chat.id
@ -530,6 +561,11 @@ method onActiveSectionChange*(self: Module, sectionId: string) =
self.setFirstChannelAsActive()
else:
self.setActiveItem(activeChatId)
if self.isCommunity():
self.controller.asyncCheckPermissionsToJoin()
self.controller.asyncCheckAllChannelsPermissions()
self.delegate.onActiveChatChange(self.controller.getMySectionId(), self.controller.getActiveChatId())
method chatsModel*(self: Module): chats_model.Model =
@ -625,7 +661,10 @@ method addNewChat*(
categoryOpened,
onlineStatus = onlineStatus,
loaderActive = setChatAsActive,
locked = self.controller.checkChatIsLocked(self.controller.getMySectionId(), chatDto.id),
requiresPermissions = self.controller.checkChatHasPermissions(self.controller.getMySectionId(), chatDto.id)
)
self.addSubmodule(
chatDto.id,
belongsToCommunity,
@ -640,6 +679,7 @@ method addNewChat*(
gifService,
mailserversService,
)
self.chatContentModules[chatDto.id].load(result)
if insertIntoModel:
self.view.chatsModel().appendItem(result)
@ -786,19 +826,17 @@ method onCommunityTokenPermissionDeleted*(self: Module, communityId: string, per
method onCommunityTokenPermissionCreated*(self: Module, communityId: string, tokenPermission: CommunityTokenPermissionDto) =
let tokenPermissionItem = buildTokenPermissionItem(tokenPermission)
if tokenPermissionItem.tokenCriteriaMet:
self.view.setAllTokenRequirementsMet(true)
self.view.tokenPermissionsModel.addItem(tokenPermissionItem)
self.view.setRequiresTokenPermissionToJoin(true)
self.view.tokenPermissionsModel.addItem(tokenPermissionItem)
self.reevaluateRequiresTokenPermissionToJoin()
singletonInstance.globalEvents.showCommunityTokenPermissionCreatedNotification(communityId, "Community permission created", "A token permission has been added")
method onCommunityCheckPermissionsToJoinResponse*(self: Module, checkPermissionsToJoinResponse: CheckPermissionsToJoinResponseDto) =
let community = self.controller.getMyCommunity()
for id, criteriaResult in checkPermissionsToJoinResponse.permissions:
proc updateTokenPermissionModel*(self: Module, permissions: Table[string, CheckPermissionsResultDto], community: CommunityDto) =
for id, criteriaResult in permissions:
if community.tokenPermissions.hasKey(id):
let tokenPermissionItem = self.view.tokenPermissionsModel.getItemById(id)
if tokenPermissionItem.id == "":
continue
var updatedTokenCriteriaItems: seq[TokenCriteriaItem] = @[]
var permissionSatisfied = true
@ -823,7 +861,7 @@ method onCommunityCheckPermissionsToJoinResponse*(self: Module, checkPermissions
tokenPermissionItem.id,
tokenPermissionItem.`type`,
updatedTokenCriteriaItems,
@[], # TODO: handle chat list items
tokenPermissionItem.getChatList().getItems(),
tokenPermissionItem.isPrivate,
permissionSatisfied
)
@ -852,9 +890,23 @@ method onCommunityCheckPermissionsToJoinResponse*(self: Module, checkPermissions
self.view.setRequiresTokenPermissionToJoin(requiresPermissionToJoin)
proc updateChannelPermissionViewData*(self: Module, chatId: string, viewOnlyPermissions: ViewOnlyOrViewAndPostPermissionsResponseDto, viewAndPostPermissions: ViewOnlyOrViewAndPostPermissionsResponseDto, community: CommunityDto) =
self.updateTokenPermissionModel(viewOnlyPermissions.permissions, community)
self.updateTokenPermissionModel(viewAndPostPermissions.permissions, community)
self.updateChatRequiresPermissions(chatId)
self.updateChatLocked(chatId)
self.chatContentModules[chatId].onUpdateViewOnlyPermissionsSatisfied(viewOnlyPermissions.satisfied)
self.chatContentModules[chatId].onUpdateViewAndPostPermissionsSatisfied(viewAndPostPermissions.satisfied)
method onCommunityCheckPermissionsToJoinResponse*(self: Module, checkPermissionsToJoinResponse: CheckPermissionsToJoinResponseDto) =
let community = self.controller.getMyCommunity()
self.view.setAllTokenRequirementsMet(checkPermissionsToJoinResponse.satisfied)
self.updateTokenPermissionModel(checkPermissionsToJoinResponse.permissions, community)
method onCommunityTokenPermissionUpdated*(self: Module, communityId: string, tokenPermission: CommunityTokenPermissionDto) =
let tokenPermissionItem = buildTokenPermissionItem(tokenPermission)
self.view.tokenPermissionsModel.updateItem(tokenPermission.id, tokenPermissionItem)
self.reevaluateRequiresTokenPermissionToJoin()
singletonInstance.globalEvents.showCommunityTokenPermissionUpdatedNotification(communityId, "Community permission updated", "A token permission has been updated")
@ -867,6 +919,15 @@ method onCommunityTokenPermissionUpdateFailed*(self: Module, communityId: string
method onCommunityTokenPermissionDeletionFailed*(self: Module, communityId: string) =
singletonInstance.globalEvents.showCommunityTokenPermissionDeletionFailedNotification(communityId, "Failed to delete community permission", "Something went wrong")
method onCommunityCheckChannelPermissionsResponse*(self: Module, chatId: string, checkChannelPermissionsResponse: CheckChannelPermissionsResponseDto) =
let community = self.controller.getMyCommunity()
self.updateChannelPermissionViewData(chatId, checkChannelPermissionsResponse.viewOnlyPermissions, checkChannelPermissionsResponse.viewAndPostPermissions, community)
method onCommunityCheckAllChannelsPermissionsResponse*(self: Module, checkAllChannelsPermissionsResponse: CheckAllChannelsPermissionsResponseDto) =
let community = self.controller.getMyCommunity()
for chatId, permissionResult in checkAllChannelsPermissionsResponse.channels:
self.updateChannelPermissionViewData(chatId, permissionResult.viewOnlyPermissions, permissionResult.viewAndPostPermissions, community)
method onCommunityTokenMetadataAdded*(self: Module, communityId: string, tokenMetadata: CommunityTokensMetadataDto) =
let tokenListItem = initTokenListItem(
key = tokenMetadata.symbol,
@ -1207,6 +1268,8 @@ proc addOrUpdateChat(self: Module,
if chatExists:
self.changeMutedOnChat(chat.id, chat.muted)
self.updateChatRequiresPermissions(chat.id)
self.updateChatLocked(chat.id)
if (chat.chatType == ChatType.PrivateGroupChat):
self.onGroupChatDetailsUpdated(chat.id, chat.name, chat.color, chat.icon)
elif (chat.chatType != ChatType.OneToOne):
@ -1278,13 +1341,19 @@ method joinSpectatedCommunity*(self: Module) =
if self.usersModule != nil:
self.usersModule.updateMembersList()
method createOrEditCommunityTokenPermission*(self: Module, communityId: string, permissionId: string, permissionType: int, tokenCriteriaJson: string, isPrivate: bool) =
method createOrEditCommunityTokenPermission*(self: Module, communityId: string, permissionId: string, permissionType: int, tokenCriteriaJson: string, channelIDs: seq[string], isPrivate: bool) =
var tokenPermission = CommunityTokenPermissionDto()
tokenPermission.id = permissionId
tokenPermission.isPrivate = isPrivate
tokenPermission.`type` = TokenPermissionType(permissionType)
tokenPermission.chatIDs = channelIDs
if tokenPermission.`type` != TokenPermissionType.View and tokenPermission.`type` != TokenPermissionType.ViewAndPost:
tokenPermission.chatIDs = @[]
let tokenCriteriaJsonObj = tokenCriteriaJson.parseJson
for tokenCriteria in tokenCriteriaJsonObj:
let viewAmount = tokenCriteria{"amount"}.getFloat

View File

@ -1,4 +1,4 @@
import NimQml, json, sequtils
import NimQml, json, sequtils, strutils
import model as chats_model
import item, active_item
import ../../shared_models/user_model as user_model
@ -401,8 +401,10 @@ QtObject:
QtProperty[QVariant] permissionsModel:
read = getTokenPermissionsModel
proc createOrEditCommunityTokenPermission*(self: View, communityId: string, permissionId: string, permissionType: int, tokenCriteriaJson: string, isPrivate: bool) {.slot.} =
self.delegate.createOrEditCommunityTokenPermission(communityId, permissionId, permissionType, tokenCriteriaJson, isPrivate)
proc createOrEditCommunityTokenPermission*(self: View, communityId: string, permissionId: string, permissionType: int, tokenCriteriaJson: string, channelIDs: string, isPrivate: bool) {.slot.} =
let chatIDs = channelIDs.split(',')
self.delegate.createOrEditCommunityTokenPermission(communityId, permissionId, permissionType, tokenCriteriaJson, chatIDs, isPrivate)
proc deleteCommunityTokenPermission*(self: View, communityId: string, permissionId: string) {.slot.} =
self.delegate.deleteCommunityTokenPermission(communityId, permissionId)
@ -450,4 +452,4 @@ QtObject:
QtProperty[bool] allTokenRequirementsMet:
read = getAllTokenRequirementsMet
notify = allTokenRequirementsMetChanged
notify = allTokenRequirementsMetChanged

View File

@ -3,23 +3,17 @@ import strformat
type
TokenPermissionChatListItem* = object
key: string
name: string
proc `$`*(self: TokenPermissionChatListItem): string =
result = fmt"""TokenPermissionChatListItem(
key: {self.key},
name: {self.name},
key: {self.key}
]"""
proc initTokenPermissionChatListItem*(
key: string,
name: string,
key: string
): TokenPermissionChatListItem =
result.key = key
result.name = name
proc getKey*(self: TokenPermissionChatListItem): string =
return self.key
proc getName*(self: TokenPermissionChatListItem): string =
return self.name

View File

@ -4,7 +4,6 @@ import token_permission_chat_list_item
type
ModelRole {.pure.} = enum
Key = UserRole + 1
Name
QtObject:
type TokenPermissionChatListModel* = ref object of QAbstractListModel
@ -24,7 +23,6 @@ QtObject:
method roleNames(self: TokenPermissionChatListModel): Table[int, string] =
{
ModelRole.Key.int:"key",
ModelRole.Name.int:"name",
}.toTable
proc countChanged(self: TokenPermissionChatListModel) {.signal.}
@ -47,8 +45,6 @@ QtObject:
case enumRole:
of ModelRole.Key:
result = newQVariant(item.getKey())
of ModelRole.Name:
result = newQVariant(item.getName())
proc addItem*(self: TokenPermissionChatListModel, item: TokenPermissionChatListItem) =
let parentModelIndex = newQModelIndex()
@ -57,3 +53,12 @@ QtObject:
self.items.add(item)
self.endInsertRows()
self.countChanged()
proc setItems*(self: TokenPermissionChatListModel, items: seq[TokenPermissionChatListItem]) =
self.beginResetModel()
self.items = items
self.endResetModel()
self.countChanged()
proc getItems*(self: TokenPermissionChatListModel): seq[TokenPermissionChatListItem] =
return self.items

View File

@ -63,7 +63,6 @@ proc getIsPrivate*(self: TokenPermissionItem): bool =
proc getTokenCriteriaMet*(self: TokenPermissionItem): bool =
return self.tokenCriteriaMet
proc buildTokenPermissionItem*(tokenPermission: CommunityTokenPermissionDto): TokenPermissionItem =
var tokenCriteriaItems: seq[TokenCriteriaItem] = @[]
@ -80,11 +79,15 @@ proc buildTokenPermissionItem*(tokenPermission: CommunityTokenPermissionDto): To
tokenCriteriaItems.add(tokenCriteriaItem)
var tokenPermissionChatListItems: seq[TokenPermissionChatListItem] = @[]
for chatID in tokenPermission.chatIDs:
tokenPermissionChatListItems.add(initTokenPermissionChatListItem(chatID))
let tokenPermissionItem = initTokenPermissionItem(
tokenPermission.id,
tokenPermission.`type`.int,
tokenCriteriaItems,
@[], # TODO: handle chat list items
tokenPermissionChatListItems,
tokenPermission.isPrivate,
false # allTokenCriteriaMet will be update by a call to checkPermissinosToJoin
)

View File

@ -1,5 +1,7 @@
import NimQml, Tables
import token_permission_item
import token_permission_chat_list_item
import token_permission_chat_list_model
import token_criteria_model
type
@ -10,6 +12,7 @@ type
TokenCriteria
ChatList
IsPrivate
TokenCriteriaMet
QtObject:
type TokenPermissionsModel* = ref object of QAbstractListModel
@ -34,15 +37,34 @@ QtObject:
ModelRole.TokenCriteria.int:"holdingsListModel",
ModelRole.ChatList.int:"channelsListModel",
ModelRole.IsPrivate.int:"isPrivate",
ModelRole.TokenCriteriaMet.int:"tokenCriteriaMet",
}.toTable
proc countChanged(self: TokenPermissionsModel) {.signal.}
proc getCount*(self: TokenPermissionsModel): int {.slot.} =
return self.items.len
QtProperty[int] count:
read = getCount
notify = countChanged
proc findIndexById(self: TokenPermissionsModel, id: string): int =
for i in 0 ..< self.items.len:
if(self.items[i].getId() == id):
return i
return -1
proc belongsToChat*(self: TokenPermissionsModel, permissionId: string, chatId: string): bool {.slot.} =
let idx = self.findIndexById(permissionId)
if(idx == -1):
return false
for clItem in self.items[idx].chatList.getItems():
if clItem.getKey() == chatId:
return true
return false
method rowCount(self: TokenPermissionsModel, index: QModelIndex = nil): int =
return self.items.len
@ -66,6 +88,8 @@ QtObject:
result = newQVariant(item.getChatList())
of ModelRole.IsPrivate:
result = newQVariant(item.getIsPrivate())
of ModelRole.TokenCriteriaMet:
result = newQVariant(item.getTokenCriteriaMet())
proc addItem*(self: TokenPermissionsModel, item: TokenPermissionItem) =
let parentModelIndex = newQModelIndex()
@ -84,12 +108,6 @@ QtObject:
proc getItems*(self: TokenPermissionsModel): seq[TokenPermissionItem] =
return self.items
proc findIndexById(self: TokenPermissionsModel, id: string): int =
for i in 0 ..< self.items.len:
if(self.items[i].getId() == id):
return i
return -1
proc removeItemWithId*(self: TokenPermissionsModel, permissionId: string) =
let idx = self.findIndexById(permissionId)
if(idx == -1):
@ -117,6 +135,7 @@ QtObject:
self.items[idx].`type` = item.`type`
self.items[idx].tokenCriteria.setItems(item.tokenCriteria.getItems())
self.items[idx].chatList.setItems(item.chatList.getItems())
self.items[idx].isPrivate = item.isPrivate
self.items[idx].tokenCriteriaMet = item.tokenCriteriaMet
@ -127,4 +146,5 @@ QtObject:
ModelRole.Type.int,
ModelRole.TokenCriteria.int,
ModelRole.IsPrivate.int,
ModelRole.TokenCriteriaMet.int
])

View File

@ -121,3 +121,44 @@ const asyncCheckPermissionsToJoinTask: Task = proc(argEncoded: string) {.gcsafe,
"communityId": arg.communityId,
"error": e.msg,
})
type
AsyncCheckChannelPermissionsTaskArg = ref object of QObjectTaskArg
communityId: string
chatId: string
const asyncCheckChannelPermissionsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AsyncCheckChannelPermissionsTaskArg](argEncoded)
try:
let response = status_go.checkCommunityChannelPermissions(arg.communityId, arg.chatId)
arg.finish(%* {
"response": response,
"communityId": arg.communityId,
"chatId": arg.chatId,
"error": "",
})
except Exception as e:
arg.finish(%* {
"communityId": arg.communityId,
"chatId": arg.chatId,
"error": e.msg,
})
type
AsyncCheckAllChannelsPermissionsTaskArg = ref object of QObjectTaskArg
communityId: string
const asyncCheckAllChannelsPermissionsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AsyncCheckAllChannelsPermissionsTaskArg](argEncoded)
try:
let response = status_go.checkAllCommunityChannelsPermissions(arg.communityId)
arg.finish(%* {
"response": response,
"communityId": arg.communityId,
"error": "",
})
except Exception as e:
arg.finish(%* {
"communityId": arg.communityId,
"error": e.msg,
})

View File

@ -39,7 +39,6 @@ type TokenPermissionType* {.pure.}= enum
View = 3,
ViewAndPost = 4,
type TokenType* {.pure.}= enum
Unknown = 0,
ERC20 = 1,
@ -71,6 +70,29 @@ type CommunityTokensMetadataDto* = object
name*: string
tokenType*: TokenType
type AccountChainIDsCombinationDto* = object
address*: string
chainIds*: seq[int]
type CheckPermissionsResultDto* = object
criteria*: seq[bool]
type CheckPermissionsToJoinResponseDto* = object
satisfied*: bool
permissions*: Table[string, CheckPermissionsResultDto]
validCombinations*: seq[AccountChainIDsCombinationDto]
type ViewOnlyOrViewAndPostPermissionsResponseDto* = object
satisfied*: bool
permissions*: Table[string, CheckPermissionsResultDto]
type CheckChannelPermissionsResponseDto* = object
viewOnlyPermissions*: ViewOnlyOrViewAndPostPermissionsResponseDto
viewAndPostPermissions*: ViewOnlyOrViewAndPostPermissionsResponseDto
type CheckAllChannelsPermissionsResponseDto* = object
channels*: Table[string, CheckChannelPermissionsResponseDto]
type CommunityDto* = object
id*: string
memberRole*: MemberRole
@ -106,6 +128,7 @@ type CommunityDto* = object
canceledRequestsToJoin*: seq[CommunityMembershipRequestDto]
tokenPermissions*: Table[string, CommunityTokenPermissionDto]
communityTokensMetadata*: seq[CommunityTokensMetadataDto]
channelPermissions*: CheckAllChannelsPermissionsResponseDto
activeMembersCount*: int64
proc isAvailable*(communityDto: CommunityDto): bool =
@ -140,18 +163,6 @@ type DiscordImportTaskProgress* = object
stopped*: bool
state*: string
type AccountChainIDsCombinationDto* = object
address*: string
chainIds*: seq[int]
type CheckPermissionToJoinResultDto* = object
criteria*: seq[bool]
type CheckPermissionsToJoinResponseDto* = object
satisfied*: bool
permissions*: Table[string, CheckPermissionToJoinResultDto]
validCombinations*: seq[AccountChainIDsCombinationDto]
proc toCommunityAdminSettingsDto*(jsonObj: JsonNode): CommunityAdminSettingsDto =
result = CommunityAdminSettingsDto()
discard jsonObj.getProp("pinMessageAllMembersEnabled", result.pinMessageAllMembersEnabled)
@ -241,7 +252,7 @@ proc toTokenCriteriaDto*(jsonObj: JsonNode): TokenCriteriaDto =
proc toCommunityTokenPermissionDto*(jsonObj: JsonNode): CommunityTokenPermissionDto =
result = CommunityTokenPermissionDto()
discard jsonObj.getProp("id", result.id)
discard jsonObj.getProp("isPrivate", result.isPrivate)
discard jsonObj.getProp("is_private", result.isPrivate)
var tokenPermissionTypeInt: int
discard jsonObj.getProp("type", tokenPermissionTypeInt)
if (tokenPermissionTypeInt >= ord(low(TokenPermissionType)) or tokenPermissionTypeInt <= ord(high(TokenPermissionType))):
@ -253,7 +264,7 @@ proc toCommunityTokenPermissionDto*(jsonObj: JsonNode): CommunityTokenPermission
result.tokenCriteria.add(tokenCriteria.toTokenCriteriaDto)
var chatIdsObj: JsonNode
if(jsonObj.getProp("chatIds", chatIdsObj) and chatIdsObj.kind == JArray):
if(jsonObj.getProp("chat_ids", chatIdsObj) and chatIdsObj.kind == JArray):
for chatId in chatIdsObj:
result.chatIds.add(chatId.getStr)
@ -262,8 +273,8 @@ proc toCommunityTokenPermissionDto*(jsonObj: JsonNode): CommunityTokenPermission
if jsonObj.hasKey("key"):
discard jsonObj.getProp("key", result.id)
proc toCheckPermissionToJoinResultDto*(jsonObj: JsonNode): CheckPermissionToJoinResultDto =
result = CheckPermissionToJoinResultDto()
proc toCheckPermissionsResultDto*(jsonObj: JsonNode): CheckPermissionsResultDto =
result = CheckPermissionsResultDto()
var criteriaObj: JsonNode
if(jsonObj.getProp("criteria", criteriaObj) and criteriaObj.kind == JArray):
for c in criteriaObj:
@ -288,9 +299,39 @@ proc toCheckPermissionsToJoinResponseDto*(jsonObj: JsonNode): CheckPermissionsTo
var permissionsObj: JsonNode
if(jsonObj.getProp("permissions", permissionsObj) and permissionsObj.kind == JObject):
result.permissions = initTable[string, CheckPermissionToJoinResultDto]()
result.permissions = initTable[string, CheckPermissionsResultDto]()
for permissionId, permission in permissionsObj:
result.permissions[permissionId] = permission.toCheckPermissionToJoinResultDto
result.permissions[permissionId] = permission.toCheckPermissionsResultDto
proc toViewOnlyOrViewAndPostPermissionsResponseDto*(jsonObj: JsonNode): ViewOnlyOrViewAndPostPermissionsResponseDto =
result = ViewOnlyOrViewAndPostPermissionsResponseDto()
discard jsonObj.getProp("satisfied", result.satisfied)
var permissionsObj: JsonNode
if(jsonObj.getProp("permissions", permissionsObj) and permissionsObj.kind == JObject):
result.permissions = initTable[string, CheckPermissionsResultDto]()
for permissionId, permission in permissionsObj:
result.permissions[permissionId] = permission.toCheckPermissionsResultDto
proc toCheckChannelPermissionsResponseDto*(jsonObj: JsonNode): CheckChannelPermissionsResponseDto =
result = CheckChannelPermissionsResponseDto()
var viewOnlyPermissionsObj: JsonNode
if(jsonObj.getProp("viewOnlyPermissions", viewOnlyPermissionsObj) and viewOnlyPermissionsObj.kind == JObject):
result.viewOnlyPermissions = viewOnlyPermissionsObj.toViewOnlyOrViewAndPostPermissionsResponseDto()
var viewAndPostPermissionsObj: JsonNode
if(jsonObj.getProp("viewAndPostPermissions", viewAndPostPermissionsObj) and viewAndPostPermissionsObj.kind == JObject):
result.viewAndPostPermissions = viewAndPostPermissionsObj.toViewOnlyOrViewAndPostPermissionsResponseDto()
proc toCheckAllChannelsPermissionsResponseDto*(jsonObj: JsonNode): CheckAllChannelsPermissionsResponseDto =
result = CheckAllChannelsPermissionsResponseDto()
result.channels = initTable[string, CheckChannelPermissionsResponseDto]()
var channelsObj: JsonNode
if(jsonObj.getProp("channels", channelsObj) and channelsObj.kind == JObject):
for channelId, permissionResponse in channelsObj:
result.channels[channelId] = permissionResponse.toCheckChannelPermissionsResponseDto()
proc toCommunityDto*(jsonObj: JsonNode): CommunityDto =
result = CommunityDto()

View File

@ -117,6 +117,15 @@ type
communityId*: string
checkPermissionsToJoinResponse*: CheckPermissionsToJoinResponseDto
CheckChannelPermissionsResponseArgs* = ref object of Args
communityId*: string
chatId*: string
checkChannelPermissionsResponse*: CheckChannelPermissionsResponseDto
CheckAllChannelsPermissionsResponseArgs* = ref object of Args
communityId*: string
checkAllChannelsPermissionsResponse*: CheckAllChannelsPermissionsResponseDto
# Signals which may be emitted by this service:
const SIGNAL_COMMUNITY_DATA_LOADED* = "communityDataLoaded"
const SIGNAL_COMMUNITY_JOINED* = "communityJoined"
@ -179,6 +188,8 @@ const TOKEN_PERMISSIONS_ADDED = "tokenPermissionsAdded"
const TOKEN_PERMISSIONS_MODIFIED = "tokenPermissionsModified"
const SIGNAL_CHECK_PERMISSIONS_TO_JOIN_RESPONSE* = "checkPermissionsToJoinResponse"
const SIGNAL_CHECK_CHANNEL_PERMISSIONS_RESPONSE* = "checkChannelPermissionsResponse"
const SIGNAL_CHECK_ALL_CHANNELS_PERMISSIONS_RESPONSE* = "checkAllChannelsPermissionsResponse"
QtObject:
type
@ -581,6 +592,7 @@ QtObject:
if tokenPermission.tokenCriteria.len != prevTokenPermission.tokenCriteria.len or
tokenPermission.isPrivate != prevTokenPermission.isPrivate or
tokenPermission.chatIds.len != prevTokenPermission.chatIds.len or
tokenPermission.`type` != prevTokenPermission.`type`:
permissionUpdated = true
@ -588,14 +600,16 @@ QtObject:
for tc in tokenPermission.tokenCriteria:
let index = findIndexBySymbol(tc.symbol, prevTokenPermission.tokenCriteria)
if index == -1:
continue
let prevTc = prevTokenPermission.tokenCriteria[index]
if tc.amount != prevTc.amount or tc.ensPattern != prevTc.ensPattern:
permissionUpdated = true
break
else:
let prevTc = prevTokenPermission.tokenCriteria[index]
if tc.amount != prevTc.amount or tc.ensPattern != prevTc.ensPattern or tc.symbol != prevTc.symbol or tc.name != prevTc.name or tc.decimals != prevTc.decimals:
permissionUpdated = true
break
if permissionUpdated:
self.communities[community.id].tokenPermissions[id] = tokenPermission
self.events.emit(SIGNAL_COMMUNITY_TOKEN_PERMISSION_UPDATED,
CommunityTokenPermissionArgs(communityId: community.id, tokenPermission: tokenPermission))
@ -1371,16 +1385,13 @@ QtObject:
self.events.emit(SIGNAL_COMMUNITY_DATA_IMPORTED, CommunityArgs(community: community))
proc asyncCheckPermissionsToJoin*(self: Service, communityId: string) =
try:
let arg = AsyncCheckPermissionsToJoinTaskArg(
tptr: cast[ByteAddress](asyncCheckPermissionsToJoinTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onAsyncCheckPermissionsToJoinDone",
communityId: communityId
)
self.threadpool.start(arg)
except Exception as e:
error "Error checking permissions to join community", msg = e.msg
let arg = AsyncCheckPermissionsToJoinTaskArg(
tptr: cast[ByteAddress](asyncCheckPermissionsToJoinTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onAsyncCheckPermissionsToJoinDone",
communityId: communityId
)
self.threadpool.start(arg)
proc onAsyncCheckPermissionsToJoinDone*(self: Service, rpcResponse: string) {.slot.} =
try:
@ -1397,6 +1408,59 @@ QtObject:
let errMsg = e.msg
error "error checking permissions to join: ", errMsg
proc asyncCheckChannelPermissions*(self: Service, communityId: string, chatId: string) =
let arg = AsyncCheckChannelPermissionsTaskArg(
tptr: cast[ByteAddress](asyncCheckChannelPermissionsTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onAsyncCheckChannelPermissionsDone",
communityId: communityId,
chatId: chatId
)
self.threadpool.start(arg)
proc onAsyncCheckChannelPermissionsDone*(self: Service, rpcResponse: string) {.slot.} =
try:
let rpcResponseObj = rpcResponse.parseJson
if rpcResponseObj{"error"}.kind != JNull and rpcResponseObj{"error"}.getStr != "":
let error = Json.decode($rpcResponseObj["error"], RpcError)
error "Error checking community channel permissions", msg = error.message
return
let communityId = rpcResponseObj{"communityId"}.getStr()
let chatId = rpcResponseObj{"chatId"}.getStr()
let checkChannelPermissionsResponse = rpcResponseObj["response"]["result"].toCheckChannelPermissionsResponseDto()
self.communities[communityId].channelPermissions.channels[chatId] = checkChannelPermissionsResponse
self.events.emit(SIGNAL_CHECK_CHANNEL_PERMISSIONS_RESPONSE, CheckChannelPermissionsResponseArgs(communityId: communityId, chatId: chatId, checkChannelPermissionsResponse: checkChannelPermissionsResponse))
except Exception as e:
let errMsg = e.msg
error "error checking all channel permissions: ", errMsg
proc asyncCheckAllChannelsPermissions*(self: Service, communityId: string) =
let arg = AsyncCheckAllChannelsPermissionsTaskArg(
tptr: cast[ByteAddress](asyncCheckAllChannelsPermissionsTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onAsyncCheckAllChannelsPermissionsDone",
communityId: communityId
)
self.threadpool.start(arg)
proc onAsyncCheckAllChannelsPermissionsDone*(self: Service, rpcResponse: string) {.slot.} =
try:
let rpcResponseObj = rpcResponse.parseJson
if rpcResponseObj{"error"}.kind != JNull and rpcResponseObj{"error"}.getStr != "":
let error = Json.decode($rpcResponseObj["error"], RpcError)
error "Error checking all community channel permissions", msg = error.message
return
let communityId = rpcResponseObj{"communityId"}.getStr()
let checkAllChannelsPermissionsResponse = rpcResponseObj["response"]["result"].toCheckAllChannelsPermissionsResponseDto()
self.communities[communityId].channelPermissions = checkAllChannelsPermissionsResponse
self.events.emit(SIGNAL_CHECK_ALL_CHANNELS_PERMISSIONS_RESPONSE, CheckAllChannelsPermissionsResponseArgs(communityId: communityId, checkAllChannelsPermissionsResponse: checkAllChannelsPermissionsResponse))
except Exception as e:
let errMsg = e.msg
error "error checking all channels permissions: ", errMsg
proc asyncRequestToJoinCommunity*(self: Service, communityId: string, ensName: string, password: string) =
try:
let arg = AsyncRequestToJoinCommunityTaskArg(
@ -1783,9 +1847,9 @@ QtObject:
var response: RpcResponse[JsonNode]
if editing:
response = status_go.editCommunityTokenPermission(communityId, tokenPermission.id, int(tokenPermission.`type`), Json.encode(tokenPermission.tokenCriteria), tokenPermission.isPrivate)
response = status_go.editCommunityTokenPermission(communityId, tokenPermission.id, int(tokenPermission.`type`), Json.encode(tokenPermission.tokenCriteria), tokenPermission.chatIDs, tokenPermission.isPrivate)
else:
response = status_go.createCommunityTokenPermission(communityId, int(tokenPermission.`type`), Json.encode(tokenPermission.tokenCriteria), tokenPermission.isPrivate)
response = status_go.createCommunityTokenPermission(communityId, int(tokenPermission.`type`), Json.encode(tokenPermission.tokenCriteria), tokenPermission.chatIDs, tokenPermission.isPrivate)
if response.result != nil and response.result.kind != JNull:
var changesField = TOKEN_PERMISSIONS_ADDED
@ -1835,3 +1899,20 @@ QtObject:
let community = self.communities[communityId]
return community.pendingRequestsToJoin[indexPending].publicKey
proc checkChatHasPermissions*(self: Service, communityId: string, chatId: string): bool =
let community = self.getCommunityById(communityId)
for id, tokenPermission in community.tokenPermissions:
if TokenPermissionType(tokenPermission.`type`) == TokenPermissionType.View or TokenPermissionType(tokenPermission.`type`) == TokenPermissionType.ViewAndPost:
for id in tokenPermission.chatIds:
if id == chatId:
return true
return false
proc checkChatIsLocked*(self: Service, communityId: string, chatId: string): bool =
if not self.communities.hasKey(communityId):
return false
let community = self.getCommunityById(communityId)
return community.channelPermissions.channels.hasKey(chatId) and not community.channelPermissions.channels[chatId].viewAndPostPermissions.satisfied

View File

@ -44,6 +44,17 @@ proc checkPermissionsToJoinCommunity*(communityId: string): RpcResponse[JsonNode
"communityId": communityId
}])
proc checkCommunityChannelPermissions*(communityId: string, chatId: string): RpcResponse[JsonNode] {.raises: [Exception].} =
result = callPrivateRPC("checkCommunityChannelPermissions".prefix, %*[{
"communityId": communityId,
"chatId": chatId
}])
proc checkAllCommunityChannelsPermissions*(communityId: string): RpcResponse[JsonNode] {.raises: [Exception].} =
result = callPrivateRPC("checkAllCommunityChannelsPermissions".prefix, %*[{
"communityId": communityId
}])
proc myPendingRequestsToJoin*(): RpcResponse[JsonNode] {.raises: [Exception].} =
result = callPrivateRPC("myPendingRequestsToJoin".prefix)
@ -177,20 +188,22 @@ proc requestImportDiscordCommunity*(
"filesToImport": filesToImport
}])
proc createCommunityTokenPermission*(communityId: string, permissionType: int, tokenCriteria: string, isPrivate: bool): RpcResponse[JsonNode] {.raises: [Exception].} =
proc createCommunityTokenPermission*(communityId: string, permissionType: int, tokenCriteria: string, chatIDs: seq[string], isPrivate: bool): RpcResponse[JsonNode] {.raises: [Exception].} =
result = callPrivateRPC("createCommunityTokenPermission".prefix, %*[{
"communityId": communityId,
"type": permissionType,
"tokenCriteria": parseJson(tokenCriteria),
"chat_ids": chatIDs,
"isPrivate": isPrivate
}])
proc editCommunityTokenPermission*(communityId: string, permissionId: string, permissionType: int, tokenCriteria: string, isPrivate: bool): RpcResponse[JsonNode] {.raises: [Exception].} =
proc editCommunityTokenPermission*(communityId: string, permissionId: string, permissionType: int, tokenCriteria: string, chatIDs: seq[string], isPrivate: bool): RpcResponse[JsonNode] {.raises: [Exception].} =
result = callPrivateRPC("editCommunityTokenPermission".prefix, %*[{
"communityId": communityId,
"permissionId": permissionId,
"type": permissionType,
"tokenCriteria": parseJson(tokenCriteria),
"chat_ids": chatIDs,
"isPrivate": isPrivate
}])

View File

@ -180,6 +180,8 @@ Item {
onlineStatus: !!model.onlineStatus ? model.onlineStatus : StatusChatListItem.OnlineStatus.Inactive
sensor.enabled: draggableItem.dragActive
dragged: draggableItem.dragActive
requiresPermissions: model.requiresPermissions
locked: model.locked
onClicked: {
highlightWhenCreated = false

View File

@ -20,6 +20,8 @@ Rectangle {
property int notificationsCount: 0
property bool muted: false
property int onlineStatus: StatusChatListItem.OnlineStatus.Inactive
property bool requiresPermissions: false
property bool locked: false
property StatusAssetSettings asset: StatusAssetSettings {
width: 24
@ -36,9 +38,6 @@ Rectangle {
property bool dragged: false
property alias sensor: sensor
property bool requiresPermissions
property bool locked
signal clicked(var mouse)
signal unmute()

View File

@ -32,6 +32,7 @@ StackLayout {
}
Loader {
id: mainViewLoader
readonly property var chatItem: root.rootStore.chatCommunitySectionModule
sourceComponent: chatItem.isCommunity() && chatItem.requiresTokenPermissionToJoin && !chatItem.amIMember ? joinCommunityViewComponent : chatViewComponent
}
@ -41,6 +42,7 @@ StackLayout {
JoinCommunityView {
id: joinCommunityView
readonly property var communityData: sectionItemModel
readonly property string communityId: communityData.id
name: communityData.name
introMessage: communityData.introMessage
communityDesc: communityData.description
@ -53,7 +55,10 @@ StackLayout {
communityData.memberRole === Constants.memberRole.admin
communityItemsModel: root.rootStore.communityItemsModel
requirementsMet: root.permissionsStore.allTokenRequirementsMet
communityHoldingsModel: root.permissionsStore.permissionsModel
requiresRequest: !communityData.amIMember
communityHoldingsModel: root.permissionsStore.becomeMemberPermissionsModel
viewOnlyHoldingsModel: root.permissionsStore.viewOnlyPermissionsModel
viewAndPostHoldingsModel: root.permissionsStore.viewAndPostPermissionsModel
assetsModel: root.rootStore.assetsModel
collectiblesModel: root.rootStore.collectiblesModel
isInvitationPending: root.rootStore.isCommunityRequestPending(communityData.id)
@ -63,19 +68,20 @@ StackLayout {
loginType: root.rootStore.loginType
onNotificationButtonClicked: Global.openActivityCenterPopup()
onAdHocChatButtonClicked: rootStore.openCloseCreateChatView()
onRevealAddressClicked: openJoinCommunityDialog()
onRevealAddressClicked: {
Global.openPopup(communityIntroDialogPopup, {
communityId: communityData.id,
isInvitationPending: joinCommunityView.isInvitationPending,
name: communityData.name,
introMessage: communityData.introMessage,
imageSrc: communityData.image,
accessType: communityData.access
})
}
onInvitationPendingClicked: {
root.rootStore.cancelPendingRequest(communityData.id)
joinCommunityView.isInvitationPending = root.rootStore.isCommunityRequestPending(communityData.id)
}
onJoined: {
root.rootStore.requestToJoinCommunityWithAuthentication(communityData.id, root.rootStore.userProfileInst.name)
}
onCancelMembershipRequest: {
root.rootStore.cancelPendingRequest(communityData.id)
joinCommunityView.isInvitationPending = root.rootStore.isCommunityRequestPending(communityData.id)
}
Connections {
target: root.rootStore.communitiesModuleInst
@ -85,6 +91,25 @@ StackLayout {
}
}
}
CommunityIntroDialog {
id: communityIntroDialog
isInvitationPending: joinCommunityView.isInvitationPending
name: communityData.name
introMessage: communityData.introMessage
imageSrc: communityData.image
accessType: communityData.access
onJoined: {
root.rootStore.requestToJoinCommunityWithAuthentication(communityData.id, root.rootStore.userProfileInst.name)
}
onCancelMembershipRequest: {
root.rootStore.cancelPendingRequest(communityData.id)
joinCommunityView.isInvitationPending = root.rootStore.isCommunityRequestPending(communityData.id)
}
}
}
}
@ -92,12 +117,26 @@ StackLayout {
id: chatViewComponent
ChatView {
id: chatView
readonly property var chatItem: root.rootStore.chatCommunitySectionModule
readonly property string communityId: root.sectionItemModel.id
emojiPopup: root.emojiPopup
stickersPopup: root.stickersPopup
contactsStore: root.contactsStore
rootStore: root.rootStore
createChatPropertiesStore: root.createChatPropertiesStore
sectionItemModel: root.sectionItemModel
amIMember: chatItem.amIMember
amISectionAdmin: root.sectionItemModel.memberRole === Constants.memberRole.owner ||
root.sectionItemModel.memberRole === Constants.memberRole.admin
hasViewOnlyPermissions: root.permissionsStore.viewOnlyPermissionsModel.count > 0
hasViewAndPostPermissions: root.permissionsStore.viewAndPostPermissionsModel.count > 0
viewOnlyPermissionsModel: root.permissionsStore.viewOnlyPermissionsModel
viewAndPostPermissionsModel: root.permissionsStore.viewAndPostPermissionsModel
assetsModel: root.rootStore.assetsModel
collectiblesModel: root.rootStore.collectiblesModel
isInvitationPending: root.rootStore.isCommunityRequestPending(root.sectionItemModel.id)
onCommunityInfoButtonClicked: root.currentIndex = 1
onCommunityManageButtonClicked: root.currentIndex = 1
@ -108,6 +147,20 @@ StackLayout {
onOpenAppSearch: {
root.openAppSearch()
}
onRevealAddressClicked: {
Global.openPopup(communityIntroDialogPopup, {
communityId: root.sectionItemModel.id,
isInvitationPending: root.rootStore.isCommunityRequestPending(root.sectionItemModel.id),
name: root.sectionItemModel.name,
introMessage: root.sectionItemModel.introMessage,
imageSrc: root.sectionItemModel.image,
accessType: root.sectionItemModel.access
})
}
onInvitationPendingClicked: {
root.rootStore.cancelPendingRequest(root.sectionItemModel.id)
chatView.isInvitationPending = root.rootStore.isCommunityRequestPending(root.sectionItemModel.id)
}
}
}
@ -134,4 +187,37 @@ StackLayout {
}
}
}
Component {
id: communityIntroDialogPopup
CommunityIntroDialog {
id: communityIntroDialog
property string communityId
onJoined: {
root.rootStore.requestToJoinCommunityWithAuthentication(communityIntroDialog.communityId, root.rootStore.userProfileInst.name)
}
onCancelMembershipRequest: {
root.rootStore.cancelPendingRequest(communityIntroDialog.communityId)
mainViewLoader.item.isInvitationPending = root.rootStore.isCommunityRequestPending(communityIntroDialog.communityId)
}
onClosed: {
destroy()
}
}
}
Connections {
target: root.rootStore
enabled: mainViewLoader.item
function onCommunityAccessRequested(communityId: string) {
if (communityId === mainViewLoader.item.communityId) {
mainViewLoader.item.isInvitationPending = root.rootStore.isCommunityRequestPending(communityId)
}
}
}
}

View File

@ -6,7 +6,7 @@ import StatusQ.Core.Theme 0.1
QtObject {
enum Type {
None, Admin, Member, Moderator, ViewAndPost, Read
None, Admin, Member, Read, ViewAndPost
}
function getName(type) {

View File

@ -112,9 +112,6 @@ StatusDropdown {
Layout.fillWidth: true
}
// TODO: Channel-level permissions are temporarily hidden until they are
// supported by the backend. Uncomment when backend functionality is ready.
/*
CustomSeparator {
Layout.fillWidth: true
Layout.preferredHeight: d.sectionHeight
@ -122,12 +119,6 @@ StatusDropdown {
text: qsTr("Channels")
}
CustomPermissionListItem {
permissionType: PermissionTypes.Type.Moderator
Layout.fillWidth: true
}
CustomPermissionListItem {
permissionType: PermissionTypes.Type.ViewAndPost
@ -139,7 +130,6 @@ StatusDropdown {
Layout.fillWidth: true
}
*/
Separator {
visible: !!group.checkedButton

View File

@ -21,10 +21,6 @@ SettingsPageLayout {
property int viewWidth: 560 // by design
// TODO: temporary property, to be removed when no need to hide the switch
// in the app
property bool showWhoHoldsSwitch: false
signal createPermissionRequested(
int permissionType, var holdings, var channels, bool isPrivate)
@ -182,8 +178,6 @@ SettingsPageLayout {
holdingsRequired: selectedHoldingsModel ? selectedHoldingsModel.count > 0
: false
showWhoHoldsSwitch: root.showWhoHoldsSwitch
permissionDuplicated: {
// dependencies
holdingsTracker.revision

View File

@ -0,0 +1,171 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Layout 0.1
ColumnLayout {
id: root
property bool joinCommunity: true // Otherwise it means join channel action
property string name
property string channelName
property bool isInvitationPending: false
property bool isJoinRequestRejected: false
property bool requiresRequest: false
property alias loginType: overlayPanel.loginType
property bool requirementsMet: true
property var communityHoldingsModel
property var viewOnlyHoldingsModel
property var viewAndPostHoldingsModel
property var moderateHoldingsModel
property var assetsModel
property var collectiblesModel
property string chatDateTimeText
property string listUsersText
property var messagesModel
signal revealAddressClicked
signal invitationPendingClicked
spacing: 0
// Blur background:
Item {
Layout.fillWidth: true
Layout.preferredHeight: Math.min(
centralPanelData.implicitHeight,
parent.height - overlayPanel.implicitHeight)
ColumnLayout {
id: centralPanelData
width: parent.width
layer.enabled: true
layer.effect: fastBlur
StatusBaseText {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 30
Layout.bottomMargin: 30
text: root.chatDateTimeText
font.pixelSize: 13
color: Theme.palette.baseColor1
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
StatusBaseText {
text: root.listUsersText
font.pixelSize: 13
}
StatusBaseText {
text: qsTr("joined the channel")
font.pixelSize: 13
color: Theme.palette.baseColor1
}
}
ListView {
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height + spacing
Layout.topMargin: 16
spacing: 16
model: root.messagesModel
delegate: StatusMessage {
width: ListView.view.width
timestamp: model.timestamp
enabled: false
messageDetails: StatusMessageDetails {
messageText: model.message
contentType: model.contentType
sender.displayName: model.senderDisplayName
sender.isContact: model.isContact
sender.trustIndicator: model.trustIndicator
sender.profileImage: StatusProfileImageSettings {
width: 40
height: 40
name: model.profileImage || ""
colorId: model.colorId
}
}
}
}
}
}
// Permissions base information content:
Rectangle {
id: panelBase
Layout.fillWidth: true
Layout.fillHeight: true
color: Theme.palette.statusAppLayout.rightPanelBackgroundColor
gradient: Gradient {
GradientStop {
position: 0.000
color: "transparent"
}
GradientStop {
position: 0.180
color: panelBase.color
}
}
StatusScrollView {
anchors.fill: parent
padding: 0
Item {
implicitHeight: Math.max(overlayPanel.implicitHeight,
panelBase.height)
implicitWidth: Math.max(overlayPanel.implicitWidth,
panelBase.width)
JoinPermissionsOverlayPanel {
id: overlayPanel
anchors.centerIn: parent
topPadding: 2 * bottomPadding
joinCommunity: root.joinCommunity
requirementsMet: root.requirementsMet
isInvitationPending: root.isInvitationPending
isJoinRequestRejected: root.isJoinRequestRejected
requiresRequest: root.requiresRequest
communityName: root.name
communityHoldingsModel: root.communityHoldingsModel
channelName: root.channelName
viewOnlyHoldingsModel: root.viewOnlyHoldingsModel
viewAndPostHoldingsModel: root.viewAndPostHoldingsModel
moderateHoldingsModel: root.moderateHoldingsModel
assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel
onRevealAddressClicked: root.revealAddressClicked()
onInvitationPendingClicked: root.invitationPendingClicked()
}
}
}
}
Component {
id: fastBlur
FastBlur {
radius: 32
transparentBorder: true
}
}
}

View File

@ -0,0 +1,73 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
RowLayout {
id: root
property bool joinCommunity: true // Otherwise it means join channel action
property color color
property string name
property string channelName
property string communityDesc
property string channelDesc
spacing: 30
StatusChatInfoButton {
id: headerInfoButton
Layout.preferredHeight: parent.height
Layout.minimumWidth: 100
Layout.fillWidth: true
title: root.joinCommunity ? root.name : root.channelName
subTitle: root.joinCommunity ? root.communityDesc : root.channelDesc
asset.color: root.color
enabled: false
type: StatusChatInfoButton.Type.CommunityChat
layer.enabled: root.joinCommunity // Blured when joining community but not when entering channel
layer.effect: fastBlur
}
RowLayout {
Layout.preferredHeight: parent.height
spacing: 10
layer.enabled: true
layer.effect: fastBlur
StatusFlatRoundButton {
id: search
icon.name: "search"
type: StatusFlatRoundButton.Type.Secondary
enabled: false
}
StatusFlatRoundButton {
icon.name: "group-chat"
type: StatusFlatRoundButton.Type.Secondary
enabled: false
}
StatusFlatRoundButton {
icon.name: "more"
type: StatusFlatRoundButton.Type.Secondary
enabled: false
}
}
Component {
id: fastBlur
FastBlur {
radius: 32
transparentBorder: true
}
}
}

View File

@ -65,6 +65,38 @@ Control {
if(root.loginType == Constants.LoginType.Password) return "password"
return root.loginType == Constants.LoginType.Biometrics ? "touch-id" : "keycard"
}
function filterPermissions(model) {
return !!model && (model.tokenCriteriaMet || !model.isPrivate)
}
readonly property var communityPermissionsModel: SortFilterProxyModel {
sourceModel: root.communityHoldingsModel
filters: [
ExpressionFilter {
expression: d.filterPermissions(model)
}
]
}
readonly property var viewOnlyPermissionsModel: SortFilterProxyModel {
sourceModel: root.viewOnlyHoldingsModel
filters: [
ExpressionFilter {
expression: d.filterPermissions(model)
}
]
}
readonly property var viewAndPostPermissionsModel: SortFilterProxyModel {
sourceModel: root.viewAndPostHoldingsModel
filters: [
ExpressionFilter {
expression: d.filterPermissions(model)
}
]
}
}
padding: 35 // default by design
@ -84,38 +116,46 @@ Control {
}
CustomHoldingsListPanel {
visible: root.joinCommunity && root.communityHoldingsModel
introText: qsTr("To join <b>%1</b> you need to prove that you hold").arg(root.communityName)
model: root.communityHoldingsModel
id: communityRequirements
visible: root.joinCommunity
introText: d.communityPermissionsModel.count > 0 ?
qsTr("To join <b>%1</b> you need to prove that you hold").arg(root.communityName) :
qsTr("Sorry, you can't join <b>%1</b> because it's a private, closed community").arg(root.communityName)
model: d.communityPermissionsModel
}
CustomHoldingsListPanel {
visible: !root.joinCommunity && !!root.viewOnlyHoldingsModel
introText: qsTr("To only view the <b>%1</b> channel you need to hold").arg(root.channelName)
model: root.viewOnlyHoldingsModel
visible: !root.joinCommunity && d.viewOnlyPermissionsModel.count > 0
introText: root.requiresRequest ?
qsTr("To view the #<b>%1</b> channel you need to join <b>%2</b> and prove that you hold").arg(root.channelName).arg(root.communityName) :
qsTr("To view the #<b>%1</b> channel you need to hold").arg(root.channelName)
model: d.viewOnlyPermissionsModel
}
CustomHoldingsListPanel {
visible: !root.joinCommunity && !!root.viewAndPostHoldingsModel
introText: qsTr("To view and post in the <b>%1</b> channel you need to hold").arg(root.channelName)
model: root.viewAndPostHoldingsModel
visible: !root.joinCommunity && d.viewAndPostPermissionsModel.count > 0
introText: root.requiresRequest ?
qsTr("To view and post in the #<b>%1</b> channel you need to join <b>%2</b> and prove that you hold").arg(root.channelName).arg(root.communityName) :
qsTr("To view and post in the #<b>%1</b> channel you need to hold").arg(root.channelName)
model: d.viewAndPostPermissionsModel
}
HoldingsListPanel {
Layout.fillWidth: true
spacing: root.spacing
visible: !root.joinCommunity && !!root.moderateHoldings
visible: !root.joinCommunity && !!d.moderateHoldings
introText: qsTr("To moderate in the <b>%1</b> channel you need to hold").arg(root.channelName)
model: root.moderateHoldingsModel
model: d.moderateHoldingsModel
}
StatusButton {
Layout.alignment: Qt.AlignHCenter
visible: !root.showOnlyPanels && !root.isJoinRequestRejected
visible: !root.showOnlyPanels && !root.isJoinRequestRejected && root.requiresRequest
text: root.isInvitationPending ? d.getInvitationPendingText() : d.getRevealAddressText()
icon.name: root.isInvitationPending ? "" : d.getRevealAddressIcon()
font.pixelSize: 13
enabled: root.requirementsMet
enabled: root.requirementsMet || d.communityPermissionsModel.count == 0
onClicked: root.isInvitationPending ? root.invitationPendingClicked() : root.revealAddressClicked()
}

View File

@ -16,3 +16,5 @@ TokenHoldersPanel 1.0 TokenHoldersPanel.qml
TokenHoldersProxyModel 1.0 TokenHoldersProxyModel.qml
WarningPanel 1.0 WarningPanel.qml
ChatPermissionQualificationPanel 1.0 ChatPermissionQualificationPanel.qml
JoinCommunityCenterPanel 1.0 JoinCommunityCenterPanel.qml
JoinCommunityHeaderPanel 1.0 JoinCommunityHeaderPanel.qml

View File

@ -1,14 +1,71 @@
import QtQml 2.15
import SortFilterProxyModel 0.2
import utils 1.0
QtObject {
id: root
required property string activeSectionId
required property string activeChannelId
required property var chatCommunitySectionModuleInst
// all permissions model
readonly property var permissionsModel:
chatCommunitySectionModuleInst.permissionsModel
readonly property var becomeMemberPermissionsModel: SortFilterProxyModel {
id: becomeMemberPermissionsModel
sourceModel: root.permissionsModel
function filterPredicate(modelData) {
return (modelData.permissionType == Constants.permissionType.member) &&
(modelData.tokenCriteriaMet || !modelData.isPrivate)
}
filters: [
ExpressionFilter {
expression: becomeMemberPermissionsModel.filterPredicate(model)
}
]
}
readonly property var viewOnlyPermissionsModel: SortFilterProxyModel {
id: viewOnlyPermissionsModel
sourceModel: root.permissionsModel
function filterPredicate(modelData) {
return (modelData.permissionType == Constants.permissionType.read) &&
root.permissionsModel.belongsToChat(modelData.id, root.activeChannelId) &&
(modelData.tokenCriteriaMet || !modelData.isPrivate)
}
filters: [
ExpressionFilter {
expression: {
root.activeChannelId // ensure predicate is re-triggered when activeChannelId changes
viewOnlyPermissionsModel.filterPredicate(model)
}
}
]
}
readonly property var viewAndPostPermissionsModel: SortFilterProxyModel {
id: viewAndPostPermissionsModel
sourceModel: root.permissionsModel
function filterPredicate(modelData) {
return (modelData.permissionType == Constants.permissionType.viewAndPost) &&
root.permissionsModel.belongsToChat(modelData.id, root.activeChannelId) &&
(modelData.tokenCriteriaMet || !modelData.isPrivate)
}
filters: [
ExpressionFilter {
expression: {
root.activeChannelId // ensure predicate is re-triggered when activeChannelId changes
viewAndPostPermissionsModel.filterPredicate(model)
}
}
]
}
readonly property bool isOwner: false
readonly property bool allTokenRequirementsMet: chatCommunitySectionModuleInst.allTokenRequirementsMet
@ -33,6 +90,7 @@ QtObject {
root.activeSectionId, key,
permissionType,
JSON.stringify(holdings),
channels.map(c => c.key).join(","),
isPrivate)
}
}

View File

@ -16,6 +16,7 @@ QtObject {
readonly property PermissionsStore permissionsStore: PermissionsStore {
activeSectionId: mainModuleInst.activeSection.id
activeChannelId: root.currentChatContentModule().chatDetails.id
chatCommunitySectionModuleInst: chatCommunitySectionModule
}
@ -78,6 +79,8 @@ QtObject {
signal communityInfoAlreadyRequested()
signal communityAccessRequested(string communityId)
signal goToMembershipRequestsPage()
function setActiveCommunity(communityId) {
@ -621,6 +624,10 @@ QtObject {
function onCommunityInfoAlreadyRequested() {
root.communityInfoAlreadyRequested()
}
function onCommunityAccessRequested(communityId) {
root.communityAccessRequested(communityId)
}
}
readonly property Connections mainModuleInstConnections: Connections {

View File

@ -44,10 +44,11 @@ Item {
property int activeChatType: parentModule && parentModule.activeItem.type
property bool stickersLoaded: false
property bool viewAndPostPermissionsSatisfied: true
property var viewAndPostPermissionsModel
property var viewAndPostHoldingsModel
readonly property var contactDetails: rootStore ? rootStore.oneToOneChatContact : null
readonly property bool isUserAdded: !!root.contactDetails && root.contactDetails.isAdded
property bool amISectionAdmin: false
signal openStickerPackPopup(string stickerPackId)
@ -267,8 +268,9 @@ Item {
if (!channelPostRestrictions.visible) {
if (d.activeChatContentModule.chatDetails.blocked)
return qsTr("This user has been blocked.")
if (!root.rootStore.sectionDetails.joined || root.rootStore.sectionDetails.amIBanned)
if (!root.rootStore.sectionDetails.joined || root.rootStore.sectionDetails.amIBanned) {
return qsTr("You need to join this community to send messages")
}
if (!root.viewAndPostPermissionsSatisfied) {
return qsTr("Sorry, you don't have the tokens needed to post in this channel.")
}
@ -355,7 +357,7 @@ Item {
height: chatInput.textInput.height
anchors.left: parent.left
anchors.leftMargin: (2*Style.current.bigPadding)
visible: (!!root.viewAndPostPermissionsModel && (root.viewAndPostPermissionsModel.count > 0)
visible: (!!root.viewAndPostHoldingsModel && (root.viewAndPostHoldingsModel.count > 0)
&& !root.amISectionAdmin)
assetsModel: root.rootStore.assetsModel
collectiblesModel: root.rootStore.collectiblesModel

View File

@ -35,11 +35,51 @@ StatusSectionLayout {
property var stickersPopup
property bool stickersLoaded: false
readonly property var chatContentModule: root.rootStore.currentChatContentModule() || null
readonly property bool viewOnlyPermissionsSatisfied: chatContentModule.viewOnlyPermissionsSatisfied
readonly property bool viewAndPostPermissionsSatisfied: chatContentModule.viewAndPostPermissionsSatisfied
property bool hasViewOnlyPermissions: false
property bool hasViewAndPostPermissions: false
property bool amIMember: false
property bool amISectionAdmin: false
property bool isInvitationPending: false
property var viewOnlyPermissionsModel
property var viewAndPostPermissionsModel
property var assetsModel
property var collectiblesModel
readonly property bool contentLocked: {
if (!rootStore.chatCommunitySectionModule.isCommunity()) {
return false
}
if (!amIMember) {
return hasViewAndPostPermissions || hasViewOnlyPermissions
}
if (amISectionAdmin) {
return false
}
if (!hasViewAndPostPermissions && hasViewOnlyPermissions) {
return !viewOnlyPermissionsSatisfied
}
if (hasViewAndPostPermissions && !hasViewOnlyPermissions) {
return !viewAndPostPermissionsSatisfied
}
if (hasViewOnlyPermissions && hasViewAndPostPermissions) {
return !viewOnlyPermissionsSatisfied && !viewAndPostPermissionsSatisfied
}
return false
}
signal communityInfoButtonClicked()
signal communityManageButtonClicked()
signal profileButtonClicked()
signal openAppSearch()
signal revealAddressClicked
signal invitationPendingClicked
Connections {
target: root.rootStore.stickersStore.stickersModule
@ -61,12 +101,9 @@ StatusSectionLayout {
notificationCount: activityCenterStore.unreadNotificationsCount
hasUnseenNotifications: activityCenterStore.hasUnseenNotifications
headerContent: ChatHeaderContentView {
id: headerContent
visible: !!root.rootStore.currentChatContentModule()
rootStore: root.rootStore
emojiPopup: root.emojiPopup
onSearchButtonClicked: root.openAppSearch()
headerContent: Loader {
id: headerContentLoader
sourceComponent: root.contentLocked ? joinCommunityHeaderPanelComponent : chatHeaderContentViewComponent
}
leftPanel: Loader {
@ -76,22 +113,16 @@ StatusSectionLayout {
contactsColumnComponent
}
centerPanel: ChatColumnView {
id: chatColumn
centerPanel: Loader {
anchors.fill: parent
parentModule: root.rootStore.chatCommunitySectionModule
rootStore: root.rootStore
createChatPropertiesStore: root.createChatPropertiesStore
contactsStore: root.contactsStore
stickersLoaded: root.stickersLoaded
emojiPopup: root.emojiPopup
stickersPopup: root.stickersPopup
onOpenStickerPackPopup: {
Global.openPopup(statusStickerPackClickPopup, {packId: stickerPackId, store: root.stickersPopup.store} )
}
sourceComponent: root.contentLocked ? joinCommunityCenterPanelComponent : chatColumnViewComponent
}
showRightPanel: {
if (root.contentLocked) {
return false
}
if (root.rootStore.openCreateChat ||
!localAccountSensitiveSettings.showOnlineUsers ||
!localAccountSensitiveSettings.expandUsersList) {
@ -123,6 +154,67 @@ StatusSectionLayout {
}
}
Component {
id: chatHeaderContentViewComponent
ChatHeaderContentView {
visible: !!root.rootStore.currentChatContentModule()
rootStore: root.rootStore
emojiPopup: root.emojiPopup
onSearchButtonClicked: root.openAppSearch()
}
}
Component {
id: joinCommunityHeaderPanelComponent
JoinCommunityHeaderPanel {
readonly property var chatContentModule: root.rootStore.currentChatContentModule() || null
joinCommunity: false
color: chatContentModule.chatDetails.color
channelName: chatContentModule.chatDetails.name
channelDesc: chatContentModule.chatDetails.description
}
}
Component {
id: chatColumnViewComponent
ChatColumnView {
parentModule: root.rootStore.chatCommunitySectionModule
rootStore: root.rootStore
createChatPropertiesStore: root.createChatPropertiesStore
contactsStore: root.contactsStore
stickersLoaded: root.stickersLoaded
emojiPopup: root.emojiPopup
stickersPopup: root.stickersPopup
viewAndPostHoldingsModel: root.viewAndPostPermissionsModel
viewAndPostPermissionsSatisfied: !root.rootStore.chatCommunitySectionModule.isCommunity() || root.amISectionAdmin || root.viewAndPostPermissionsSatisfied
amISectionAdmin: root.amISectionAdmin
onOpenStickerPackPopup: {
Global.openPopup(statusStickerPackClickPopup, {packId: stickerPackId, store: root.stickersPopup.store} )
}
}
}
Component {
id: joinCommunityCenterPanelComponent
JoinCommunityCenterPanel {
joinCommunity: false
name: sectionItemModel.name
channelName: root.chatContentModule.chatDetails.name
viewOnlyHoldingsModel: root.viewOnlyPermissionsModel
viewAndPostHoldingsModel: root.viewAndPostPermissionsModel
assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel
isInvitationPending: root.isInvitationPending
requiresRequest: !root.amIMember
requirementsMet: (viewOnlyPermissionsSatisfied && viewOnlyPermissionsModel.count > 0) ||
(viewAndPostPermissionsSatisfied && viewAndPostPermissionsModel.count > 0)
onRevealAddressClicked: root.revealAddressClicked()
onInvitationPendingClicked: root.invitationPendingClicked()
}
}
Component {
id: contactsColumnComponent
ContactsColumnView {
@ -138,7 +230,9 @@ StatusSectionLayout {
root.openAppSearch()
}
onAddRemoveGroupMemberClicked: {
headerContent.addRemoveGroupMember()
if (headerContentLoader.item && headerContentLoader.item instanceof ChatHeaderContentView) {
headerContentLoader.item.addRemoveGroupMember()
}
}
}
}

View File

@ -29,10 +29,6 @@ StatusScrollView {
property int viewWidth: 560 // by design
property bool isEditState: false
// TODO: temporary property, to be removed when no need to hide the switch
// in the app
property bool showWhoHoldsSwitch: false
readonly property bool dirty:
root.holdingsRequired !== d.dirtyValues.holdingsRequired ||
(d.dirtyValues.holdingsRequired && !holdingsModelComparator.equal) ||
@ -221,8 +217,6 @@ StatusScrollView {
children: StatusSwitch {
id: whoHoldsSwitch
visible: root.showWhoHoldsSwitch
padding: 0
anchors.right: parent.right
anchors.top: parent.top

View File

@ -36,7 +36,7 @@ StatusSectionLayout {
property bool requirementsMet: true
property bool isJoinRequestRejected: false
property bool requiresRequest: false
property alias loginType: overlayPanel.loginType
property alias loginType: joinCommunityCenterPanel.loginType
property var communityHoldingsModel
property var viewOnlyHoldingsModel
@ -57,12 +57,7 @@ StatusSectionLayout {
signal adHocChatButtonClicked
signal revealAddressClicked
signal invitationPendingClicked
signal joined
signal cancelMembershipRequest
function openJoinCommunityDialog() {
joinCommunityDialog.open()
}
QtObject {
id: d
@ -70,50 +65,13 @@ StatusSectionLayout {
readonly property int blurryRadius: 32
}
// Blur background:
headerContent: RowLayout {
anchors.fill: parent
spacing: 30
StatusChatInfoButton {
id: headerInfoButton
Layout.preferredHeight: parent.height
Layout.minimumWidth: 100
Layout.fillWidth: true
title: root.joinCommunity ? root.name : root.channelName
subTitle: root.joinCommunity ? root.communityDesc : root.channelDesc
asset.color: root.color
enabled: false
type: StatusChatInfoButton.Type.CommunityChat
layer.enabled: root.joinCommunity // Blured when joining community but not when entering channel
layer.effect: fastBlur
}
RowLayout {
Layout.preferredHeight: parent.height
spacing: 10
layer.enabled: true
layer.effect: fastBlur
StatusFlatRoundButton {
id: search
icon.name: "search"
type: StatusFlatRoundButton.Type.Secondary
enabled: false
}
StatusFlatRoundButton {
icon.name: "group-chat"
type: StatusFlatRoundButton.Type.Secondary
enabled: false
}
StatusFlatRoundButton {
icon.name: "more"
type: StatusFlatRoundButton.Type.Secondary
enabled: false
}
}
headerContent: JoinCommunityHeaderPanel {
joinCommunity: root.joinCommunity
color: root.color
name: root.name
channelName: root.channelName
communityDesc: root.communityDesc
channelDesc: root.channelDesc
}
// Blur background:
@ -135,7 +93,7 @@ StatusSectionLayout {
ColumnLayout {
Layout.fillWidth: true
Layout.margins: Style.current.halfPadding
layer.enabled: true
layer.enabled: root.joinCommunity
layer.effect: fastBlur
Repeater {
@ -160,127 +118,36 @@ StatusSectionLayout {
}
// Blur background + Permissions base information content:
centerPanel: ColumnLayout {
centerPanel: JoinCommunityCenterPanel {
id: joinCommunityCenterPanel
anchors.fill: parent
spacing: 0
// Blur background:
Item {
Layout.fillWidth: true
Layout.preferredHeight: Math.min(centralPanelData.implicitHeight, parent.height - overlayPanel.implicitHeight)
joinCommunity: root.joinCommunity // Otherwise it means join channel action
ColumnLayout {
id: centralPanelData
width: parent.width
layer.enabled: true
layer.effect: fastBlur
name: root.name
channelName: root.channelName
StatusBaseText {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 30
Layout.bottomMargin: 30
text: root.chatDateTimeText
font.pixelSize: 13
color: Theme.palette.baseColor1
}
isInvitationPending: root.isInvitationPending
isJoinRequestRejected: root.isJoinRequestRejected
requiresRequest: root.requiresRequest
requirementsMet: root.requirementsMet
RowLayout {
Layout.alignment: Qt.AlignHCenter
communityHoldingsModel: root.communityHoldingsModel
viewOnlyHoldingsModel: root.viewOnlyHoldingsModel
viewAndPostHoldingsModel: root.viewAndPostHoldingsModel
moderateHoldingsModel: root.moderateHoldingsModel
assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel
StatusBaseText {
text: root.listUsersText
font.pixelSize: 13
}
chatDateTimeText: root.chatDateTimeText
listUsersText: root.listUsersText
messagesModel: root.messagesModel
StatusBaseText {
text: qsTr("joined the channel")
font.pixelSize: 13
color: Theme.palette.baseColor1
}
}
ListView {
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height + spacing
Layout.topMargin: 16
spacing: 16
model: root.messagesModel
delegate: StatusMessage {
width: ListView.view.width
timestamp: model.timestamp
enabled: false
messageDetails: StatusMessageDetails {
messageText: model.message
contentType: model.contentType
sender.displayName: model.senderDisplayName
sender.isContact: model.isContact
sender.trustIndicator: model.trustIndicator
sender.profileImage: StatusProfileImageSettings {
width: 40
height: 40
name: model.profileImage || ""
colorId: model.colorId
}
}
}
}
}
}
// Permissions base information content:
Rectangle {
id: panelBase
Layout.fillWidth: true
Layout.fillHeight: true
color: Theme.palette.statusAppLayout.rightPanelBackgroundColor
gradient: Gradient {
GradientStop {
position: 0.000
color: "transparent"
}
GradientStop {
position: 0.180
color: panelBase.color
}
}
StatusScrollView {
anchors.fill: parent
padding: 0
Item {
implicitHeight: Math.max(overlayPanel.implicitHeight, panelBase.height)
implicitWidth: Math.max(overlayPanel.implicitWidth, panelBase.width)
JoinPermissionsOverlayPanel {
id: overlayPanel
anchors.centerIn: parent
topPadding: 2 * bottomPadding
joinCommunity: root.joinCommunity
requirementsMet: root.requirementsMet
isInvitationPending: root.isInvitationPending
isJoinRequestRejected: root.isJoinRequestRejected
requiresRequest: root.requiresRequest
communityName: root.name
communityHoldingsModel: root.communityHoldingsModel
channelName: root.channelName
viewOnlyHoldingsModel: root.viewOnlyHoldingsModel
viewAndPostHoldingsModel: root.viewAndPostHoldingsModel
moderateHoldingsModel: root.moderateHoldingsModel
assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel
onRevealAddressClicked: root.revealAddressClicked()
onInvitationPendingClicked: root.invitationPendingClicked()
}
}
}
}
onRevealAddressClicked: root.revealAddressClicked()
onInvitationPendingClicked: root.invitationPendingClicked()
}
showRightPanel: false
Component {
@ -291,17 +158,4 @@ StatusSectionLayout {
transparentBorder: true
}
}
CommunityIntroDialog {
id: joinCommunityDialog
name: root.name
introMessage: root.introMessage
imageSrc: root.image
accessType: root.accessType
isInvitationPending: root.isInvitationPending
onJoined: root.joined()
onCancelMembershipRequest: root.cancelMembershipRequest()
}
}

View File

@ -404,6 +404,14 @@ QtObject {
readonly property int admin: 4
}
readonly property QtObject permissionType: QtObject{
readonly property int none: 0
readonly property int admin: 1
readonly property int member: 2
readonly property int read: 3
readonly property int viewAndPost: 4
}
readonly property QtObject messageContentType: QtObject {
readonly property int newMessagesMarker: -3
readonly property int fetchMoreMessagesButton: -2

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit a46bf97bf3f46b07d1f447732947011e20fa499f
Subproject commit bf64f97d5a2bcb1b5fb6b134dda69994e0ddd2bb