feat: introduce first JoinCommunityView for token-gated communities
This does a few things: - It integrates with the latest `CommunityTokensMetadata` to access community specific ERC721 token - It changes `ChatLayout` such that it conditionally loads either `ChatView` or `JoinCommunityView`. `JoinCommunityView` has been specifically designed for token-gated communities Here's what works (in terms of token permissions): 1. If a community has token permissions and the the current users is not a member of that community, we show `JoinCommunityView` instead of `ChatView` 2. Any community token permissions of type "Become member" are listed in the `JoinCommunityView` 3. There are different types of token critera a permission can have: ERC20 token, ERC721 token, or ENS (which is also ERC721 but we have a type for that nonetheless) Only ERC20 token balances are checked for the known wallet accounts. This happens every time the known token list has been updated (every 10 min atm). We still need to add balance checks for any ERC721 tokens and ENS. 4. If token permissions are created, updated or deleted by the community owner, the `JoinCommunityView` will update in real-time. You'll also notice that the `Reveal my address and request access` button will be enabled if any of the token permissions are fulfilled (only ERC20 at the time being). Clicking that button will not yet send a request. This will be done in the next step as part of https://github.com/status-im/status-desktop/issues/9761
This commit is contained in:
parent
8f3a965a49
commit
25b0641cc2
|
@ -244,6 +244,12 @@ proc init*(self: Controller) =
|
||||||
if (args.communityId == self.sectionId):
|
if (args.communityId == self.sectionId):
|
||||||
self.delegate.onCommunityTokenPermissionDeletionFailed(args.communityId)
|
self.delegate.onCommunityTokenPermissionDeletionFailed(args.communityId)
|
||||||
|
|
||||||
|
self.events.on(SIGNAL_COMMUNITY_TOKEN_METADATA_ADDED) do(e: Args):
|
||||||
|
let args = CommunityTokenMetadataArgs(e)
|
||||||
|
if (args.communityId == self.sectionId):
|
||||||
|
self.delegate.onCommunityTokenMetadataAdded(args.communityId, args.tokenMetadata)
|
||||||
|
|
||||||
|
|
||||||
self.events.on(SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT) do(e: Args):
|
self.events.on(SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT) do(e: Args):
|
||||||
self.delegate.onWalletAccountTokensRebuilt()
|
self.delegate.onWalletAccountTokensRebuilt()
|
||||||
|
|
||||||
|
|
|
@ -353,5 +353,8 @@ method onCommunityTokenPermissionDeleted*(self: AccessInterface, communityId: st
|
||||||
method onCommunityTokenPermissionDeletionFailed*(self: AccessInterface, communityId: string) =
|
method onCommunityTokenPermissionDeletionFailed*(self: AccessInterface, communityId: string) =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method onCommunityTokenMetadataAdded*(self: AccessInterface, communityId: string, tokenMetadata: CommunityTokensMetadataDto) =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method onWalletAccountTokensRebuilt*(self: AccessInterface) =
|
method onWalletAccountTokensRebuilt*(self: AccessInterface) =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
|
@ -12,6 +12,7 @@ import ../../shared_models/token_permission_item
|
||||||
import ../../shared_models/token_criteria_item
|
import ../../shared_models/token_criteria_item
|
||||||
import ../../shared_models/token_criteria_model
|
import ../../shared_models/token_criteria_model
|
||||||
import ../../shared_models/token_list_item
|
import ../../shared_models/token_list_item
|
||||||
|
import ../../shared_models/token_list_model
|
||||||
|
|
||||||
import chat_content/module as chat_content_module
|
import chat_content/module as chat_content_module
|
||||||
import chat_content/users/module as users_module
|
import chat_content/users/module as users_module
|
||||||
|
@ -66,6 +67,8 @@ proc buildChatSectionUI(self: Module,
|
||||||
|
|
||||||
proc buildTokenPermissionItem*(self: Module, tokenPermission: CommunityTokenPermissionDto): TokenPermissionItem
|
proc buildTokenPermissionItem*(self: Module, tokenPermission: CommunityTokenPermissionDto): TokenPermissionItem
|
||||||
|
|
||||||
|
proc buildTokenList*(self: Module)
|
||||||
|
|
||||||
proc newModule*(
|
proc newModule*(
|
||||||
delegate: delegate_interface.AccessInterface,
|
delegate: delegate_interface.AccessInterface,
|
||||||
events: EventEmitter,
|
events: EventEmitter,
|
||||||
|
@ -303,6 +306,8 @@ proc initContactRequestsModel(self: Module) =
|
||||||
self.view.contactRequestsModel().addItems(contactsWhoAddedMe)
|
self.view.contactRequestsModel().addItems(contactsWhoAddedMe)
|
||||||
|
|
||||||
proc rebuildCommunityTokenPermissionsModel(self: Module) =
|
proc rebuildCommunityTokenPermissionsModel(self: Module) =
|
||||||
|
self.buildTokenList()
|
||||||
|
|
||||||
let community = self.controller.getMyCommunity()
|
let community = self.controller.getMyCommunity()
|
||||||
var tokenPermissionsItems: seq[TokenPermissionItem] = @[]
|
var tokenPermissionsItems: seq[TokenPermissionItem] = @[]
|
||||||
var allTokenRequirementsMet = false
|
var allTokenRequirementsMet = false
|
||||||
|
@ -322,17 +327,17 @@ proc rebuildCommunityTokenPermissionsModel(self: Module) =
|
||||||
|
|
||||||
self.view.tokenPermissionsModel().setItems(tokenPermissionsItems)
|
self.view.tokenPermissionsModel().setItems(tokenPermissionsItems)
|
||||||
self.view.setAllTokenRequirementsMet(allTokenRequirementsMet)
|
self.view.setAllTokenRequirementsMet(allTokenRequirementsMet)
|
||||||
|
self.view.setRequiresTokenPermissionToJoin(tokenPermissionsItems.len > 0)
|
||||||
|
|
||||||
proc initCommunityTokenPermissionsModel(self: Module) =
|
proc initCommunityTokenPermissionsModel(self: Module) =
|
||||||
self.rebuildCommunityTokenPermissionsModel()
|
self.rebuildCommunityTokenPermissionsModel()
|
||||||
|
|
||||||
proc buildTokenList(self: Module) =
|
proc buildTokenList(self: Module) =
|
||||||
|
|
||||||
var tokenListItems: seq[TokenListItem]
|
var tokenListItems: seq[TokenListItem]
|
||||||
var collectiblesListItems: seq[TokenListItem]
|
var collectiblesListItems: seq[TokenListItem]
|
||||||
|
|
||||||
|
let community = self.controller.getMyCommunity()
|
||||||
let erc20Tokens = self.controller.getTokenList()
|
let erc20Tokens = self.controller.getTokenList()
|
||||||
let communityTokens = self.controller.getCommunityTokenList()
|
|
||||||
|
|
||||||
for token in erc20Tokens:
|
for token in erc20Tokens:
|
||||||
let tokenListItem = initTokenListItem(
|
let tokenListItem = initTokenListItem(
|
||||||
|
@ -346,7 +351,7 @@ proc buildTokenList(self: Module) =
|
||||||
|
|
||||||
tokenListItems.add(tokenListItem)
|
tokenListItems.add(tokenListItem)
|
||||||
|
|
||||||
for token in communityTokens:
|
for token in community.communityTokensMetadata:
|
||||||
let tokenListItem = initTokenListItem(
|
let tokenListItem = initTokenListItem(
|
||||||
key = token.symbol,
|
key = token.symbol,
|
||||||
name = token.name,
|
name = token.name,
|
||||||
|
@ -402,8 +407,9 @@ method load*(
|
||||||
self.initContactRequestsModel()
|
self.initContactRequestsModel()
|
||||||
else:
|
else:
|
||||||
self.usersModule.load()
|
self.usersModule.load()
|
||||||
|
let community = self.controller.getMyCommunity()
|
||||||
|
self.view.setAmIMember(community.joined)
|
||||||
self.initCommunityTokenPermissionsModel()
|
self.initCommunityTokenPermissionsModel()
|
||||||
self.buildTokenList()
|
|
||||||
|
|
||||||
let activeChatId = self.controller.getActiveChatId()
|
let activeChatId = self.controller.getActiveChatId()
|
||||||
let isCurrentSectionActive = self.controller.getIsCurrentSectionActive()
|
let isCurrentSectionActive = self.controller.getIsCurrentSectionActive()
|
||||||
|
@ -743,14 +749,18 @@ method onChatUnmuted*(self: Module, chatId: string) =
|
||||||
|
|
||||||
method onCommunityTokenPermissionDeleted*(self: Module, communityId: string, permissionId: string) =
|
method onCommunityTokenPermissionDeleted*(self: Module, communityId: string, permissionId: string) =
|
||||||
self.view.tokenPermissionsModel().removeItemWithId(permissionId)
|
self.view.tokenPermissionsModel().removeItemWithId(permissionId)
|
||||||
|
self.view.setRequiresTokenPermissionToJoin(self.view.tokenPermissionsModel().getCount() > 0)
|
||||||
singletonInstance.globalEvents.showCommunityTokenPermissionDeletedNotification(communityId, "Community permission deleted", "A token permission has been removed")
|
singletonInstance.globalEvents.showCommunityTokenPermissionDeletedNotification(communityId, "Community permission deleted", "A token permission has been removed")
|
||||||
|
|
||||||
method onCommunityTokenPermissionCreated*(self: Module, communityId: string, tokenPermission: CommunityTokenPermissionDto) =
|
method onCommunityTokenPermissionCreated*(self: Module, communityId: string, tokenPermission: CommunityTokenPermissionDto) =
|
||||||
if tokenPermission.`type` == TokenPermissionType.BecomeMember:
|
if tokenPermission.`type` == TokenPermissionType.BecomeMember:
|
||||||
let tokenPermissionItem = self.buildTokenPermissionItem(tokenPermission)
|
let tokenPermissionItem = self.buildTokenPermissionItem(tokenPermission)
|
||||||
self.view.tokenPermissionsModel.addItem(tokenPermissionItem)
|
|
||||||
if tokenPermissionItem.tokenCriteriaMet:
|
if tokenPermissionItem.tokenCriteriaMet:
|
||||||
self.view.setAllTokenRequirementsMet(true)
|
self.view.setAllTokenRequirementsMet(true)
|
||||||
|
|
||||||
|
self.view.tokenPermissionsModel.addItem(tokenPermissionItem)
|
||||||
|
self.view.setRequiresTokenPermissionToJoin(true)
|
||||||
singletonInstance.globalEvents.showCommunityTokenPermissionCreatedNotification(communityId, "Community permission created", "A token permission has been added")
|
singletonInstance.globalEvents.showCommunityTokenPermissionCreatedNotification(communityId, "Community permission created", "A token permission has been added")
|
||||||
|
|
||||||
method onCommunityTokenPermissionUpdated*(self: Module, communityId: string, tokenPermission: CommunityTokenPermissionDto) =
|
method onCommunityTokenPermissionUpdated*(self: Module, communityId: string, tokenPermission: CommunityTokenPermissionDto) =
|
||||||
|
@ -784,6 +794,23 @@ method onCommunityTokenPermissionUpdateFailed*(self: Module, communityId: string
|
||||||
method onCommunityTokenPermissionDeletionFailed*(self: Module, communityId: string) =
|
method onCommunityTokenPermissionDeletionFailed*(self: Module, communityId: string) =
|
||||||
singletonInstance.globalEvents.showCommunityTokenPermissionDeletionFailedNotification(communityId, "Failed to delete community permission", "Something went wrong")
|
singletonInstance.globalEvents.showCommunityTokenPermissionDeletionFailedNotification(communityId, "Failed to delete community permission", "Something went wrong")
|
||||||
|
|
||||||
|
method onCommunityTokenMetadataAdded*(self: Module, communityId: string, tokenMetadata: CommunityTokensMetadataDto) =
|
||||||
|
let tokenListItem = initTokenListItem(
|
||||||
|
key = tokenMetadata.symbol,
|
||||||
|
name = tokenMetadata.name,
|
||||||
|
symbol = tokenMetadata.symbol,
|
||||||
|
color = "", # tokenMetadata doesn't provide a color
|
||||||
|
image = tokenMetadata.image,
|
||||||
|
category = ord(TokenListItemCategory.Community)
|
||||||
|
)
|
||||||
|
|
||||||
|
if tokenMetadata.tokenType == community_dto.TokenType.ERC721 and not self.view.collectiblesListModel().hasItem(tokenMetadata.symbol):
|
||||||
|
self.view.collectiblesListModel.addItems(@[tokenListItem])
|
||||||
|
return
|
||||||
|
|
||||||
|
if tokenMetadata.tokenType == community_dto.TokenType.ERC20 and not self.view.tokenListModel().hasItem(tokenMetadata.symbol):
|
||||||
|
self.view.tokenListModel.addItems(@[tokenListItem])
|
||||||
|
|
||||||
method onMarkAllMessagesRead*(self: Module, chatId: string) =
|
method onMarkAllMessagesRead*(self: Module, chatId: string) =
|
||||||
self.updateBadgeNotifications(chatId, hasUnreadMessages=false, unviewedMentionsCount=0)
|
self.updateBadgeNotifications(chatId, hasUnreadMessages=false, unviewedMentionsCount=0)
|
||||||
let chatDetails = self.controller.getChatDetails(chatId)
|
let chatDetails = self.controller.getChatDetails(chatId)
|
||||||
|
|
|
@ -32,6 +32,8 @@ QtObject:
|
||||||
collectiblesListModel: TokenListModel
|
collectiblesListModel: TokenListModel
|
||||||
collectiblesListModelVariant: QVariant
|
collectiblesListModelVariant: QVariant
|
||||||
allTokenRequirementsMet: bool
|
allTokenRequirementsMet: bool
|
||||||
|
requiresTokenPermissionToJoin: bool
|
||||||
|
amIMember: bool
|
||||||
|
|
||||||
proc delete*(self: View) =
|
proc delete*(self: View) =
|
||||||
self.model.delete
|
self.model.delete
|
||||||
|
@ -74,6 +76,8 @@ QtObject:
|
||||||
result.tokenListModelVariant = newQVariant(result.tokenListModel)
|
result.tokenListModelVariant = newQVariant(result.tokenListModel)
|
||||||
result.collectiblesListModel = newTokenListModel()
|
result.collectiblesListModel = newTokenListModel()
|
||||||
result.collectiblesListModelVariant = newQVariant(result.collectiblesListModel)
|
result.collectiblesListModelVariant = newQVariant(result.collectiblesListModel)
|
||||||
|
result.amIMember = false
|
||||||
|
result.requiresTokenPermissionToJoin = false
|
||||||
|
|
||||||
proc load*(self: View) =
|
proc load*(self: View) =
|
||||||
self.delegate.viewDidLoad()
|
self.delegate.viewDidLoad()
|
||||||
|
@ -381,6 +385,36 @@ QtObject:
|
||||||
proc deleteCommunityTokenPermission*(self: View, communityId: string, permissionId: string) {.slot.} =
|
proc deleteCommunityTokenPermission*(self: View, communityId: string, permissionId: string) {.slot.} =
|
||||||
self.delegate.deleteCommunityTokenPermission(communityId, permissionId)
|
self.delegate.deleteCommunityTokenPermission(communityId, permissionId)
|
||||||
|
|
||||||
|
proc requiresTokenPermissionToJoinChanged*(self: View) {.signal.}
|
||||||
|
|
||||||
|
proc getRequiresTokenPermissionToJoin(self: View): bool {.slot.} =
|
||||||
|
return self.requiresTokenPermissionToJoin
|
||||||
|
|
||||||
|
proc setRequiresTokenPermissionToJoin*(self: View, value: bool) =
|
||||||
|
if (value == self.requiresTokenPermissionToJoin):
|
||||||
|
return
|
||||||
|
self.requiresTokenPermissionToJoin = value
|
||||||
|
self.requiresTokenPermissionToJoinChanged()
|
||||||
|
|
||||||
|
QtProperty[bool] requiresTokenPermissionToJoin:
|
||||||
|
read = getRequiresTokenPermissionToJoin
|
||||||
|
notify = requiresTokenPermissionToJoinChanged
|
||||||
|
|
||||||
|
proc getAmIMember*(self: View): bool {.slot.} =
|
||||||
|
return self.amIMember
|
||||||
|
|
||||||
|
proc amIMemberChanged*(self: View) {.signal.}
|
||||||
|
|
||||||
|
proc setAmIMember*(self: View, value: bool) =
|
||||||
|
if (value == self.amIMember):
|
||||||
|
return
|
||||||
|
self.amIMember = value
|
||||||
|
self.amIMemberChanged()
|
||||||
|
|
||||||
|
QtProperty[bool] amIMember:
|
||||||
|
read = getAmIMember
|
||||||
|
notify = amIMemberChanged
|
||||||
|
|
||||||
proc getAllTokenRequirementsMet*(self: View): bool {.slot.} =
|
proc getAllTokenRequirementsMet*(self: View): bool {.slot.} =
|
||||||
return self.allTokenRequirementsMet
|
return self.allTokenRequirementsMet
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,12 @@ QtObject:
|
||||||
proc getItems*(self: TokenCriteriaModel): seq[TokenCriteriaItem] =
|
proc getItems*(self: TokenCriteriaModel): seq[TokenCriteriaItem] =
|
||||||
return self.items
|
return self.items
|
||||||
|
|
||||||
|
proc setItems*(self: TokenCriteriaModel, items: seq[TokenCriteriaItem]) =
|
||||||
|
self.beginResetModel()
|
||||||
|
self.items = items
|
||||||
|
self.endResetModel()
|
||||||
|
self.countChanged()
|
||||||
|
|
||||||
proc addItem*(self: TokenCriteriaModel, item: TokenCriteriaItem) =
|
proc addItem*(self: TokenCriteriaModel, item: TokenCriteriaItem) =
|
||||||
let parentModelIndex = newQModelIndex()
|
let parentModelIndex = newQModelIndex()
|
||||||
defer: parentModelIndex.delete
|
defer: parentModelIndex.delete
|
||||||
|
|
|
@ -35,13 +35,24 @@ QtObject:
|
||||||
read = getCount
|
read = getCount
|
||||||
notify = countChanged
|
notify = countChanged
|
||||||
|
|
||||||
proc setItems*(self: TokenlistModel, items: seq[TokenListItem]) =
|
proc setItems*(self: TokenListModel, items: seq[TokenListItem]) =
|
||||||
self.beginResetModel()
|
self.beginResetModel()
|
||||||
self.items = items
|
self.items = items
|
||||||
self.endResetModel()
|
self.endResetModel()
|
||||||
self.countChanged()
|
self.countChanged()
|
||||||
|
|
||||||
proc addItems*(self: TokenlistModel, items: seq[TokenListItem]) =
|
proc hasItem*(self: TokenListModel, symbol: string): bool =
|
||||||
|
for item in self.items:
|
||||||
|
if item.getSymbol() == symbol:
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
proc getItem*(self: TokenListModel, symbol: string): TokenListItem =
|
||||||
|
for item in self.items:
|
||||||
|
if item.getSymbol() == symbol:
|
||||||
|
return item
|
||||||
|
|
||||||
|
proc addItems*(self: TokenListModel, items: seq[TokenListItem]) =
|
||||||
if(items.len == 0):
|
if(items.len == 0):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import NimQml, Tables
|
import NimQml, Tables
|
||||||
import token_permission_item
|
import token_permission_item
|
||||||
|
import token_criteria_model
|
||||||
|
|
||||||
type
|
type
|
||||||
ModelRole {.pure.} = enum
|
ModelRole {.pure.} = enum
|
||||||
|
@ -36,7 +37,7 @@ QtObject:
|
||||||
}.toTable
|
}.toTable
|
||||||
|
|
||||||
proc countChanged(self: TokenPermissionsModel) {.signal.}
|
proc countChanged(self: TokenPermissionsModel) {.signal.}
|
||||||
proc getCount(self: TokenPermissionsModel): int {.slot.} =
|
proc getCount*(self: TokenPermissionsModel): int {.slot.} =
|
||||||
self.items.len
|
self.items.len
|
||||||
QtProperty[int] count:
|
QtProperty[int] count:
|
||||||
read = getCount
|
read = getCount
|
||||||
|
@ -107,9 +108,8 @@ QtObject:
|
||||||
if(idx == -1):
|
if(idx == -1):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.items[idx].id = permissionId
|
|
||||||
self.items[idx].`type` = item.`type`
|
self.items[idx].`type` = item.`type`
|
||||||
self.items[idx].tokenCriteria = item.tokenCriteria
|
self.items[idx].tokenCriteria.setItems(item.tokenCriteria.getItems())
|
||||||
self.items[idx].isPrivate = item.isPrivate
|
self.items[idx].isPrivate = item.isPrivate
|
||||||
|
|
||||||
let index = self.createIndex(idx, 0, nil)
|
let index = self.createIndex(idx, 0, nil)
|
||||||
|
|
|
@ -64,6 +64,7 @@ type CommunityTokensMetadataDto* = object
|
||||||
description*: string
|
description*: string
|
||||||
image*: string
|
image*: string
|
||||||
symbol*: string
|
symbol*: string
|
||||||
|
name*: string
|
||||||
tokenType*: TokenType
|
tokenType*: TokenType
|
||||||
|
|
||||||
type CommunityDto* = object
|
type CommunityDto* = object
|
||||||
|
@ -165,6 +166,7 @@ proc toCommunityTokensMetadataDto*(jsonObj: JsonNode): CommunityTokensMetadataDt
|
||||||
discard jsonObj.getProp("description", result.description)
|
discard jsonObj.getProp("description", result.description)
|
||||||
discard jsonObj.getProp("image", result.image)
|
discard jsonObj.getProp("image", result.image)
|
||||||
discard jsonObj.getProp("symbol", result.symbol)
|
discard jsonObj.getProp("symbol", result.symbol)
|
||||||
|
discard jsonObj.getProp("name", result.name)
|
||||||
var tokenTypeInt: int
|
var tokenTypeInt: int
|
||||||
discard jsonObj.getProp("tokenType", tokenTypeInt)
|
discard jsonObj.getProp("tokenType", tokenTypeInt)
|
||||||
result.tokenType = intToEnum(tokenTypeInt, TokenType.ERC721)
|
result.tokenType = intToEnum(tokenTypeInt, TokenType.ERC721)
|
||||||
|
|
|
@ -21,8 +21,6 @@ export community_dto
|
||||||
logScope:
|
logScope:
|
||||||
topics = "community-service"
|
topics = "community-service"
|
||||||
|
|
||||||
include ../../common/json_utils
|
|
||||||
|
|
||||||
type
|
type
|
||||||
CommunityArgs* = ref object of Args
|
CommunityArgs* = ref object of Args
|
||||||
community*: CommunityDto
|
community*: CommunityDto
|
||||||
|
@ -90,6 +88,10 @@ type
|
||||||
tokenPermission*: CommunityTokenPermissionDto
|
tokenPermission*: CommunityTokenPermissionDto
|
||||||
error*: string
|
error*: string
|
||||||
|
|
||||||
|
CommunityTokenMetadataArgs* = ref object of Args
|
||||||
|
communityId*: string
|
||||||
|
tokenMetadata*: CommunityTokensMetadataDto
|
||||||
|
|
||||||
CommunityTokenPermissionRemovedArgs* = ref object of Args
|
CommunityTokenPermissionRemovedArgs* = ref object of Args
|
||||||
communityId*: string
|
communityId*: string
|
||||||
permissionId*: string
|
permissionId*: string
|
||||||
|
@ -156,6 +158,7 @@ const SIGNAL_COMMUNITY_TOKEN_PERMISSION_UPDATED* = "communityTokenPermissionUpda
|
||||||
const SIGNAL_COMMUNITY_TOKEN_PERMISSION_UPDATE_FAILED* = "communityTokenPermissionUpdateFailed"
|
const SIGNAL_COMMUNITY_TOKEN_PERMISSION_UPDATE_FAILED* = "communityTokenPermissionUpdateFailed"
|
||||||
const SIGNAL_COMMUNITY_TOKEN_PERMISSION_DELETED* = "communityTokenPermissionDeleted"
|
const SIGNAL_COMMUNITY_TOKEN_PERMISSION_DELETED* = "communityTokenPermissionDeleted"
|
||||||
const SIGNAL_COMMUNITY_TOKEN_PERMISSION_DELETION_FAILED* = "communityTokenPermissionDeletionFailed"
|
const SIGNAL_COMMUNITY_TOKEN_PERMISSION_DELETION_FAILED* = "communityTokenPermissionDeletionFailed"
|
||||||
|
const SIGNAL_COMMUNITY_TOKEN_METADATA_ADDED* = "communityTokenMetadataAdded"
|
||||||
|
|
||||||
const SIGNAL_CURATED_COMMUNITIES_LOADING* = "curatedCommunitiesLoading"
|
const SIGNAL_CURATED_COMMUNITIES_LOADING* = "curatedCommunitiesLoading"
|
||||||
const SIGNAL_CURATED_COMMUNITIES_LOADED* = "curatedCommunitiesLoaded"
|
const SIGNAL_CURATED_COMMUNITIES_LOADED* = "curatedCommunitiesLoaded"
|
||||||
|
@ -357,6 +360,14 @@ QtObject:
|
||||||
return idx
|
return idx
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
proc findIndexBySymbol(symbol: string, tokens: seq[CommunityTokensMetadataDto]): int =
|
||||||
|
var idx = -1
|
||||||
|
for token in tokens:
|
||||||
|
inc idx
|
||||||
|
if(token.symbol == symbol):
|
||||||
|
return idx
|
||||||
|
return -1
|
||||||
|
|
||||||
proc saveUpdatedCommunity(self: Service, community: var CommunityDto) =
|
proc saveUpdatedCommunity(self: Service, community: var CommunityDto) =
|
||||||
# Community data we get from the signals and responses don't contgain the pending requests
|
# Community data we get from the signals and responses don't contgain the pending requests
|
||||||
# therefore, we must keep the old one
|
# therefore, we must keep the old one
|
||||||
|
@ -512,6 +523,15 @@ QtObject:
|
||||||
self.events.emit(SIGNAL_COMMUNITY_MEMBERS_CHANGED,
|
self.events.emit(SIGNAL_COMMUNITY_MEMBERS_CHANGED,
|
||||||
CommunityMembersArgs(communityId: community.id, members: community.members))
|
CommunityMembersArgs(communityId: community.id, members: community.members))
|
||||||
|
|
||||||
|
# token metadata was added
|
||||||
|
if community.communityTokensMetadata.len > prev_community.communityTokensMetadata.len:
|
||||||
|
for tokenMetadata in community.communityTokensMetadata:
|
||||||
|
if findIndexBySymbol(tokenMetadata.symbol, prev_community.communityTokensMetadata) == -1:
|
||||||
|
self.communities[community.id].communityTokensMetadata.add(tokenMetadata)
|
||||||
|
self.events.emit(SIGNAL_COMMUNITY_TOKEN_METADATA_ADDED,
|
||||||
|
CommunityTokenMetadataArgs(communityId: community.id,
|
||||||
|
tokenMetadata: tokenMetadata))
|
||||||
|
|
||||||
# tokenPermission was added
|
# tokenPermission was added
|
||||||
if community.tokenPermissions.len > prev_community.tokenPermissions.len:
|
if community.tokenPermissions.len > prev_community.tokenPermissions.len:
|
||||||
for id, tokenPermission in community.tokenPermissions:
|
for id, tokenPermission in community.tokenPermissions:
|
||||||
|
|
|
@ -5,14 +5,18 @@ import QtQuick.Layouts 1.14
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
|
|
||||||
import "views"
|
import "views"
|
||||||
|
import "views/communities"
|
||||||
import "stores"
|
import "stores"
|
||||||
import "popups/community"
|
import "popups/community"
|
||||||
|
|
||||||
|
import AppLayouts.Chat.stores 1.0
|
||||||
|
|
||||||
StackLayout {
|
StackLayout {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property RootStore rootStore
|
property RootStore rootStore
|
||||||
readonly property var contactsStore: rootStore.contactsStore
|
readonly property var contactsStore: rootStore.contactsStore
|
||||||
|
readonly property var permissionsStore: rootStore.permissionsStore
|
||||||
|
|
||||||
property var emojiPopup
|
property var emojiPopup
|
||||||
property var stickersPopup
|
property var stickersPopup
|
||||||
|
@ -37,28 +41,69 @@ StackLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatView {
|
Loader {
|
||||||
id: chatView
|
|
||||||
emojiPopup: root.emojiPopup
|
|
||||||
stickersPopup: root.stickersPopup
|
|
||||||
contactsStore: root.contactsStore
|
|
||||||
rootStore: root.rootStore
|
|
||||||
membershipRequestPopup: membershipRequestPopupComponent
|
|
||||||
|
|
||||||
onCommunityInfoButtonClicked: root.currentIndex = 1
|
readonly property var chatItem: root.rootStore.chatCommunitySectionModule
|
||||||
onCommunityManageButtonClicked: root.currentIndex = 1
|
sourceComponent: chatItem.isCommunity() && chatItem.requiresTokenPermissionToJoin && !chatItem.amIMember ? joinCommunityViewComponent : chatViewComponent
|
||||||
|
}
|
||||||
|
|
||||||
onImportCommunityClicked: {
|
Component {
|
||||||
root.importCommunityClicked();
|
id: joinCommunityViewComponent
|
||||||
|
JoinCommunityView {
|
||||||
|
id: joinCommunityView
|
||||||
|
readonly property var communityData: root.rootStore.mainModuleInst ? root.rootStore.mainModuleInst.activeSection || {} : {}
|
||||||
|
name: communityData.name
|
||||||
|
communityDesc: communityData.description
|
||||||
|
color: communityData.color
|
||||||
|
image: communityData.image
|
||||||
|
membersCount: communityData.members.count
|
||||||
|
accessType: communityData.access
|
||||||
|
joinCommunity: true
|
||||||
|
amISectionAdmin: communityData.amISectionAdmin
|
||||||
|
communityItemsModel: root.rootStore.communityItemsModel
|
||||||
|
requirementsMet: root.permissionsStore.allTokenRequirementsMet
|
||||||
|
communityHoldingsModel: root.permissionsStore.permissionsModel
|
||||||
|
assetsModel: root.rootStore.assetsModel
|
||||||
|
collectiblesModel: root.rootStore.collectiblesModel
|
||||||
|
isInvitationPending: root.rootStore.isCommunityRequestPending(communityData.id)
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root.rootStore.communitiesModuleInst
|
||||||
|
function onCommunityAccessRequested(communityId: string) {
|
||||||
|
if (communityId === joinCommunityView.communityData.id) {
|
||||||
|
joinCommunityView.isInvitationPending = root.rootStore.isCommunityRequestPending(communityData.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onCreateCommunityClicked: {
|
|
||||||
root.createCommunityClicked();
|
}
|
||||||
}
|
|
||||||
onProfileButtonClicked: {
|
Component {
|
||||||
root.profileButtonClicked()
|
id: chatViewComponent
|
||||||
}
|
ChatView {
|
||||||
onOpenAppSearch: {
|
id: chatView
|
||||||
root.openAppSearch()
|
emojiPopup: root.emojiPopup
|
||||||
|
stickersPopup: root.stickersPopup
|
||||||
|
contactsStore: root.contactsStore
|
||||||
|
rootStore: root.rootStore
|
||||||
|
membershipRequestPopup: membershipRequestPopupComponent
|
||||||
|
|
||||||
|
onCommunityInfoButtonClicked: root.currentIndex = 1
|
||||||
|
onCommunityManageButtonClicked: root.currentIndex = 1
|
||||||
|
|
||||||
|
onImportCommunityClicked: {
|
||||||
|
root.importCommunityClicked();
|
||||||
|
}
|
||||||
|
onCreateCommunityClicked: {
|
||||||
|
root.createCommunityClicked();
|
||||||
|
}
|
||||||
|
onProfileButtonClicked: {
|
||||||
|
root.profileButtonClicked()
|
||||||
|
}
|
||||||
|
onOpenAppSearch: {
|
||||||
|
root.openAppSearch()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@ QtObject {
|
||||||
|
|
||||||
readonly property bool isOwner: false
|
readonly property bool isOwner: false
|
||||||
|
|
||||||
|
readonly property bool allTokenRequirementsMet: chatCommunitySectionModuleInst.allTokenRequirementsMet
|
||||||
|
|
||||||
readonly property QtObject _d: QtObject {
|
readonly property QtObject _d: QtObject {
|
||||||
id: d
|
id: d
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import QtQuick 2.13
|
import QtQuick 2.13
|
||||||
|
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
||||||
import shared.stores 1.0
|
import shared.stores 1.0
|
||||||
|
|
||||||
|
@ -30,6 +31,57 @@ QtObject {
|
||||||
// Each `ChatLayout` has its own chatCommunitySectionModule
|
// Each `ChatLayout` has its own chatCommunitySectionModule
|
||||||
// (on the backend chat and community sections share the same module since they are actually the same)
|
// (on the backend chat and community sections share the same module since they are actually the same)
|
||||||
property var chatCommunitySectionModule
|
property var chatCommunitySectionModule
|
||||||
|
|
||||||
|
property var communityItemsModel: chatCommunitySectionModule.model
|
||||||
|
|
||||||
|
property var assetsModel: SortFilterProxyModel {
|
||||||
|
sourceModel: chatCommunitySectionModule.tokenList
|
||||||
|
|
||||||
|
proxyRoles: ExpressionRole {
|
||||||
|
|
||||||
|
// list of symbols for which pngs are stored to avoid
|
||||||
|
// accessing not existing resources and providing
|
||||||
|
// default icon
|
||||||
|
readonly property var pngs: [
|
||||||
|
"aKNC", "AST", "BLT", "CND", "DNT", "EQUAD", "HEZ", "LOOM", "MTH",
|
||||||
|
"PAY", "RCN", "SALT", "STRK", "TRST", "WBTC", "AKRO", "aSUSD", "BLZ",
|
||||||
|
"COB", "DPY", "ETH2x-FLI", "HST", "LPT", "MTL", "PBTC", "RDN", "SAN",
|
||||||
|
"STT", "TRX", "WETH", "0-native", "aLEND", "ATMChain", "BNB", "COMP",
|
||||||
|
"DRT", "ETHOS", "HT", "LRC", "MYB", "PLR", "renBCH", "SNGLS", "STX",
|
||||||
|
"TUSD", "WINGS", "0XBTC", "aLINK", "aTUSD", "BNT", "CUSTOM-TOKEN",
|
||||||
|
"DTA", "ETH", "ICN", "MANA", "NEXO", "POE", "renBTC", "SNM", "SUB",
|
||||||
|
"UBT", "WTC", "1ST", "aMANA", "aUSDC", "BQX", "CVC", "EDG", "EVX",
|
||||||
|
"ICOS", "MCO", "NEXXO", "POLY", "REN", "SNT", "SUPR", "UKG", "XAUR",
|
||||||
|
"aBAT", "AMB", "aUSDT", "BRLN", "DAI", "EDO", "FUEL", "IOST", "MDA",
|
||||||
|
"NMR", "POWR", "renZEC", "SNX", "SUSD", "UNI", "XPA", "ABT", "aMKR",
|
||||||
|
"aWBTC", "BTM", "DATA", "EKG", "FUN", "KDO", "MET", "NPXS", "PPP",
|
||||||
|
"REP", "SOCKS", "TAAS", "UPP", "XRL", "aBUSD", "AMPL", "aYFI", "BTU",
|
||||||
|
"DAT", "EKO", "FXC", "KIN", "MFG", "OGN", "PPT", "REQ", "SPANK",
|
||||||
|
"TAUD", "USDC", "XUC", "ABYSS", "ANT", "aZRX", "CDAI", "DCN", "ELF",
|
||||||
|
"GDC", "KNC", "MGO", "OMG", "PT", "RHOC", "SPIKE", "TCAD", "USDS",
|
||||||
|
"ZRX", "aDAI", "APPC", "BAL", "CDT", "DEFAULT-TOKEN", "EMONA", "GEN",
|
||||||
|
"Kudos", "MKR", "OST", "QKC", "RLC", "SPN", "TGBP", "USDT", "ZSC",
|
||||||
|
"aENJ", "aREN", "BAM", "Centra", "DGD", "ENG", "GNO", "LEND", "MLN",
|
||||||
|
"OTN", "QRL", "ROL", "STORJ", "TKN", "VERI", "AE", "aREP", "BAND",
|
||||||
|
"CFI", "DGX", "ENJ", "GNT", "LINK", "MOC", "PAXG", "QSP", "R",
|
||||||
|
"STORM", "TKX", "VIB", "aETH", "aSNX", "BAT", "CK", "DLT", "EOS",
|
||||||
|
"GRID", "LISK", "MOD", "PAX", "RAE", "SAI", "ST", "TNT", "WABI"
|
||||||
|
]
|
||||||
|
|
||||||
|
function icon(symbol) {
|
||||||
|
if (pngs.indexOf(symbol) !== -1)
|
||||||
|
return Style.png("tokens/" + symbol)
|
||||||
|
|
||||||
|
return Style.png("tokens/DEFAULT-TOKEN")
|
||||||
|
}
|
||||||
|
|
||||||
|
name: "iconSource"
|
||||||
|
expression: !!model.icon ? model.icon : icon(model.symbol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property var collectiblesModel: chatCommunitySectionModule.collectiblesModel
|
||||||
|
|
||||||
// Since qml component doesn't follow encaptulation from the backend side, we're introducing
|
// Since qml component doesn't follow encaptulation from the backend side, we're introducing
|
||||||
// a method which will return appropriate chat content module for selected chat/channel
|
// a method which will return appropriate chat content module for selected chat/channel
|
||||||
function currentChatContentModule(){
|
function currentChatContentModule(){
|
||||||
|
@ -170,9 +222,6 @@ QtObject {
|
||||||
stickersModule: stickersModuleInst
|
stickersModule: stickersModuleInst
|
||||||
}
|
}
|
||||||
|
|
||||||
property var assetsModel: chatCommunitySectionModule.tokenList
|
|
||||||
property var collectiblesModel: chatCommunitySectionModule.collectiblesModel
|
|
||||||
|
|
||||||
function sendSticker(channelId, hash, replyTo, pack, url) {
|
function sendSticker(channelId, hash, replyTo, pack, url) {
|
||||||
stickersModuleInst.send(channelId, hash, replyTo, pack, url)
|
stickersModuleInst.send(channelId, hash, replyTo, pack, url)
|
||||||
}
|
}
|
||||||
|
|
|
@ -257,52 +257,8 @@ StatusSectionLayout {
|
||||||
// method is used in wallet (constructing filename from asset's
|
// method is used in wallet (constructing filename from asset's
|
||||||
// symbol) and is intended to be replaced by more robust
|
// symbol) and is intended to be replaced by more robust
|
||||||
// solution soon.
|
// solution soon.
|
||||||
assetsModel: SortFilterProxyModel {
|
|
||||||
sourceModel: rootStore.assetsModel
|
|
||||||
|
|
||||||
proxyRoles: ExpressionRole {
|
|
||||||
|
|
||||||
// list of symbols for which pngs are stored to avoid
|
|
||||||
// accessing not existing resources and providing
|
|
||||||
// default icon
|
|
||||||
readonly property var pngs: [
|
|
||||||
"aKNC", "AST", "BLT", "CND", "DNT", "EQUAD", "HEZ", "LOOM", "MTH",
|
|
||||||
"PAY", "RCN", "SALT", "STRK", "TRST", "WBTC", "AKRO", "aSUSD", "BLZ",
|
|
||||||
"COB", "DPY", "ETH2x-FLI", "HST", "LPT", "MTL", "PBTC", "RDN", "SAN",
|
|
||||||
"STT", "TRX", "WETH", "0-native", "aLEND", "ATMChain", "BNB", "COMP",
|
|
||||||
"DRT", "ETHOS", "HT", "LRC", "MYB", "PLR", "renBCH", "SNGLS", "STX",
|
|
||||||
"TUSD", "WINGS", "0XBTC", "aLINK", "aTUSD", "BNT", "CUSTOM-TOKEN",
|
|
||||||
"DTA", "ETH", "ICN", "MANA", "NEXO", "POE", "renBTC", "SNM", "SUB",
|
|
||||||
"UBT", "WTC", "1ST", "aMANA", "aUSDC", "BQX", "CVC", "EDG", "EVX",
|
|
||||||
"ICOS", "MCO", "NEXXO", "POLY", "REN", "SNT", "SUPR", "UKG", "XAUR",
|
|
||||||
"aBAT", "AMB", "aUSDT", "BRLN", "DAI", "EDO", "FUEL", "IOST", "MDA",
|
|
||||||
"NMR", "POWR", "renZEC", "SNX", "SUSD", "UNI", "XPA", "ABT", "aMKR",
|
|
||||||
"aWBTC", "BTM", "DATA", "EKG", "FUN", "KDO", "MET", "NPXS", "PPP",
|
|
||||||
"REP", "SOCKS", "TAAS", "UPP", "XRL", "aBUSD", "AMPL", "aYFI", "BTU",
|
|
||||||
"DAT", "EKO", "FXC", "KIN", "MFG", "OGN", "PPT", "REQ", "SPANK",
|
|
||||||
"TAUD", "USDC", "XUC", "ABYSS", "ANT", "aZRX", "CDAI", "DCN", "ELF",
|
|
||||||
"GDC", "KNC", "MGO", "OMG", "PT", "RHOC", "SPIKE", "TCAD", "USDS",
|
|
||||||
"ZRX", "aDAI", "APPC", "BAL", "CDT", "DEFAULT-TOKEN", "EMONA", "GEN",
|
|
||||||
"Kudos", "MKR", "OST", "QKC", "RLC", "SPN", "TGBP", "USDT", "ZSC",
|
|
||||||
"aENJ", "aREN", "BAM", "Centra", "DGD", "ENG", "GNO", "LEND", "MLN",
|
|
||||||
"OTN", "QRL", "ROL", "STORJ", "TKN", "VERI", "AE", "aREP", "BAND",
|
|
||||||
"CFI", "DGX", "ENJ", "GNT", "LINK", "MOC", "PAXG", "QSP", "R",
|
|
||||||
"STORM", "TKX", "VIB", "aETH", "aSNX", "BAT", "CK", "DLT", "EOS",
|
|
||||||
"GRID", "LISK", "MOD", "PAX", "RAE", "SAI", "ST", "TNT", "WABI"
|
|
||||||
]
|
|
||||||
|
|
||||||
function icon(symbol) {
|
|
||||||
if (pngs.indexOf(symbol) !== -1)
|
|
||||||
return Style.png("tokens/" + symbol)
|
|
||||||
|
|
||||||
return Style.png("tokens/DEFAULT-TOKEN")
|
|
||||||
}
|
|
||||||
|
|
||||||
name: "iconSource"
|
|
||||||
expression: !!model.icon ? model.icon : icon(model.symbol)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
assetsModel: rootStore.assetsModel
|
||||||
collectiblesModel: rootStore.collectiblesModel
|
collectiblesModel: rootStore.collectiblesModel
|
||||||
channelsModel: rootStore.chatCommunitySectionModule.model
|
channelsModel: rootStore.chatCommunitySectionModule.model
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 3f3e8f8894dfd5c8a6124bce805d0bd708fb6433
|
Subproject commit f60716412259aece88d899d09fc71de4c4ba5d2e
|
Loading…
Reference in New Issue