feat(@desktop/wallet): implement token management settings for collectibles

Fixes #13233
This commit is contained in:
Dario Gabriel Lipicar 2024-01-17 11:36:13 -03:00 committed by dlipicar
parent c82a4ba602
commit b186a133ab
14 changed files with 397 additions and 3 deletions

View File

@ -10,6 +10,7 @@ import ../../app_service/service/chat/service as chat_service
import ../../app_service/service/community/service as community_service
import ../../app_service/service/message/service as message_service
import ../../app_service/service/token/service as token_service
import ../../app_service/service/collectible/service as collectible_service
import ../../app_service/service/currency/service as currency_service
import ../../app_service/service/transaction/service as transaction_service
import ../../app_service/service/wallet_account/service as wallet_account_service
@ -75,6 +76,7 @@ type
communityService: community_service.Service
messageService: message_service.Service
tokenService: token_service.Service
collectibleService: collectible_service.Service
currencyService: currency_service.Service
transactionService: transaction_service.Service
walletAccountService: wallet_account_service.Service
@ -178,6 +180,9 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.tokenService = token_service.newService(
statusFoundation.events, statusFoundation.threadpool, result.networkService, result.settingsService
)
result.collectibleService = collectible_service.newService(
statusFoundation.events, statusFoundation.threadpool
)
result.currencyService = currency_service.newService(
statusFoundation.events, statusFoundation.threadpool, result.tokenService, result.settingsService
)
@ -253,6 +258,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.communityService,
result.messageService,
result.tokenService,
result.collectibleService,
result.currencyService,
result.transactionService,
result.walletAccountService,
@ -313,6 +319,7 @@ proc delete*(self: AppController) =
self.chatService.delete
self.communityService.delete
self.currencyService.delete
self.collectibleService.delete
self.tokenService.delete
self.transactionService.delete
self.walletAccountService.delete
@ -441,6 +448,7 @@ proc load(self: AppController) =
self.networkService.init()
self.tokenService.init()
self.collectibleService.init()
self.currencyService.init()
self.walletAccountService.init()

View File

