refactor: load gifs asynchronously

Instead of loading recent gifs right on startup eagerly,
we postpone the task to when it's actually needed (when the gif popup is
opened and the recent gifs tab is activated), and on top of that
we also load the data asynchronously to keep the amount of work
that needs to be done in a single tick as short as possible.

This needs: status-im/status-go#3170

Closes #9437
This commit is contained in:
Pascal Precht 2023-03-07 10:15:26 +01:00 committed by Jonathan Rainville
parent 2ac1216b68
commit 919f3dc6f7
9 changed files with 253 additions and 98 deletions

View File

@ -209,7 +209,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.mailserversService = mailservers_service.newService(statusFoundation.events, statusFoundation.threadpool,
result.settingsService, result.nodeConfigurationService, statusFoundation.fleetConfiguration)
result.nodeService = node_service.newService(statusFoundation.events, result.settingsService, result.nodeConfigurationService)
result.gifService = gif_service.newService(result.settingsService)
result.gifService = gif_service.newService(result.settingsService, statusFoundation.events, statusFoundation.threadpool)
result.ensService = ens_service.newService(statusFoundation.events, statusFoundation.threadpool,
result.settingsService, result.walletAccountService, result.transactionService,
result.networkService, result.tokenService)

View File

@ -4,11 +4,13 @@ import ../../../../../../app_service/service/community/service as community_serv
import ../../../../../../app_service/service/chat/service as chat_service
import ../../../../../../app_service/service/gif/service as gif_service
import ../../../../../../app_service/service/gif/dto
import ../../../../../core/eventemitter
type
Controller* = ref object of RootObj
delegate: io_interface.AccessInterface
sectionId: string
events: EventEmitter
chatId: string
belongsToCommunity: bool
communityService: community_service.Service
@ -17,6 +19,7 @@ type
proc newController*(
delegate: io_interface.AccessInterface,
events: EventEmitter,
sectionId: string,
chatId: string,
belongsToCommunity: bool,
@ -26,6 +29,7 @@ proc newController*(
): Controller =
result = Controller()
result.delegate = delegate
result.events = events
result.sectionId = chatId
result.chatId = chatId
result.belongsToCommunity = belongsToCommunity
@ -37,7 +41,13 @@ proc delete*(self: Controller) =
discard
proc init*(self: Controller) =
discard
self.events.on(SIGNAL_LOAD_RECENT_GIFS_DONE) do(e:Args):
let args = GifsArgs(e)
self.delegate.loadRecentGifsDone(args.gifs)
self.events.on(SIGNAL_LOAD_FAVORITE_GIFS_DONE) do(e:Args):
let args = GifsArgs(e)
self.delegate.loadFavoriteGifsDone(args.gifs)
proc getChatId*(self: Controller): string =
return self.chatId
@ -84,6 +94,12 @@ proc getTrendingsGifs*(self: Controller): seq[GifDto] =
proc getRecentsGifs*(self: Controller): seq[GifDto] =
return self.gifService.getRecents()
proc loadRecentGifs*(self: Controller) =
self.gifService.asyncLoadRecentGifs()
proc loadFavoriteGifs*(self: Controller) =
self.gifService.asyncLoadFavoriteGifs()
proc getFavoritesGifs*(self: Controller): seq[GifDto] =
return self.gifService.getFavorites()

View File

@ -50,9 +50,21 @@ method getTrendingsGifs*(self: AccessInterface): seq[GifDto] {.base.} =
method getRecentsGifs*(self: AccessInterface): seq[GifDto] {.base.} =
raise newException(ValueError, "No implementation available")
method loadRecentGifs*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method loadRecentGifsDone*(self: AccessInterface, gifs: seq[GifDto]) {.base.} =
raise newException(ValueError, "No implementation available")
method getFavoritesGifs*(self: AccessInterface): seq[GifDto] {.base.} =
raise newException(ValueError, "No implementation available")
method loadFavoriteGifs*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method loadFavoriteGifsDone*(self: AccessInterface, gifs: seq[GifDto]) {.base.} =
raise newException(ValueError, "No implementation available")
method toggleFavoriteGif*(self: AccessInterface, item: GifDto) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -3,6 +3,7 @@ import io_interface
import ../io_interface as delegate_interface
import view, controller
import ../../../../../global/global_singleton
import ../../../../../core/eventemitter
import ../../../../../../app_service/service/chat/service as chat_service
import ../../../../../../app_service/service/community/service as community_service
@ -21,6 +22,7 @@ type
proc newModule*(
delegate: delegate_interface.AccessInterface,
events: EventEmitter,
sectionId: string,
chatId: string,
belongsToCommunity: bool,
@ -33,7 +35,7 @@ proc newModule*(
result.delegate = delegate
result.view = view.newView(result)
result.viewVariant = newQVariant(result.view)
result.controller = controller.newController(result, sectionId, chatId, belongsToCommunity, chatService, communityService, gifService)
result.controller = controller.newController(result, events, sectionId, chatId, belongsToCommunity, chatService, communityService, gifService)
result.moduleLoaded = false
method delete*(self: Module) =
@ -98,9 +100,21 @@ method getTrendingsGifs*(self: Module): seq[GifDto] =
method getRecentsGifs*(self: Module): seq[GifDto] =
return self.controller.getRecentsGifs()
method loadRecentGifs*(self: Module) =
self.controller.loadRecentGifs()
method loadRecentGifsDone*(self: Module, gifs: seq[GifDto]) =
self.view.updateGifColumns(gifs)
method getFavoritesGifs*(self: Module): seq[GifDto] =
return self.controller.getFavoritesGifs()
method loadFavoriteGifs*(self: Module) =
self.controller.loadFavoriteGifs()
method loadFavoriteGifsDone*(self: Module, gifs: seq[GifDto]) =
self.view.updateGifColumns(gifs)
method toggleFavoriteGif*(self: Module, item: GifDto) =
self.controller.toggleFavoriteGif(item)

View File

@ -80,7 +80,7 @@ QtObject:
read = getGifColumnC
notify = gifLoaded
proc updateGifColumns(self: View, data: seq[GifDto]) =
proc updateGifColumns*(self: View, data: seq[GifDto]) =
var columnAData: seq[GifDto] = @[]
var columnAHeight = 0
var columnBData: seq[GifDto] = @[]
@ -115,11 +115,21 @@ QtObject:
proc getRecentsGifs*(self: View) {.slot.} =
let data = self.delegate.getRecentsGifs()
if data.len > 0:
self.updateGifColumns(data)
return
# recent gifs were not loaded yet, so we do it now
self.delegate.loadRecentGifs()
proc getFavoritesGifs*(self: View) {.slot.} =
let data = self.delegate.getFavoritesGifs()
if data.len > 0:
self.updateGifColumns(data)
return
# favorite gifs were not loaded yet, so we do it now
self.delegate.loadFavoriteGifs()
proc findGifDto(self: View, id: string): GifDto =
for item in self.gifColumnAModel.gifs:

View File

@ -52,7 +52,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitt
isUsersListAvailable, settingsService, nodeConfigurationService, contactService, chatService, communityService, messageService)
result.moduleLoaded = false
result.inputAreaModule = input_area_module.newModule(result, sectionId, chatId, belongsToCommunity, chatService, communityService, gifService)
result.inputAreaModule = input_area_module.newModule(result, events, sectionId, chatId, belongsToCommunity, chatService, communityService, gifService)
result.messagesModule = messages_module.newModule(result, events, sectionId, chatId, belongsToCommunity,
contactService, communityService, chatService, messageService, mailserversService)
result.usersModule =

View File

@ -0,0 +1,19 @@
include ../../common/json_utils
include ../../../app/core/tasks/common
import ../../../backend/gifs as status_go
type
AsyncGetRecentGifsTaskArg = ref object of QObjectTaskArg
const asyncGetRecentGifsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AsyncGetRecentGifsTaskArg](argEncoded)
let response = status_go.getRecentGifs()
arg.finish(response)
type
AsyncGetFavoriteGifsTaskArg = ref object of QObjectTaskArg
const asyncGetFavoriteGifsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AsyncGetFavoriteGifsTaskArg](argEncoded)
let response = status_go.getFavoriteGifs()
arg.finish(response)