@ -36,6 +36,7 @@ import ../../../app_service/service/chat/service as chat_service
import ../../../app_service/service/community/service as community_service
import ../../../app_service/service/message/service as message_service
import ../../../app_service/service/token/service as token_service
import ../../../app_service/service/collectible/service as collectible_service
import ../../../app_service/service/currency/service as currency_service
import ../../../app_service/service/transaction/service as transaction_service
import ../../../app_service/service/wallet_account/service as wallet_account_service
@ -135,6 +136,7 @@ proc newModule*[T](
communityService: community_service.Service,
messageService: message_service.Service,
tokenService: token_service.Service,
collectibleService: collectible_service.Service,
currencyService: currency_service.Service,
transactionService: transaction_service.Service,
walletAccountService: wallet_account_service.Service,
@ -204,7 +206,7 @@ proc newModule*[T](
# Submodules
result.channelGroupModules = initOrderedTable[string, chat_section_module.AccessInterface]()
result.walletSectionModule = wallet_section_module.newModule(
result, events, tokenService, currencyService,
result, events, tokenService, collectibleService, currencyService,
transactionService, walletAccountService,
settingsService, savedAddressService, networkService, accountsService,
keycardService, nodeService, networkConnectionService, devicesService

View File

@ -0,0 +1,61 @@
import json, sequtils, sugar
import ./io_interface
import app/core/eventemitter
import app_service/service/collectible/service as collectible_service
import app_service/service/network/service as network_service
import app_service/service/wallet_account/service as wallet_account_service
import app_service/service/settings/service as settings_service
type
Controller* = ref object of RootObj
delegate: io_interface.AccessInterface
events: EventEmitter
collectibleService: collectible_service.Service
networkService: network_service.Service
walletAccountService: wallet_account_service.Service
settingsService: settings_service.Service
proc newController*(
delegate: io_interface.AccessInterface,
events: EventEmitter,
collectibleService: collectible_service.Service,
networkService: network_service.Service,
walletAccountService: wallet_account_service.Service,
settingsService: settings_service.Service
): Controller =
result = Controller()
result.events = events
result.delegate = delegate
result.networkService = networkService
result.walletAccountService = walletAccountService
result.collectibleService = collectibleService
result.settingsService = settingsService
proc delete*(self: Controller) =
discard
proc init*(self: Controller) =
discard
proc updateCollectiblePreferences*(self: Controller, tokenPreferencesJson: string) =
self.collectibleService.updateCollectiblePreferences(tokenPreferencesJson)
proc getCollectiblePreferencesJson*(self: Controller): string =
let data = self.collectibleService.getCollectiblePreferences()
if data.isNil:
return "[]"
return $data
proc getCollectibleGroupByCommunity*(self: Controller): bool =
return self.settingsService.collectibleGroupByCommunity()
proc toggleCollectibleGroupByCommunity*(self: Controller): bool =
return self.settingsService.toggleCollectibleGroupByCommunity()
proc getCollectibleGroupByCollection*(self: Controller): bool =
return self.settingsService.collectibleGroupByCollection()
proc toggleCollectibleGroupByCollection*(self: Controller): bool =
return self.settingsService.toggleCollectibleGroupByCollection()

View File

@ -0,0 +1,38 @@
import app/modules/shared_models/collectibles_model as collectibles_model
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
## Abstract class for any input/interaction with this module.
method delete*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method load*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method isLoaded*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method updateCollectiblePreferences*(self: AccessInterface, collectiblePreferencesJson: string) {.base.} =
raise newException(ValueError, "No implementation available")
method getCollectiblePreferencesJson*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
# View Delegate Interface
# Delegate for the view must be declared here due to use of QtObject and multi
# inheritance, which is not well supported in Nim.
method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method getCollectibleGroupByCommunity*(self: AccessInterface): bool =
raise newException(ValueError, "No implementation available")
method toggleCollectibleGroupByCommunity*(self: AccessInterface): bool =
raise newException(ValueError, "No implementation available")
method getCollectibleGroupByCollection*(self: AccessInterface): bool =
raise newException(ValueError, "No implementation available")
method toggleCollectibleGroupByCollection*(self: AccessInterface): bool =
raise newException(ValueError, "No implementation available")

View File

@ -0,0 +1,81 @@
import NimQml
import ./io_interface, ./view
import ./controller as all_collectibles_controller
import ../io_interface as delegate_interface
import app/global/global_singleton
import app/core/eventemitter
import app/modules/shared_modules/collectibles/controller as collectibles_controller
import app/modules/shared_models/collectibles_model as collectibles_model
import app_service/service/collectible/service as collectible_service
import app_service/service/network/service as network_service
import app_service/service/wallet_account/service as wallet_account_service
import app_service/service/settings/service as settings_service
import backend/collectibles as backend_collectibles
export io_interface
type
Module* = ref object of io_interface.AccessInterface
delegate: delegate_interface.AccessInterface
events: EventEmitter
view: View
controller: all_collectibles_controller.Controller
moduleLoaded: bool
proc newModule*(
delegate: delegate_interface.AccessInterface,
events: EventEmitter,
collectibleService: collectible_service.Service,
networkService: network_service.Service,
walletAccountService: wallet_account_service.Service,
settingsService: settings_service.Service
): Module =
result = Module()
result.delegate = delegate
result.events = events
result.controller = all_collectibles_controller.newController(result, events, collectibleService, networkService, walletAccountService, settingsService)
result.view = newView(result)
result.moduleLoaded = false
method delete*(self: Module) =
self.view.delete
self.controller.delete
method load*(self: Module) =
singletonInstance.engine.setRootContextProperty("walletSectionAllCollectibles", newQVariant(self.view))
self.events.on(SIGNAL_COLLECTIBLE_PREFERENCES_UPDATED) do(e: Args):
let args = ResultArgs(e)
self.view.collectiblePreferencesUpdated(args.success)
self.controller.init()
self.view.load()
method isLoaded*(self: Module): bool =
return self.moduleLoaded
method viewDidLoad*(self: Module) =
self.moduleLoaded = true
self.delegate.allCollectiblesModuleDidLoad()
method updateCollectiblePreferences*(self: Module, collectiblePreferencesJson: string) {.slot.} =
self.controller.updateCollectiblePreferences(collectiblePreferencesJson)
method getCollectiblePreferencesJson*(self: Module): string =
return self.controller.getCollectiblePreferencesJson()
method getCollectibleGroupByCommunity*(self: Module): bool =
return self.controller.getCollectibleGroupByCommunity()
method toggleCollectibleGroupByCommunity*(self: Module): bool =
return self.controller.toggleCollectibleGroupByCommunity()
method getCollectibleGroupByCollection*(self: Module): bool =
return self.controller.getCollectibleGroupByCollection()
method toggleCollectibleGroupByCollection*(self: Module): bool =
return self.controller.toggleCollectibleGroupByCollection()

View File

@ -0,0 +1,60 @@
import NimQml, sequtils, strutils, chronicles
import ./io_interface
QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
proc delete*(self: View) =
self.QObject.delete
proc newView*(delegate: io_interface.AccessInterface): View =
new(result, delete)
result.QObject.setup
result.delegate = delegate
proc load*(self: View) =
self.delegate.viewDidLoad()
proc collectiblePreferencesUpdated*(self: View, result: bool) {.signal.}
proc updateCollectiblePreferences*(self: View, collectiblePreferencesJson: string) {.slot.} =
self.delegate.updateCollectiblePreferences(collectiblePreferencesJson)
proc getCollectiblePreferencesJson(self: View): QVariant {.slot.} =
let preferences = self.delegate.getCollectiblePreferencesJson()
return newQVariant(preferences)
QtProperty[QVariant] collectiblePreferencesJson:
read = getCollectiblePreferencesJson
proc collectibleGroupByCommunityChanged*(self: View) {.signal.}
proc getCollectibleGroupByCommunity(self: View): bool {.slot.} =
return self.delegate.getCollectibleGroupByCommunity()
QtProperty[bool] collectibleGroupByCommunity:
read = getCollectibleGroupByCommunity
notify = collectibleGroupByCommunityChanged
proc toggleCollectibleGroupByCommunity*(self: View): bool {.slot.} =
if not self.delegate.toggleCollectibleGroupByCommunity():
error "Failed to toggle collectibleGroupByCommunity"
return
self.collectibleGroupByCommunityChanged()
proc collectibleGroupByCollectionChanged*(self: View) {.signal.}
proc getCollectibleGroupByCollection(self: View): bool {.slot.} =
return self.delegate.getCollectibleGroupByCollection()
QtProperty[bool] collectibleGroupByCollection:
read = getCollectibleGroupByCollection
notify = collectibleGroupByCollectionChanged
proc toggleCollectibleGroupByCollection*(self: View): bool {.slot.} =
if not self.delegate.toggleCollectibleGroupByCollection():
error "Failed to toggle collectibleGroupByCollection"
return
self.collectibleGroupByCollectionChanged()

View File

@ -46,6 +46,9 @@ method accountsModuleDidLoad*(self: AccessInterface) {.base.} =
method allTokensModuleDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method allCollectiblesModuleDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method collectiblesModuleDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -6,6 +6,7 @@ import ../io_interface as delegate_interface
import ./accounts/module as accounts_module
import ./all_tokens/module as all_tokens_module
import ./all_collectibles/module as all_collectibles_module
import ./assets/module as assets_module
import ./saved_addresses/module as saved_addresses_module
import ./buy_sell_crypto/module as buy_sell_crypto_module
@ -25,6 +26,7 @@ import app/modules/shared_modules/add_account/module as add_account_module
import app/modules/shared_modules/keypair_import/module as keypair_import_module
import app_service/service/keycard/service as keycard_service
import app_service/service/token/service as token_service
import app_service/service/collectible/service as collectible_service
import app_service/service/currency/service as currency_service
import app_service/service/transaction/service as transaction_service
import app_service/service/wallet_account/service as wallet_account_service
@ -64,6 +66,7 @@ type
# modules
accountsModule: accounts_module.AccessInterface
allTokensModule: all_tokens_module.AccessInterface
allCollectiblesModule: all_collectibles_module.AccessInterface
assetsModule: assets_module.AccessInterface
sendModule: send_module.AccessInterface
savedAddressesModule: saved_addresses_module.AccessInterface
@ -93,6 +96,7 @@ proc newModule*(
delegate: delegate_interface.AccessInterface,
events: EventEmitter,
tokenService: token_service.Service,
collectibleService: collectible_service.Service,
currencyService: currency_service.Service,
transactionService: transaction_service.Service,
walletAccountService: wallet_account_service.Service,
@ -117,6 +121,7 @@ proc newModule*(
result.accountsModule = accounts_module.newModule(result, events, walletAccountService, networkService, currencyService)
result.allTokensModule = all_tokens_module.newModule(result, events, tokenService, walletAccountService, settingsService)
result.allCollectiblesModule = all_collectibles_module.newModule(result, events, collectibleService, networkService, walletAccountService, settingsService)
result.assetsModule = assets_module.newModule(result, events, walletAccountService, networkService, tokenService,
currencyService)
result.sendModule = send_module.newModule(result, events, walletAccountService, networkService, currencyService,
@ -149,6 +154,7 @@ proc newModule*(
method delete*(self: Module) =
self.accountsModule.delete
self.allTokensModule.delete
self.allCollectiblesModule.delete
self.assetsModule.delete
self.savedAddressesModule.delete
self.buySellCryptoModule.delete
@ -282,6 +288,7 @@ method load*(self: Module) =
self.view.load()
self.accountsModule.load()
self.allTokensModule.load()
self.allCollectiblesModule.load()
self.assetsModule.load()
self.savedAddressesModule.load()
self.buySellCryptoModule.load()
@ -299,6 +306,9 @@ proc checkIfModuleDidLoad(self: Module) =
if(not self.allTokensModule.isLoaded()):
return
if(not self.allCollectiblesModule.isLoaded()):
return
if(not self.assetsModule.isLoaded()):
return
@ -336,6 +346,9 @@ method accountsModuleDidLoad*(self: Module) =
method allTokensModuleDidLoad*(self: Module) =
self.checkIfModuleDidLoad()
method allCollectiblesModuleDidLoad*(self: Module) =
self.checkIfModuleDidLoad()
method collectiblesModuleDidLoad*(self: Module) =
self.checkIfModuleDidLoad()

View File

@ -0,0 +1,64 @@
import NimQml, Tables, json, sequtils, chronicles, strutils, algorithm, sugar
import backend/collectibles as backend
import app/core/eventemitter
import app/core/tasks/[qt, threadpool]
import app/core/signals/types
logScope:
topics = "collectible-service"
# Signals which may be emitted by this service:
const SIGNAL_COLLECTIBLE_PREFERENCES_UPDATED* = "collectiblePreferencesUpdated"
type
ResultArgs* = ref object of Args
success*: bool
QtObject:
type Service* = ref object of QObject
events: EventEmitter
threadpool: ThreadPool
proc delete*(self: Service) =
self.QObject.delete
proc newService*(
events: EventEmitter,
threadpool: ThreadPool
): Service =
new(result, delete)
result.QObject.setup
result.events = events
result.threadpool = threadpool
proc init*(self: Service) =
discard
proc getCollectiblePreferences*(self: Service): JsonNode =
try:
let response = backend.getCollectiblePreferences()
if not response.error.isNil:
error "status-go error", procName="getCollectiblePreferences", errCode=response.error.code, errDesription=response.error.message
return
return response.result
except Exception as e:
error "error: ", procName="getCollectiblePreferences", errName=e.name, errDesription=e.msg
proc updateCollectiblePreferences*(self: Service, collectiblePreferencesJson: string) =
var updated = false
try:
let preferencesJson = parseJson(collectiblePreferencesJson)
var collectiblePreferences: seq[CollectiblePreferences]
if preferencesJson.kind == JArray:
for preferences in preferencesJson:
add(collectiblePreferences, fromJson(preferences, CollectiblePreferences))
let response = backend.updateCollectiblePreferences(collectiblePreferences)
if not response.error.isNil:
raise newException(CatchableError, response.error.message)
updated = true
except Exception as e:
error "error: ", procName="updateCollectiblePreferences", errName=e.name, errDesription=e.msg
self.events.emit(SIGNAL_COLLECTIBLE_PREFERENCES_UPDATED, ResultArgs(success: updated))

View File

@ -49,6 +49,8 @@ const KEY_TOKEN_GROUP_BY_COMMUNITY* = "token-group-by-community?"
const KEY_SHOW_COMMUNITY_ASSET_WHEN_SENDING_TOKENS* = "show-community-asset-when-sending-tokens?"
const KEY_DISPLAY_ASSETS_BELOW_BALANCE* = "display-assets-below-balance?"
const KEY_DISPLAY_ASSETS_BELOW_BALANCE_THRESHOLD* = "display-assets-below-balance-threshold"
const KEY_COLLECTIBLE_GROUP_BY_COMMUNITY* = "collectible-group-by-community?"
const KEY_COLLECTIBLE_GROUP_BY_COLLECTION* = "collectible-group-by-collection?"
const PROFILE_MIGRATION_NEEDED* = "profile-migration-needed"
const KEY_URL_UNFURLING_MODE* = "url-unfurling-mode"
@ -160,6 +162,8 @@ type
showCommunityAssetWhenSendingTokens*: bool
displayAssetsBelowBalance*: bool
displayAssetsBelowBalanceThreshold*: int64
collectibleGroupByCommunity*: bool
collectibleGroupByCollection*: bool
urlUnfurlingMode*: UrlUnfurlingMode
@ -221,6 +225,8 @@ proc toSettingsDto*(jsonObj: JsonNode): SettingsDto =
discard jsonObj.getProp(KEY_SHOW_COMMUNITY_ASSET_WHEN_SENDING_TOKENS, result.showCommunityAssetWhenSendingTokens)
discard jsonObj.getProp(KEY_DISPLAY_ASSETS_BELOW_BALANCE, result.displayAssetsBelowBalance)
discard jsonObj.getProp(KEY_DISPLAY_ASSETS_BELOW_BALANCE_THRESHOLD, result.displayAssetsBelowBalanceThreshold)
discard jsonObj.getProp(KEY_COLLECTIBLE_GROUP_BY_COMMUNITY, result.collectibleGroupByCommunity)
discard jsonObj.getProp(KEY_COLLECTIBLE_GROUP_BY_COLLECTION, result.collectibleGroupByCollection)
discard jsonObj.getProp(PROFILE_MIGRATION_NEEDED, result.profileMigrationNeeded)
var urlUnfurlingMode: int

View File

@ -537,6 +537,26 @@ QtObject:
return true
return false
proc collectibleGroupByCommunity*(self: Service): bool =
return self.settings.collectibleGroupByCommunity
proc toggleCollectibleGroupByCommunity*(self: Service): bool =
let newValue = not self.settings.collectibleGroupByCommunity
if(self.saveSetting(KEY_COLLECTIBLE_GROUP_BY_COMMUNITY, newValue)):
self.settings.collectibleGroupByCommunity = newValue
return true
return false
proc collectibleGroupByCollection*(self: Service): bool =
return self.settings.collectibleGroupByCollection
proc toggleCollectibleGroupByCollection*(self: Service): bool =
let newValue = not self.settings.collectibleGroupByCollection
if(self.saveSetting(KEY_COLLECTIBLE_GROUP_BY_COLLECTION, newValue)):
self.settings.collectibleGroupByCollection = newValue
return true
return false
proc urlUnfurlingMode*(self: Service): UrlUnfurlingMode =
return self.settings.urlUnfurlingMode

View File

@ -272,3 +272,9 @@ rpc(getCollectiblesByUniqueIDAsync, "wallet"):
rpc(refetchOwnedCollectibles, "wallet"):
discard
rpc(updateCollectiblePreferences, "accounts"):
preferences: seq[CollectiblePreferences]
rpc(getCollectiblePreferences, "accounts"):
discard

View File

@ -2,6 +2,8 @@ import json, strformat, json_serialization
import stint, Tables, options
import community_tokens_types
include app_service/common/json_utils
type
# Mirrors services/wallet/thirdparty/collectible_types.go ContractID
ContractID* = ref object of RootObj
@ -74,6 +76,20 @@ type
contractAddress*: string
owners*: seq[CollectibleOwner]
# see status-go/services/wallet/collectibles/service.go CollectibleDataType
CollectiblePreferencesItemType* {.pure.} = enum
NonCommunityCollectible = 1,
CommunityCollectible,
Collection,
Community
# Mirrors services/wallet/thirdparty/collectible_types.go CollectibleContractOwnership
CollectiblePreferences* = ref object of RootObj
itemType* {.serializedFieldName("type").}: CollectiblePreferencesItemType
key* {.serializedFieldName("key").}: string
position* {.serializedFieldName("position").}: int
visible* {.serializedFieldName("visible").}: bool
# ContractID
proc `$`*(self: ContractID): string =
return fmt"""ContractID(
@ -365,4 +381,20 @@ proc fromJson*(t: JsonNode, T: typedesc[CollectibleContractOwnership]): Collecti
return CollectibleContractOwnership(
contractAddress: t{"contractAddress"}.getStr,
owners: getCollectibleOwners(t{"owners"})
)
)
# CollectiblePreferences
proc `$`*(self: CollectiblePreferences): string =
return fmt"""CollectiblePreferences(
type:{self.itemType},
key:{self.key},
position:{self.position},
visible:{self.visible}
"""
proc fromJson*(t: JsonNode, T: typedesc[CollectiblePreferences]): CollectiblePreferences {.inline.} =
result = CollectiblePreferences()
result.itemType = t{"type"}.getInt().CollectiblePreferencesItemType
discard t.getProp("key", result.key)
discard t.getProp("position", result.position)
discard t.getProp("visible", result.visible)

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit b38e2c9278b0f02368048d38a158da8f4a65bba7
Subproject commit 0d2c3cef7c00d9818f820a4b67e4c4442b3b1be8