View File

@ -4,11 +4,17 @@ import os
import uri
import chronicles
import sequtils
import NimQml
import ../settings/service as settings_service
import ../../../app/core/eventemitter
import ../../../backend/backend
import ../../../app/core/tasks/[qt, threadpool]
import ../../../app/core/[main]
import ./dto
include ./async_tasks
logScope:
topics = "gif-service"
@ -23,18 +29,35 @@ let TENOR_API_KEY_RESOLVED =
else:
TENOR_API_KEY
const SIGNAL_LOAD_RECENT_GIFS_STARTED* = "loadRecentGifsStarted"
const SIGNAL_LOAD_RECENT_GIFS_DONE* = "loadRecentGifsDone"
const SIGNAL_LOAD_FAVORITE_GIFS_STARTED* = "loadFavoriteGifsStarted"
const SIGNAL_LOAD_FAVORITE_GIFS_DONE* = "loadFavoriteGifsDone"
type
Service* = ref object of RootObj
GifsArgs* = ref object of Args
gifs*: seq[GifDto]
error*: string
QtObject:
type
Service* = ref object of QObject
threadpool: ThreadPool
settingsService: settings_service.Service
favorites: seq[GifDto]
recents: seq[GifDto]
events: EventEmitter
proc delete*(self: Service) =
discard
proc newService*(settingsService: settings_service.Service): Service =
proc newService*(settingsService: settings_service.Service, events: EventEmitter, threadpool: ThreadPool): Service =
result = Service()
result.QObject.setup
result.settingsService = settingsService
result.events = events
result.threadpool = threadpool
result.favorites = @[]
result.recents = @[]
@ -71,14 +94,62 @@ proc getFavoriteGifs(self: Service) =
except Exception as e:
error "error: ", procName="getFavoriteGifs", errName = e.name, errDesription = e.msg
proc asyncLoadRecentGifs*(self: Service) =
self.events.emit(SIGNAL_LOAD_RECENT_GIFS_STARTED, Args())
try:
let arg = AsyncGetRecentGifsTaskArg(
tptr: cast[ByteAddress](asyncGetRecentGifsTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onAsyncGetRecentGifsDone"
)
self.threadpool.start(arg)
except Exception as e:
error "Error loading recent gifs", msg = e.msg
proc onAsyncGetRecentGifsDone*(self: Service, response: string) {.slot.} =
try:
let rpcResponseObj = response.parseJson
if (rpcResponseObj{"error"}.kind != JNull):
let error = Json.decode($rpcResponseObj["error"], RpcError)
error "error loading recent gifs", msg = error.message
return
self.recents = map(rpcResponseObj{"result"}.getElems(), settingToGifDto)
self.events.emit(SIGNAL_LOAD_RECENT_GIFS_DONE, GifsArgs(gifs: self.recents))
except Exception as e:
let errMsg = e.msg
error "error: ", errMsg
proc asyncLoadFavoriteGifs*(self: Service) =
self.events.emit(SIGNAL_LOAD_FAVORITE_GIFS_STARTED, Args())
try:
let arg = AsyncGetFavoriteGifsTaskArg(
tptr: cast[ByteAddress](asyncGetFavoriteGifsTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onAsyncGetFavoriteGifsDone"
)
self.threadpool.start(arg)
except Exception as e:
error "Error loading favorite gifs", msg = e.msg
proc onAsyncGetFavoriteGifsDone*(self: Service, response: string) {.slot.} =
try:
let rpcResponseObj = response.parseJson
if (rpcResponseObj{"error"}.kind != JNull):
let error = Json.decode($rpcResponseObj["error"], RpcError)
error "error loading favorite gifs", msg = error.message
return
self.favorites = map(rpcResponseObj{"result"}.getElems(), settingToGifDto)
self.events.emit(SIGNAL_LOAD_FAVORITE_GIFS_DONE, GifsArgs(gifs: self.favorites))
except Exception as e:
let errMsg = e.msg
error "error: ", errMsg
proc init*(self: Service) =
# set Tenor API Key
self.setTenorAPIKey()
# get recent and favorite gifs on the database
self.getRecentGifs()
self.getFavoriteGifs()
proc tenorQuery(self: Service, path: string): seq[GifDto] =
try:
let response = backend.fetchGifs(path)

13
src/backend/gifs.nim Normal file
View File

@ -0,0 +1,13 @@
import json, strutils
import core
import response_type
export response_type
proc getRecentGifs*(): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* []
result = callPrivateRPC("gif_getRecentGifs", payload)
proc getFavoriteGifs*(): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* []
result = callPrivateRPC("gif_getFavoriteGifs", payload)