feat(CreateCommunityPopup): add discord import progress panel and discord message handling
This adds the UI plus all necessary models and signal handling to render discord import progress in the desktop application. It also introduces message handling for discord chat message types. Requires status-im/status-go#2826 to function Co-authored with @caybro
This commit is contained in:
parent
90ce349675
commit
bf14b06d55
|
@ -19,6 +19,18 @@ type DiscordCategoriesAndChannelsExtractedSignal* = ref object of Signal
|
||||||
errors*: Table[string, DiscordImportError]
|
errors*: Table[string, DiscordImportError]
|
||||||
errorsCount*: int
|
errorsCount*: int
|
||||||
|
|
||||||
|
type DiscordCommunityImportProgressSignal* = ref object of Signal
|
||||||
|
communityId*: string
|
||||||
|
communityName*: string
|
||||||
|
tasks*: seq[DiscordImportTaskProgress]
|
||||||
|
progress*: float
|
||||||
|
errorsCount*: int
|
||||||
|
warningsCount*: int
|
||||||
|
stopped*: bool
|
||||||
|
|
||||||
|
type DiscordCommunityImportFinishedSignal* = ref object of Signal
|
||||||
|
communityId*: string
|
||||||
|
|
||||||
proc fromEvent*(T: type CommunitySignal, event: JsonNode): CommunitySignal =
|
proc fromEvent*(T: type CommunitySignal, event: JsonNode): CommunitySignal =
|
||||||
result = CommunitySignal()
|
result = CommunitySignal()
|
||||||
result.signalType = SignalType.CommunityFound
|
result.signalType = SignalType.CommunityFound
|
||||||
|
@ -46,6 +58,30 @@ proc fromEvent*(T: type DiscordCategoriesAndChannelsExtractedSignal, event: Json
|
||||||
result.errors[key] = err
|
result.errors[key] = err
|
||||||
result.errorsCount = result.errorsCount+1
|
result.errorsCount = result.errorsCount+1
|
||||||
|
|
||||||
|
proc fromEvent*(T: type DiscordCommunityImportProgressSignal, event: JsonNode): DiscordCommunityImportProgressSignal =
|
||||||
|
result = DiscordCommunityImportProgressSignal()
|
||||||
|
result.signalType = SignalType.DiscordCommunityImportProgress
|
||||||
|
result.tasks = @[]
|
||||||
|
|
||||||
|
if event["event"]["importProgress"].kind == JObject:
|
||||||
|
let importProgressObj = event["event"]["importProgress"]
|
||||||
|
|
||||||
|
result.communityId = importProgressObj{"communityId"}.getStr()
|
||||||
|
result.communityName = importProgressObj{"communityName"}.getStr()
|
||||||
|
result.progress = importProgressObj{"progress"}.getFloat()
|
||||||
|
result.errorsCount = importProgressObj{"errorsCount"}.getInt()
|
||||||
|
result.warningsCount = importProgressObj{"warningsCount"}.getInt()
|
||||||
|
result.stopped = importProgressObj{"stopped"}.getBool()
|
||||||
|
|
||||||
|
if importProgressObj["tasks"].kind == JArray:
|
||||||
|
for task in importProgressObj["tasks"]:
|
||||||
|
result.tasks.add(task.toDiscordImportTaskProgress())
|
||||||
|
|
||||||
|
proc fromEvent*(T: type DiscordCommunityImportFinishedSignal, event: JsonNode): DiscordCommunityImportFinishedSignal =
|
||||||
|
result = DiscordCommunityImportFinishedSignal()
|
||||||
|
result.signalType = SignalType.DiscordCommunityImportFinished
|
||||||
|
result.communityId = event["event"]{"communityId"}.getStr()
|
||||||
|
|
||||||
proc createFromEvent*(T: type HistoryArchivesSignal, event: JsonNode): HistoryArchivesSignal =
|
proc createFromEvent*(T: type HistoryArchivesSignal, event: JsonNode): HistoryArchivesSignal =
|
||||||
result = HistoryArchivesSignal()
|
result = HistoryArchivesSignal()
|
||||||
result.communityId = event["event"]{"communityId"}.getStr()
|
result.communityId = event["event"]{"communityId"}.getStr()
|
||||||
|
@ -83,3 +119,8 @@ proc historyArchivesUnseededFromEvent*(T: type HistoryArchivesSignal, event: Jso
|
||||||
proc historyArchiveDownloadedFromEvent*(T: type HistoryArchivesSignal, event: JsonNode): HistoryArchivesSignal =
|
proc historyArchiveDownloadedFromEvent*(T: type HistoryArchivesSignal, event: JsonNode): HistoryArchivesSignal =
|
||||||
result = HistoryArchivesSignal.createFromEvent(event)
|
result = HistoryArchivesSignal.createFromEvent(event)
|
||||||
result.signalType = SignalType.HistoryArchiveDownloaded
|
result.signalType = SignalType.HistoryArchiveDownloaded
|
||||||
|
|
||||||
|
proc downloadingHistoryArchivesFinishedFromEvent*(T: type HistoryArchivesSignal, event: JsonNode): HistoryArchivesSignal =
|
||||||
|
result = HistoryArchivesSignal()
|
||||||
|
result.communityId = event["event"]{"communityId"}.getStr()
|
||||||
|
result.signalType = SignalType.DownloadingHistoryArchivesFinished
|
||||||
|
|
|
@ -39,9 +39,12 @@ type SignalType* {.pure.} = enum
|
||||||
HistoryArchivesSeeding = "community.historyArchivesSeeding"
|
HistoryArchivesSeeding = "community.historyArchivesSeeding"
|
||||||
HistoryArchivesUnseeded = "community.historyArchivesUnseeded"
|
HistoryArchivesUnseeded = "community.historyArchivesUnseeded"
|
||||||
HistoryArchiveDownloaded = "community.historyArchiveDownloaded"
|
HistoryArchiveDownloaded = "community.historyArchiveDownloaded"
|
||||||
|
DownloadingHistoryArchivesFinished = "community.downloadingHistoryArchivesFinished"
|
||||||
UpdateAvailable = "update.available"
|
UpdateAvailable = "update.available"
|
||||||
DiscordCategoriesAndChannelsExtracted = "community.discordCategoriesAndChannelsExtracted"
|
DiscordCategoriesAndChannelsExtracted = "community.discordCategoriesAndChannelsExtracted"
|
||||||
StatusUpdatesTimedout = "status.updates.timedout"
|
StatusUpdatesTimedout = "status.updates.timedout"
|
||||||
|
DiscordCommunityImportFinished = "community.discordCommunityImportFinished"
|
||||||
|
DiscordCommunityImportProgress = "community.discordCommunityImportProgress"
|
||||||
Unknown
|
Unknown
|
||||||
|
|
||||||
proc event*(self:SignalType):string =
|
proc event*(self:SignalType):string =
|
||||||
|
|
|
@ -93,9 +93,12 @@ QtObject:
|
||||||
of SignalType.HistoryArchivesSeeding: HistoryArchivesSignal.historyArchivesSeedingFromEvent(jsonSignal)
|
of SignalType.HistoryArchivesSeeding: HistoryArchivesSignal.historyArchivesSeedingFromEvent(jsonSignal)
|
||||||
of SignalType.HistoryArchivesUnseeded: HistoryArchivesSignal.historyArchivesUnseededFromEvent(jsonSignal)
|
of SignalType.HistoryArchivesUnseeded: HistoryArchivesSignal.historyArchivesUnseededFromEvent(jsonSignal)
|
||||||
of SignalType.HistoryArchiveDownloaded: HistoryArchivesSignal.historyArchiveDownloadedFromEvent(jsonSignal)
|
of SignalType.HistoryArchiveDownloaded: HistoryArchivesSignal.historyArchiveDownloadedFromEvent(jsonSignal)
|
||||||
|
of SignalType.DownloadingHistoryArchivesFinished: HistoryArchivesSignal.downloadingHistoryArchivesFinishedFromEvent(jsonSignal)
|
||||||
of SignalType.UpdateAvailable: UpdateAvailableSignal.fromEvent(jsonSignal)
|
of SignalType.UpdateAvailable: UpdateAvailableSignal.fromEvent(jsonSignal)
|
||||||
of SignalType.DiscordCategoriesAndChannelsExtracted: DiscordCategoriesAndChannelsExtractedSignal.fromEvent(jsonSignal)
|
of SignalType.DiscordCategoriesAndChannelsExtracted: DiscordCategoriesAndChannelsExtractedSignal.fromEvent(jsonSignal)
|
||||||
of SignalType.StatusUpdatesTimedout: StatusUpdatesTimedoutSignal.fromEvent(jsonSignal)
|
of SignalType.StatusUpdatesTimedout: StatusUpdatesTimedoutSignal.fromEvent(jsonSignal)
|
||||||
|
of SignalType.DiscordCommunityImportFinished: DiscordCommunityImportFinishedSignal.fromEvent(jsonSignal)
|
||||||
|
of SignalType.DiscordCommunityImportProgress: DiscordCommunityImportProgressSignal.fromEvent(jsonSignal)
|
||||||
else: Signal()
|
else: Signal()
|
||||||
|
|
||||||
result.signalType = signalType
|
result.signalType = signalType
|
||||||
|
|
|
@ -96,7 +96,8 @@ proc createMessageItemFromDto(self: Module, message: MessageDto, chatDetails: Ch
|
||||||
newTransactionParametersItem("","","","","","",-1,""),
|
newTransactionParametersItem("","","","","","",-1,""),
|
||||||
message.mentionedUsersPks,
|
message.mentionedUsersPks,
|
||||||
contactDetails.details.trustStatus,
|
contactDetails.details.trustStatus,
|
||||||
contactDetails.details.ensVerified
|
contactDetails.details.ensVerified,
|
||||||
|
message.discordMessage
|
||||||
))
|
))
|
||||||
|
|
||||||
method convertToItems*(
|
method convertToItems*(
|
||||||
|
@ -243,4 +244,4 @@ method getDetails*(self: Module, sectionId: string, chatId: string): string =
|
||||||
jsonObject["cImage"] = %* chatImage
|
jsonObject["cImage"] = %* chatImage
|
||||||
jsonObject["cColor"] = %* c.color
|
jsonObject["cColor"] = %* c.color
|
||||||
jsonObject["cEmoji"] = %* c.emoji
|
jsonObject["cEmoji"] = %* c.emoji
|
||||||
return $jsonObject
|
return $jsonObject
|
||||||
|
|
|
@ -97,7 +97,8 @@ proc createFetchMoreMessagesItem(self: Module): Item =
|
||||||
transactionParameters = newTransactionParametersItem("","","","","","",-1,""),
|
transactionParameters = newTransactionParametersItem("","","","","","",-1,""),
|
||||||
mentionedUsersPks = @[],
|
mentionedUsersPks = @[],
|
||||||
senderTrustStatus = TrustStatus.Unknown,
|
senderTrustStatus = TrustStatus.Unknown,
|
||||||
senderEnsVerified = false
|
senderEnsVerified = false,
|
||||||
|
DiscordMessage()
|
||||||
)
|
)
|
||||||
|
|
||||||
proc createChatIdentifierItem(self: Module): Item =
|
proc createChatIdentifierItem(self: Module): Item =
|
||||||
|
@ -136,7 +137,8 @@ proc createChatIdentifierItem(self: Module): Item =
|
||||||
transactionParameters = newTransactionParametersItem("","","","","","",-1,""),
|
transactionParameters = newTransactionParametersItem("","","","","","",-1,""),
|
||||||
mentionedUsersPks = @[],
|
mentionedUsersPks = @[],
|
||||||
senderTrustStatus = TrustStatus.Unknown,
|
senderTrustStatus = TrustStatus.Unknown,
|
||||||
senderEnsVerified = false
|
senderEnsVerified = false,
|
||||||
|
DiscordMessage()
|
||||||
)
|
)
|
||||||
|
|
||||||
proc checkIfMessageLoadedAndScrollToItIfItIs(self: Module): bool =
|
proc checkIfMessageLoadedAndScrollToItIfItIs(self: Module): bool =
|
||||||
|
@ -185,7 +187,7 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: se
|
||||||
sender.defaultDisplayName,
|
sender.defaultDisplayName,
|
||||||
sender.optionalName,
|
sender.optionalName,
|
||||||
sender.icon,
|
sender.icon,
|
||||||
isCurrentUser,
|
(isCurrentUser and m.contentType.ContentType != ContentType.DiscordMessage),
|
||||||
sender.details.added,
|
sender.details.added,
|
||||||
m.outgoingStatus,
|
m.outgoingStatus,
|
||||||
renderedMessageText,
|
renderedMessageText,
|
||||||
|
@ -209,7 +211,8 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: se
|
||||||
m.transactionParameters.signature),
|
m.transactionParameters.signature),
|
||||||
m.mentionedUsersPks(),
|
m.mentionedUsersPks(),
|
||||||
sender.details.trustStatus,
|
sender.details.trustStatus,
|
||||||
sender.details.ensVerified
|
sender.details.ensVerified,
|
||||||
|
m.discordMessage
|
||||||
)
|
)
|
||||||
|
|
||||||
for r in reactions:
|
for r in reactions:
|
||||||
|
@ -277,7 +280,7 @@ method messageAdded*(self: Module, message: MessageDto) =
|
||||||
sender.defaultDisplayName,
|
sender.defaultDisplayName,
|
||||||
sender.optionalName,
|
sender.optionalName,
|
||||||
sender.icon,
|
sender.icon,
|
||||||
isCurrentUser,
|
(isCurrentUser and message.contentType.ContentType != ContentType.DiscordMessage),
|
||||||
sender.details.added,
|
sender.details.added,
|
||||||
message.outgoingStatus,
|
message.outgoingStatus,
|
||||||
renderedMessageText,
|
renderedMessageText,
|
||||||
|
@ -301,7 +304,8 @@ method messageAdded*(self: Module, message: MessageDto) =
|
||||||
message.transactionParameters.signature),
|
message.transactionParameters.signature),
|
||||||
message.mentionedUsersPks,
|
message.mentionedUsersPks,
|
||||||
sender.details.trustStatus,
|
sender.details.trustStatus,
|
||||||
sender.details.ensVerified
|
sender.details.ensVerified,
|
||||||
|
message.discordMessage
|
||||||
)
|
)
|
||||||
|
|
||||||
self.view.model().insertItemBasedOnTimestamp(item)
|
self.view.model().insertItemBasedOnTimestamp(item)
|
||||||
|
|
|
@ -194,7 +194,8 @@ proc buildPinnedMessageItem(self: Module, messageId: string, actionInitiatedBy:
|
||||||
m.transactionParameters.signature),
|
m.transactionParameters.signature),
|
||||||
m.mentionedUsersPks,
|
m.mentionedUsersPks,
|
||||||
contactDetails.details.trustStatus,
|
contactDetails.details.trustStatus,
|
||||||
contactDetails.details.ensVerified
|
contactDetails.details.ensVerified,
|
||||||
|
m.discordMessage
|
||||||
)
|
)
|
||||||
item.pinned = true
|
item.pinned = true
|
||||||
item.pinnedBy = actionInitiatedBy
|
item.pinnedBy = actionInitiatedBy
|
||||||
|
|
|
@ -70,6 +70,10 @@ proc init*(self: Controller) =
|
||||||
let args = DiscordCategoriesAndChannelsArgs(e)
|
let args = DiscordCategoriesAndChannelsArgs(e)
|
||||||
self.delegate.discordCategoriesAndChannelsExtracted(args.categories, args.channels, args.oldestMessageTimestamp, args.errors, args.errorsCount)
|
self.delegate.discordCategoriesAndChannelsExtracted(args.categories, args.channels, args.oldestMessageTimestamp, args.errors, args.errorsCount)
|
||||||
|
|
||||||
|
self.events.on(SIGNAL_DISCORD_COMMUNITY_IMPORT_PROGRESS) do(e:Args):
|
||||||
|
let args = DiscordImportProgressArgs(e)
|
||||||
|
self.delegate.discordImportProgressUpdated(args.communityId, args.communityName, args.tasks, args.progress, args.errorsCount, args.warningsCount, args.stopped)
|
||||||
|
|
||||||
proc getCommunityTags*(self: Controller): string =
|
proc getCommunityTags*(self: Controller): string =
|
||||||
result = self.communityService.getCommunityTags()
|
result = self.communityService.getCommunityTags()
|
||||||
|
|
||||||
|
@ -113,6 +117,36 @@ proc createCommunity*(
|
||||||
pinMessageAllMembersEnabled,
|
pinMessageAllMembersEnabled,
|
||||||
bannerJsonStr)
|
bannerJsonStr)
|
||||||
|
|
||||||
|
proc requestImportDiscordCommunity*(
|
||||||
|
self: Controller,
|
||||||
|
name: string,
|
||||||
|
description: string,
|
||||||
|
introMessage: string,
|
||||||
|
outroMessage: string,
|
||||||
|
access: int,
|
||||||
|
color: string,
|
||||||
|
tags: string,
|
||||||
|
imageUrl: string,
|
||||||
|
aX: int, aY: int, bX: int, bY: int,
|
||||||
|
historyArchiveSupportEnabled: bool,
|
||||||
|
pinMessageAllMembersEnabled: bool,
|
||||||
|
filesToImport: seq[string],
|
||||||
|
fromTimestamp: int) =
|
||||||
|
self.communityService.requestImportDiscordCommunity(
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
introMessage,
|
||||||
|
outroMessage,
|
||||||
|
access,
|
||||||
|
color,
|
||||||
|
tags,
|
||||||
|
imageUrl,
|
||||||
|
aX, aY, bX, bY,
|
||||||
|
historyArchiveSupportEnabled,
|
||||||
|
pinMessageAllMembersEnabled,
|
||||||
|
filesToImport,
|
||||||
|
fromTimestamp)
|
||||||
|
|
||||||
proc reorderCommunityChat*(
|
proc reorderCommunityChat*(
|
||||||
self: Controller,
|
self: Controller,
|
||||||
communityId: string,
|
communityId: string,
|
||||||
|
@ -167,3 +201,6 @@ proc getStatusForContactWithId*(self: Controller, publicKey: string): StatusUpda
|
||||||
|
|
||||||
proc requestExtractDiscordChannelsAndCategories*(self: Controller, filesToImport: seq[string]) =
|
proc requestExtractDiscordChannelsAndCategories*(self: Controller, filesToImport: seq[string]) =
|
||||||
self.communityService.requestExtractDiscordChannelsAndCategories(filesToImport)
|
self.communityService.requestExtractDiscordChannelsAndCategories(filesToImport)
|
||||||
|
|
||||||
|
proc requestCancelDiscordCommunityImport*(self: Controller, id: string) =
|
||||||
|
self.communityService.requestCancelDiscordCommunityImport(id)
|
||||||
|
|
|
@ -34,6 +34,11 @@ method createCommunity*(self: AccessInterface, name: string, description, introM
|
||||||
historyArchiveSupportEnabled: bool, pinMessageAllMembersEnabled: bool, bannerJsonStr: string) {.base.} =
|
historyArchiveSupportEnabled: bool, pinMessageAllMembersEnabled: bool, bannerJsonStr: string) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method requestImportDiscordCommunity*(self: AccessInterface, name: string, description, introMessage, outroMessage: string, access: int,
|
||||||
|
color: string, tags: string, imagePath: string, aX: int, aY: int, bX: int, bY: int,
|
||||||
|
historyArchiveSupportEnabled: bool, pinMessageAllMembersEnabled: bool, filesToImport: seq[string], fromTimestamp: int) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method deleteCommunityCategory*(self: AccessInterface, communityId: string, categoryId: string) {.base.} =
|
method deleteCommunityCategory*(self: AccessInterface, communityId: string, categoryId: string) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
@ -117,3 +122,9 @@ method requestExtractDiscordChannelsAndCategories*(self: AccessInterface, filesT
|
||||||
|
|
||||||
method discordCategoriesAndChannelsExtracted*(self: AccessInterface, categories: seq[DiscordCategoryDto], channels: seq[DiscordChannelDto], oldestMessageTimestamp: int, errors: Table[string, DiscordImportError], errorsCount: int) {.base.} =
|
method discordCategoriesAndChannelsExtracted*(self: AccessInterface, categories: seq[DiscordCategoryDto], channels: seq[DiscordChannelDto], oldestMessageTimestamp: int, errors: Table[string, DiscordImportError], errorsCount: int) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method discordImportProgressUpdated*(self: AccessInterface, communityId: string, communityName: string, tasks: seq[DiscordImportTaskProgress], progress: float, errorsCount: int, warningsCount: int, stopped: bool) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method requestCancelDiscordCommunityImport*(self: AccessInterface, id: string) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import strformat
|
||||||
|
type
|
||||||
|
DiscordImportErrorItem* = object
|
||||||
|
taskId*: string
|
||||||
|
code*: int
|
||||||
|
message*: string
|
||||||
|
|
||||||
|
proc initDiscordImportErrorItem*(
|
||||||
|
taskId: string,
|
||||||
|
code: int,
|
||||||
|
message: string,
|
||||||
|
): DiscordImportErrorItem =
|
||||||
|
result.taskId = taskId
|
||||||
|
result.code = code
|
||||||
|
result.message = message
|
||||||
|
|
||||||
|
proc `$`*(self: DiscordImportErrorItem): string =
|
||||||
|
result = fmt"""DiscordImportErrorItem(
|
||||||
|
taskId: {self.taskId},
|
||||||
|
code: {self.code},
|
||||||
|
message: {self.message}
|
||||||
|
]"""
|
||||||
|
|
||||||
|
proc getTaskId*(self: DiscordImportErrorItem): string =
|
||||||
|
return self.taskId
|
||||||
|
|
||||||
|
proc getCode*(self: DiscordImportErrorItem): int =
|
||||||
|
return self.code
|
||||||
|
|
||||||
|
proc getMessage*(self: DiscordImportErrorItem): string =
|
||||||
|
return self.message
|
|
@ -0,0 +1,60 @@
|
||||||
|
import NimQml, Tables
|
||||||
|
import discord_import_error_item
|
||||||
|
|
||||||
|
type
|
||||||
|
ModelRole {.pure.} = enum
|
||||||
|
TaskId = UserRole + 1
|
||||||
|
Code
|
||||||
|
Message
|
||||||
|
|
||||||
|
QtObject:
|
||||||
|
type DiscordImportErrorsModel* = ref object of QAbstractListModel
|
||||||
|
items*: seq[DiscordImportErrorItem]
|
||||||
|
|
||||||
|
proc setup(self: DiscordImportErrorsModel) =
|
||||||
|
self.QAbstractListModel.setup
|
||||||
|
|
||||||
|
proc delete(self: DiscordImportErrorsModel) =
|
||||||
|
self.items = @[]
|
||||||
|
self.QAbstractListModel.delete
|
||||||
|
|
||||||
|
proc newDiscordDiscordImportErrorsModel*(): DiscordImportErrorsModel =
|
||||||
|
new(result, delete)
|
||||||
|
result.setup
|
||||||
|
|
||||||
|
method roleNames(self: DiscordImportErrorsModel): Table[int, string] =
|
||||||
|
{
|
||||||
|
ModelRole.TaskId.int:"taskId",
|
||||||
|
ModelRole.Code.int:"code",
|
||||||
|
ModelRole.Message.int:"message",
|
||||||
|
}.toTable
|
||||||
|
|
||||||
|
method rowCount(self: DiscordImportErrorsModel, index: QModelIndex = nil): int =
|
||||||
|
return self.items.len
|
||||||
|
|
||||||
|
method data(self: DiscordImportErrorsModel, index: QModelIndex, role: int): QVariant =
|
||||||
|
if not index.isValid:
|
||||||
|
return
|
||||||
|
if index.row < 0 or index.row >= self.items.len:
|
||||||
|
return
|
||||||
|
let item = self.items[index.row]
|
||||||
|
let enumRole = role.ModelRole
|
||||||
|
case enumRole:
|
||||||
|
of ModelRole.TaskId:
|
||||||
|
result = newQVariant(item.getTaskId())
|
||||||
|
of ModelRole.Code:
|
||||||
|
result = newQVariant(item.getCode())
|
||||||
|
of ModelRole.Message:
|
||||||
|
result = newQVariant(item.getMessage())
|
||||||
|
|
||||||
|
proc setItems*(self: DiscordImportErrorsModel, items: seq[DiscordImportErrorItem]) =
|
||||||
|
self.beginResetModel()
|
||||||
|
self.items = items
|
||||||
|
self.endResetModel()
|
||||||
|
|
||||||
|
proc addItem*(self: DiscordImportErrorsModel, item: DiscordImportErrorItem) =
|
||||||
|
let parentModelIndex = newQModelIndex()
|
||||||
|
defer: parentModelIndex.delete
|
||||||
|
self.beginInsertRows(parentModelIndex, self.items.len, self.items.len)
|
||||||
|
self.items.add(item)
|
||||||
|
self.endInsertRows()
|
|
@ -0,0 +1,45 @@
|
||||||
|
import strformat
|
||||||
|
type
|
||||||
|
DiscordImportProgressItem* = object
|
||||||
|
communityId*: string
|
||||||
|
progress*: float
|
||||||
|
errorsCount*: int
|
||||||
|
warningsCount*: int
|
||||||
|
stopped*: bool
|
||||||
|
|
||||||
|
proc initDiscordImportProgressItem*(
|
||||||
|
communityId: string,
|
||||||
|
progress: float,
|
||||||
|
errorsCount: int,
|
||||||
|
warningsCount: int,
|
||||||
|
stopped: bool,
|
||||||
|
): DiscordImportProgressItem =
|
||||||
|
result.communityId = communityId
|
||||||
|
result.progress = progress
|
||||||
|
result.errorsCount = errorsCount
|
||||||
|
result.warningsCount = warningsCount
|
||||||
|
result.stopped = stopped
|
||||||
|
|
||||||
|
proc `$`*(self: DiscordImportProgressItem): string =
|
||||||
|
result = fmt"""DiscordImportProgressItem(
|
||||||
|
communityId: {self.communityId},
|
||||||
|
progress: {self.progress},
|
||||||
|
errorsCount: {self.errorsCount},
|
||||||
|
warningsCount: {self.warningsCount},
|
||||||
|
stopped: {self.stopped}
|
||||||
|
]"""
|
||||||
|
|
||||||
|
proc getCommunitId*(self: DiscordImportProgressItem): string =
|
||||||
|
return self.communityId
|
||||||
|
|
||||||
|
proc getProgress*(self: DiscordImportProgressItem): float =
|
||||||
|
return self.progress
|
||||||
|
|
||||||
|
proc getErrorsCount*(self: DiscordImportProgressItem): int =
|
||||||
|
return self.errorsCount
|
||||||
|
|
||||||
|
proc getWarningsCount*(self: DiscordImportProgressItem): int =
|
||||||
|
return self.warningsCount
|
||||||
|
|
||||||
|
proc getStopped*(self: DiscordImportProgressItem): bool =
|
||||||
|
return self.stopped
|
|
@ -0,0 +1,68 @@
|
||||||
|
import strformat
|
||||||
|
import discord_import_errors_model, discord_import_error_item
|
||||||
|
import ../../../../../app_service/service/community/dto/community
|
||||||
|
|
||||||
|
|
||||||
|
const MAX_VISIBLE_ERROR_ITEMS* = 3
|
||||||
|
|
||||||
|
type
|
||||||
|
DiscordImportTaskItem* = object
|
||||||
|
`type`*: string
|
||||||
|
progress*: float
|
||||||
|
state*: string
|
||||||
|
errors*: DiscordImportErrorsModel
|
||||||
|
stopped*: bool
|
||||||
|
errorsCount*: int
|
||||||
|
warningsCount*: int
|
||||||
|
|
||||||
|
proc `$`*(self: DiscordImportTaskItem): string =
|
||||||
|
result = fmt"""DiscordImportTaskItem(
|
||||||
|
type: {self.type},
|
||||||
|
state: {self.state},
|
||||||
|
progress: {self.progress},
|
||||||
|
stopped: {self.stopped},
|
||||||
|
]"""
|
||||||
|
|
||||||
|
proc initDiscordImportTaskItem*(
|
||||||
|
`type`: string,
|
||||||
|
progress: float,
|
||||||
|
state: string,
|
||||||
|
errors: seq[DiscordImportError],
|
||||||
|
stopped: bool,
|
||||||
|
errorsCount: int,
|
||||||
|
warningsCount: int
|
||||||
|
): DiscordImportTaskItem =
|
||||||
|
result.type = type
|
||||||
|
result.progress = progress
|
||||||
|
result.state = state
|
||||||
|
result.errors = newDiscordDiscordImportErrorsModel()
|
||||||
|
result.stopped = stopped
|
||||||
|
result.errorsCount = errorsCount
|
||||||
|
result.warningsCount = warningsCount
|
||||||
|
|
||||||
|
# We only show the first 3 errors per task, then we add another
|
||||||
|
# "#n more issues" item in the UI
|
||||||
|
for i, error in errors:
|
||||||
|
if i < MAX_VISIBLE_ERROR_ITEMS:
|
||||||
|
result.errors.addItem(initDiscordImportErrorItem(`type`, error.code, error.message))
|
||||||
|
|
||||||
|
proc getType*(self: DiscordImportTaskItem): string =
|
||||||
|
return self.type
|
||||||
|
|
||||||
|
proc getProgress*(self: DiscordImportTaskItem): float =
|
||||||
|
return self.progress
|
||||||
|
|
||||||
|
proc getState*(self: DiscordImportTaskItem): string =
|
||||||
|
return self.state
|
||||||
|
|
||||||
|
proc getErrors*(self: DiscordImportTaskItem): DiscordImportErrorsModel =
|
||||||
|
return self.errors
|
||||||
|
|
||||||
|
proc getStopped*(self: DiscordImportTaskItem): bool =
|
||||||
|
return self.stopped
|
||||||
|
|
||||||
|
proc getErrorsCount*(self: DiscordImportTaskItem): int =
|
||||||
|
return self.errorsCount
|
||||||
|
|
||||||
|
proc getWarningsCount*(self: DiscordImportTaskItem): int =
|
||||||
|
return self.warningsCount
|
|
@ -0,0 +1,125 @@
|
||||||
|
import NimQml, Tables
|
||||||
|
import discord_import_error_item, discord_import_errors_model
|
||||||
|
import discord_import_task_item as taskItem
|
||||||
|
import ../../../../../app_service/service/community/dto/community
|
||||||
|
|
||||||
|
type
|
||||||
|
ModelRole {.pure.} = enum
|
||||||
|
Type = UserRole + 1
|
||||||
|
Progress
|
||||||
|
State
|
||||||
|
Errors
|
||||||
|
Stopped
|
||||||
|
ErrorsCount
|
||||||
|
WarningsCount
|
||||||
|
|
||||||
|
QtObject:
|
||||||
|
type DiscordImportTasksModel* = ref object of QAbstractListModel
|
||||||
|
items*: seq[DiscordImportTaskItem]
|
||||||
|
|
||||||
|
proc setup(self: DiscordImportTasksModel) =
|
||||||
|
self.QAbstractListModel.setup
|
||||||
|
|
||||||
|
proc delete(self: DiscordImportTasksModel) =
|
||||||
|
self.items = @[]
|
||||||
|
self.QAbstractListModel.delete
|
||||||
|
|
||||||
|
proc newDiscordDiscordImportTasksModel*(): DiscordImportTasksmodel =
|
||||||
|
new(result, delete)
|
||||||
|
result.setup
|
||||||
|
|
||||||
|
method roleNames(self: DiscordImportTasksModel): Table[int, string] =
|
||||||
|
{
|
||||||
|
ModelRole.Type.int:"type",
|
||||||
|
ModelRole.Progress.int:"progress",
|
||||||
|
ModelRole.State.int:"state",
|
||||||
|
ModelRole.Errors.int:"errors",
|
||||||
|
ModelRole.Stopped.int:"stopped",
|
||||||
|
ModelRole.ErrorsCount.int:"errorsCount",
|
||||||
|
ModelRole.WarningsCount.int:"warningsCount",
|
||||||
|
}.toTable
|
||||||
|
|
||||||
|
method data(self: DiscordImportTasksModel, index: QModelIndex, role: int): QVariant =
|
||||||
|
if not index.isValid:
|
||||||
|
return
|
||||||
|
if index.row < 0 or index.row >= self.items.len:
|
||||||
|
return
|
||||||
|
let item = self.items[index.row]
|
||||||
|
let enumRole = role.ModelRole
|
||||||
|
case enumRole:
|
||||||
|
of ModelRole.Type:
|
||||||
|
result = newQVariant(item.getType())
|
||||||
|
of ModelRole.Progress:
|
||||||
|
result = newQVariant(item.getProgress())
|
||||||
|
of ModelRole.State:
|
||||||
|
result = newQVariant(item.getState())
|
||||||
|
of ModelRole.Errors:
|
||||||
|
result = newQVariant(item.getErrors())
|
||||||
|
of ModelRole.Stopped:
|
||||||
|
result = newQVariant(item.getStopped())
|
||||||
|
of ModelRole.ErrorsCount:
|
||||||
|
result = newQVariant(item.getErrorsCount())
|
||||||
|
of ModelRole.WarningsCount:
|
||||||
|
result = newQVariant(item.getWarningsCount())
|
||||||
|
|
||||||
|
method rowCount(self: DiscordImportTasksModel, index: QModelIndex = nil): int =
|
||||||
|
return self.items.len
|
||||||
|
|
||||||
|
proc setItems*(self: DiscordImportTasksModel, items: seq[DiscordImportTaskItem]) =
|
||||||
|
self.beginResetModel()
|
||||||
|
self.items = items
|
||||||
|
self.endResetModel()
|
||||||
|
|
||||||
|
proc hasItemByType*(self: DiscordImportTasksModel, `type`: string): bool =
|
||||||
|
for i, item in self.items:
|
||||||
|
if self.items[i].`type` == `type`:
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
proc addItem*(self: DiscordImportTasksModel, item: DiscordImportTaskItem) =
|
||||||
|
let parentModelIndex = newQModelIndex()
|
||||||
|
defer: parentModelIndex.delete
|
||||||
|
self.beginInsertRows(parentModelIndex, self.items.len, self.items.len)
|
||||||
|
self.items.add(item)
|
||||||
|
self.endInsertRows()
|
||||||
|
|
||||||
|
proc findIndexByType(self: DiscordImportTasksModel, `type`: string): int =
|
||||||
|
for i in 0 ..< self.items.len:
|
||||||
|
if(self.items[i].`type` == `type`):
|
||||||
|
return i
|
||||||
|
return -1
|
||||||
|
|
||||||
|
proc updateItem*(self: DiscordImportTasksModel, item: DiscordImportTaskProgress) =
|
||||||
|
let idx = self.findIndexByType(item.`type`)
|
||||||
|
if idx > -1:
|
||||||
|
let index = self.createIndex(idx, 0, nil)
|
||||||
|
let errorsAndWarningsCount = self.items[idx].warningsCount + self.items[idx].errorsCount
|
||||||
|
self.items[idx].progress = item.progress
|
||||||
|
self.items[idx].state = item.state
|
||||||
|
self.items[idx].stopped = item.stopped
|
||||||
|
self.items[idx].errorsCount = item.errorsCount
|
||||||
|
self.items[idx].warningsCount = item.warningsCount
|
||||||
|
|
||||||
|
let errorItemsCount = self.items[idx].errors.items.len
|
||||||
|
|
||||||
|
# We only show the first 3 errors per task, then we add another
|
||||||
|
# "#n more issues" item in the UI
|
||||||
|
for i, error in item.errors:
|
||||||
|
if errorItemsCount + i < taskItem.MAX_VISIBLE_ERROR_ITEMS:
|
||||||
|
let errorItem = initDiscordImportErrorItem(item.`type`, error.code, error.message)
|
||||||
|
self.items[idx].errors.addItem(errorItem)
|
||||||
|
|
||||||
|
self.dataChanged(index, index, @[
|
||||||
|
ModelRole.Progress.int,
|
||||||
|
ModelRole.State.int,
|
||||||
|
ModelRole.Errors.int,
|
||||||
|
ModelRole.Stopped.int,
|
||||||
|
ModelRole.ErrorsCount.int,
|
||||||
|
ModelRole.WarningsCount.int
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
proc clearItems*(self: DiscordImportTasksModel) =
|
||||||
|
self.beginResetModel()
|
||||||
|
self.items = @[]
|
||||||
|
self.endResetModel()
|
|
@ -10,6 +10,10 @@ import ./models/discord_categories_model
|
||||||
import ./models/discord_channel_item
|
import ./models/discord_channel_item
|
||||||
import ./models/discord_channels_model
|
import ./models/discord_channels_model
|
||||||
import ./models/discord_file_list_model
|
import ./models/discord_file_list_model
|
||||||
|
import ./models/discord_import_task_item
|
||||||
|
import ./models/discord_import_tasks_model
|
||||||
|
import ./models/discord_import_error_item
|
||||||
|
import ./models/discord_import_errors_model
|
||||||
import ../../shared_models/section_item
|
import ../../shared_models/section_item
|
||||||
import ../../shared_models/[member_item, member_model, section_model]
|
import ../../shared_models/[member_item, member_model, section_model]
|
||||||
import ../../../global/global_singleton
|
import ../../../global/global_singleton
|
||||||
|
@ -288,3 +292,44 @@ method onImportCommunityErrorOccured*(self: Module, error: string) =
|
||||||
method requestExtractDiscordChannelsAndCategories*(self: Module, filesToImport: seq[string]) =
|
method requestExtractDiscordChannelsAndCategories*(self: Module, filesToImport: seq[string]) =
|
||||||
self.view.setDiscordDataExtractionInProgress(true)
|
self.view.setDiscordDataExtractionInProgress(true)
|
||||||
self.controller.requestExtractDiscordChannelsAndCategories(filesToImport)
|
self.controller.requestExtractDiscordChannelsAndCategories(filesToImport)
|
||||||
|
|
||||||
|
method requestImportDiscordCommunity*(self: Module, name: string, description, introMessage, outroMessage: string, access: int,
|
||||||
|
color: string, tags: string, imagePath: string, aX: int, aY: int, bX: int, bY: int,
|
||||||
|
historyArchiveSupportEnabled: bool, pinMessageAllMembersEnabled: bool, filesToImport: seq[string], fromTimestamp: int) =
|
||||||
|
self.controller.requestImportDiscordCommunity(name, description, introMessage, outroMessage, access, color, tags, imagePath, aX, aY, bX, bY, historyArchiveSupportEnabled, pinMessageAllMembersEnabled, filesToImport, fromTimestamp)
|
||||||
|
|
||||||
|
method getDiscordImportTaskItem(self: Module, t: DiscordImportTaskProgress): DiscordImportTaskItem =
|
||||||
|
return initDiscordImportTaskItem(
|
||||||
|
t.`type`,
|
||||||
|
t.progress,
|
||||||
|
t.state,
|
||||||
|
t.errors,
|
||||||
|
t.stopped,
|
||||||
|
t.errorsCount,
|
||||||
|
t.warningsCount)
|
||||||
|
|
||||||
|
method discordImportProgressUpdated*(self: Module, communityId: string, communityName: string, tasks: seq[DiscordImportTaskProgress], progress: float, errorsCount: int, warningsCount: int, stopped: bool) =
|
||||||
|
|
||||||
|
var taskItems: seq[DiscordImportTaskItem] = @[]
|
||||||
|
|
||||||
|
for task in tasks:
|
||||||
|
if not self.view.discordImportTasksModel().hasItemByType(task.`type`):
|
||||||
|
self.view.discordImportTasksModel().addItem(self.getDiscordImportTaskItem(task))
|
||||||
|
else:
|
||||||
|
self.view.discordImportTasksModel().updateItem(task)
|
||||||
|
|
||||||
|
self.view.setDiscordImportCommunityId(communityId)
|
||||||
|
self.view.setDiscordImportCommunityName(communityName)
|
||||||
|
self.view.setDiscordImportErrorsCount(errorsCount)
|
||||||
|
self.view.setDiscordImportWarningsCount(warningsCount)
|
||||||
|
# For some reason, exposing the global `progress` as QtProperty[float]`
|
||||||
|
# doesn't translate well into QML.
|
||||||
|
# That's why we pass it as integer instead.
|
||||||
|
self.view.setDiscordImportProgress((progress*100).int)
|
||||||
|
self.view.setDiscordImportProgressStopped(stopped)
|
||||||
|
if stopped or progress.int >= 1:
|
||||||
|
self.view.setDiscordImportInProgress(false)
|
||||||
|
|
||||||
|
method requestCancelDiscordCommunityImport*(self: Module, id: string) =
|
||||||
|
self.controller.requestCancelDiscordCommunityImport(id)
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ import ./models/discord_categories_model
|
||||||
import ./models/discord_category_item
|
import ./models/discord_category_item
|
||||||
import ./models/discord_channels_model
|
import ./models/discord_channels_model
|
||||||
import ./models/discord_channel_item
|
import ./models/discord_channel_item
|
||||||
|
import ./models/discord_import_tasks_model
|
||||||
|
import ./models/discord_import_errors_model
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type
|
type
|
||||||
|
@ -32,7 +34,15 @@ QtObject:
|
||||||
discordOldestMessageTimestamp: int
|
discordOldestMessageTimestamp: int
|
||||||
discordImportErrorsCount: int
|
discordImportErrorsCount: int
|
||||||
discordImportWarningsCount: int
|
discordImportWarningsCount: int
|
||||||
|
discordImportProgress: int
|
||||||
|
discordImportInProgress: bool
|
||||||
|
discordImportCancelled: bool
|
||||||
|
discordImportProgressStopped: bool
|
||||||
|
discordImportTasksModel: DiscordImportTasksModel
|
||||||
|
discordImportTasksModelVariant: QVariant
|
||||||
discordDataExtractionInProgress: bool
|
discordDataExtractionInProgress: bool
|
||||||
|
discordImportCommunityId: string
|
||||||
|
discordImportCommunityName: string
|
||||||
|
|
||||||
proc delete*(self: View) =
|
proc delete*(self: View) =
|
||||||
self.model.delete
|
self.model.delete
|
||||||
|
@ -46,6 +56,8 @@ QtObject:
|
||||||
self.discordCategoriesModelVariant.delete
|
self.discordCategoriesModelVariant.delete
|
||||||
self.discordChannelsModel.delete
|
self.discordChannelsModel.delete
|
||||||
self.discordChannelsModelVariant.delete
|
self.discordChannelsModelVariant.delete
|
||||||
|
self.discordImportTasksModel.delete
|
||||||
|
self.discordImportTasksModelVariant.delete
|
||||||
self.QObject.delete
|
self.QObject.delete
|
||||||
|
|
||||||
proc newView*(delegate: io_interface.AccessInterface): View =
|
proc newView*(delegate: io_interface.AccessInterface): View =
|
||||||
|
@ -67,6 +79,12 @@ QtObject:
|
||||||
result.discordDataExtractionInProgress = false
|
result.discordDataExtractionInProgress = false
|
||||||
result.discordImportWarningsCount = 0
|
result.discordImportWarningsCount = 0
|
||||||
result.discordImportErrorsCount = 0
|
result.discordImportErrorsCount = 0
|
||||||
|
result.discordImportProgress = 0
|
||||||
|
result.discordImportInProgress = false
|
||||||
|
result.discordImportCancelled = false
|
||||||
|
result.discordImportProgressStopped = false
|
||||||
|
result.discordImportTasksModel = newDiscordDiscordImportTasksModel()
|
||||||
|
result.discordImportTasksModelVariant = newQVariant(result.discordImportTasksModel)
|
||||||
result.observedItem = newActiveSection()
|
result.observedItem = newActiveSection()
|
||||||
|
|
||||||
proc load*(self: View) =
|
proc load*(self: View) =
|
||||||
|
@ -119,6 +137,62 @@ QtObject:
|
||||||
read = getDiscordImportErrorsCount
|
read = getDiscordImportErrorsCount
|
||||||
notify = discordImportErrorsCountChanged
|
notify = discordImportErrorsCountChanged
|
||||||
|
|
||||||
|
proc discordImportProgressChanged*(self: View) {.signal.}
|
||||||
|
|
||||||
|
proc setDiscordImportProgress*(self: View, value: int) {.slot.} =
|
||||||
|
if (self.discordImportProgress == value): return
|
||||||
|
self.discordImportProgress = value
|
||||||
|
self.discordImportProgressChanged()
|
||||||
|
|
||||||
|
proc getDiscordImportProgress*(self: View): int {.slot.} =
|
||||||
|
return self.discordImportProgress
|
||||||
|
|
||||||
|
QtProperty[int] discordImportProgress:
|
||||||
|
read = getDiscordImportProgress
|
||||||
|
notify = discordImportProgressChanged
|
||||||
|
|
||||||
|
proc discordImportInProgressChanged*(self: View) {.signal.}
|
||||||
|
|
||||||
|
proc setDiscordImportInProgress*(self: View, value: bool) {.slot.} =
|
||||||
|
if (self.discordImportInProgress == value): return
|
||||||
|
self.discordImportInProgress = value
|
||||||
|
self.discordImportInProgressChanged()
|
||||||
|
|
||||||
|
proc getDiscordImportInProgress*(self: View): bool {.slot.} =
|
||||||
|
return self.discordImportInProgress
|
||||||
|
|
||||||
|
QtProperty[bool] discordImportInProgress:
|
||||||
|
read = getDiscordImportInProgress
|
||||||
|
notify = discordImportInProgressChanged
|
||||||
|
|
||||||
|
proc discordImportCancelledChanged*(self: View) {.signal.}
|
||||||
|
|
||||||
|
proc setDiscordImportCancelled*(self: View, value: bool) {.slot.} =
|
||||||
|
if (self.discordImportCancelled == value): return
|
||||||
|
self.discordImportCancelled = value
|
||||||
|
self.discordImportCancelledChanged()
|
||||||
|
|
||||||
|
proc getDiscordImportCancelled*(self: View): bool {.slot.} =
|
||||||
|
return self.discordImportCancelled
|
||||||
|
|
||||||
|
QtProperty[bool] discordImportCancelled:
|
||||||
|
read = getDiscordImportCancelled
|
||||||
|
notify = discordImportCancelledChanged
|
||||||
|
|
||||||
|
proc discordImportProgressStoppedChanged*(self: View) {.signal.}
|
||||||
|
|
||||||
|
proc setDiscordImportProgressStopped*(self: View, stopped: bool) {.slot.} =
|
||||||
|
if (self.discordImportProgressStopped == stopped): return
|
||||||
|
self.discordImportProgressStopped = stopped
|
||||||
|
self.discordImportProgressStoppedChanged()
|
||||||
|
|
||||||
|
proc getDiscordImportProgressStopped*(self: View): bool {.slot.} =
|
||||||
|
return self.discordImportProgressStopped
|
||||||
|
|
||||||
|
QtProperty[int] discordImportProgressStopped:
|
||||||
|
read = getDiscordImportProgressStopped
|
||||||
|
notify = discordImportProgressStoppedChanged
|
||||||
|
|
||||||
proc addItem*(self: View, item: SectionItem) =
|
proc addItem*(self: View, item: SectionItem) =
|
||||||
self.model.addItem(item)
|
self.model.addItem(item)
|
||||||
self.communityAdded(item.id)
|
self.communityAdded(item.id)
|
||||||
|
@ -174,6 +248,15 @@ QtObject:
|
||||||
QtProperty[QVariant] discordChannels:
|
QtProperty[QVariant] discordChannels:
|
||||||
read = getDiscordChannelsModel
|
read = getDiscordChannelsModel
|
||||||
|
|
||||||
|
proc discordImportTasksModel*(self: View): DiscordImportTasksModel =
|
||||||
|
result = self.discordImportTasksModel
|
||||||
|
|
||||||
|
proc getDiscordImportTasksModel(self: View): QVariant {.slot.} =
|
||||||
|
return self.discordImportTasksModelVariant
|
||||||
|
|
||||||
|
QtProperty[QVariant] discordImportTasks:
|
||||||
|
read = getDiscordImportTasksModel
|
||||||
|
|
||||||
proc observedItemChanged*(self:View) {.signal.}
|
proc observedItemChanged*(self:View) {.signal.}
|
||||||
|
|
||||||
proc getObservedItem(self: View): QVariant {.slot.} =
|
proc getObservedItem(self: View): QVariant {.slot.} =
|
||||||
|
@ -204,6 +287,34 @@ QtObject:
|
||||||
read = getDiscordDataExtractionInProgress
|
read = getDiscordDataExtractionInProgress
|
||||||
notify = discordDataExtractionInProgressChanged
|
notify = discordDataExtractionInProgressChanged
|
||||||
|
|
||||||
|
proc discordImportCommunityIdChanged*(self: View) {.signal.}
|
||||||
|
|
||||||
|
proc getDiscordImportCommunityId(self: View): string {.slot.} =
|
||||||
|
return self.discordImportCommunityId
|
||||||
|
|
||||||
|
proc setDiscordImportCommunityId*(self: View, id: string) {.slot.} =
|
||||||
|
if (self.discordImportCommunityId == id): return
|
||||||
|
self.discordImportCommunityId = id
|
||||||
|
self.discordImportCommunityIdChanged()
|
||||||
|
|
||||||
|
QtProperty[string] discordImportCommunityId:
|
||||||
|
read = getDiscordImportCommunityId
|
||||||
|
notify = discordImportCommunityIdChanged
|
||||||
|
|
||||||
|
proc discordImportCommunityNameChanged*(self: View) {.signal.}
|
||||||
|
|
||||||
|
proc getDiscordImportCommunityName(self: View): string {.slot.} =
|
||||||
|
return self.discordImportCommunityName
|
||||||
|
|
||||||
|
proc setDiscordImportCommunityName*(self: View, name: string) {.slot.} =
|
||||||
|
if (self.discordImportCommunityName == name): return
|
||||||
|
self.discordImportCommunityName = name
|
||||||
|
self.discordImportCommunityNameChanged()
|
||||||
|
|
||||||
|
QtProperty[string] discordImportCommunityName:
|
||||||
|
read = getDiscordImportCommunityName
|
||||||
|
notify = discordImportCommunityNameChanged
|
||||||
|
|
||||||
proc joinCommunity*(self: View, communityId: string, ensName: string) {.slot.} =
|
proc joinCommunity*(self: View, communityId: string, ensName: string) {.slot.} =
|
||||||
# Users always have to request to join a community but might
|
# Users always have to request to join a community but might
|
||||||
# get automatically accepted.
|
# get automatically accepted.
|
||||||
|
@ -219,6 +330,48 @@ QtObject:
|
||||||
self.delegate.createCommunity(name, description, introMessage, outroMessage, access, color, tags,
|
self.delegate.createCommunity(name, description, introMessage, outroMessage, access, color, tags,
|
||||||
imagePath, aX, aY, bX, bY, historyArchiveSupportEnabled, pinMessageAllMembersEnabled, bannerJsonStr)
|
imagePath, aX, aY, bX, bY, historyArchiveSupportEnabled, pinMessageAllMembersEnabled, bannerJsonStr)
|
||||||
|
|
||||||
|
proc clearFileList*(self: View) {.slot.} =
|
||||||
|
self.discordFileListModel.clearItems()
|
||||||
|
self.setDiscordImportErrorsCount(0)
|
||||||
|
self.setDiscordImportWarningsCount(0)
|
||||||
|
|
||||||
|
proc clearDiscordCategoriesAndChannels*(self: View) {.slot.} =
|
||||||
|
self.discordCategoriesModel.clearItems()
|
||||||
|
self.discordChannelsModel.clearItems()
|
||||||
|
|
||||||
|
proc resetDiscordImport*(self: View, cancelled: bool) {.slot.} =
|
||||||
|
self.clearFileList()
|
||||||
|
self.clearDiscordCategoriesAndChannels()
|
||||||
|
self.discordImportTasksModel.clearItems()
|
||||||
|
self.setDiscordImportProgress(0)
|
||||||
|
self.setDiscordImportProgressStopped(false)
|
||||||
|
self.setDiscordImportErrorsCount(0)
|
||||||
|
self.setDiscordImportWarningsCount(0)
|
||||||
|
self.setDiscordImportCommunityId("")
|
||||||
|
self.setDiscordImportCommunityName("")
|
||||||
|
self.setDiscordImportInProgress(false)
|
||||||
|
self.setDiscordImportCancelled(cancelled)
|
||||||
|
|
||||||
|
|
||||||
|
proc requestImportDiscordCommunity*(self: View, name: string,
|
||||||
|
description: string, introMessage: string, outroMessage: string,
|
||||||
|
access: int, color: string, tags: string,
|
||||||
|
imagePath: string,
|
||||||
|
aX: int, aY: int, bX: int, bY: int,
|
||||||
|
historyArchiveSupportEnabled: bool,
|
||||||
|
pinMessageAllMembersEnabled: bool,
|
||||||
|
fromTimestamp: int) {.slot.} =
|
||||||
|
let selectedItems = self.discordChannelsModel.getSelectedItems()
|
||||||
|
var filesToImport: seq[string] = @[]
|
||||||
|
|
||||||
|
for i in 0 ..< selectedItems.len:
|
||||||
|
filesToImport.add(selectedItems[i].getFilePath())
|
||||||
|
|
||||||
|
self.resetDiscordImport(false)
|
||||||
|
self.setDiscordImportInProgress(true)
|
||||||
|
self.delegate.requestImportDiscordCommunity(name, description, introMessage, outroMessage, access, color, tags,
|
||||||
|
imagePath, aX, aY, bX, bY, historyArchiveSupportEnabled, pinMessageAllMembersEnabled, filesToImport, fromTimestamp)
|
||||||
|
|
||||||
proc deleteCommunityCategory*(self: View, communityId: string, categoryId: string): string {.slot.} =
|
proc deleteCommunityCategory*(self: View, communityId: string, categoryId: string): string {.slot.} =
|
||||||
self.delegate.deleteCommunityCategory(communityId, categoryId)
|
self.delegate.deleteCommunityCategory(communityId, categoryId)
|
||||||
|
|
||||||
|
@ -288,15 +441,14 @@ QtObject:
|
||||||
self.setDiscordImportErrorsCount(0)
|
self.setDiscordImportErrorsCount(0)
|
||||||
self.setDiscordImportWarningsCount(0)
|
self.setDiscordImportWarningsCount(0)
|
||||||
|
|
||||||
proc clearFileList*(self: View) {.slot.} =
|
|
||||||
self.discordFileListModel.clearItems()
|
|
||||||
self.setDiscordImportErrorsCount(0)
|
|
||||||
self.setDiscordImportWarningsCount(0)
|
|
||||||
|
|
||||||
proc requestExtractDiscordChannelsAndCategories*(self: View) {.slot.} =
|
proc requestExtractDiscordChannelsAndCategories*(self: View) {.slot.} =
|
||||||
let filePaths = self.discordFileListModel.getSelectedFilePaths()
|
let filePaths = self.discordFileListModel.getSelectedFilePaths()
|
||||||
self.delegate.requestExtractDiscordChannelsAndCategories(filePaths)
|
self.delegate.requestExtractDiscordChannelsAndCategories(filePaths)
|
||||||
|
|
||||||
|
proc requestCancelDiscordCommunityImport*(self: View, id: string) {.slot.} =
|
||||||
|
self.delegate.requestCancelDiscordCommunityImport(id)
|
||||||
|
self.resetDiscordImport(true)
|
||||||
|
|
||||||
proc toggleDiscordCategory*(self: View, id: string, selected: bool) {.slot.} =
|
proc toggleDiscordCategory*(self: View, id: string, selected: bool) {.slot.} =
|
||||||
if selected:
|
if selected:
|
||||||
self.discordCategoriesModel.selectItem(id)
|
self.discordCategoriesModel.selectItem(id)
|
||||||
|
@ -316,6 +468,3 @@ QtObject:
|
||||||
if self.discordChannelsModel.allChannelsByCategoryUnselected(item.getCategoryId()):
|
if self.discordChannelsModel.allChannelsByCategoryUnselected(item.getCategoryId()):
|
||||||
self.discordCategoriesModel.unselectItem(item.getCategoryId())
|
self.discordCategoriesModel.unselectItem(item.getCategoryId())
|
||||||
|
|
||||||
proc clearDiscordCategoriesAndChannels*(self: View) {.slot.} =
|
|
||||||
self.discordCategoriesModel.clearItems()
|
|
||||||
self.discordChannelsModel.clearItems()
|
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
import Nimqml, json, strformat
|
||||||
|
|
||||||
|
import ../../../app_service/service/message/dto/message
|
||||||
|
|
||||||
|
QtObject:
|
||||||
|
type
|
||||||
|
DiscordMessageItem* = ref object of QObject
|
||||||
|
id: string
|
||||||
|
timestamp: string
|
||||||
|
timestampEdited: string
|
||||||
|
content: string
|
||||||
|
author: DiscordMessageAuthor
|
||||||
|
|
||||||
|
proc setup(self: DiscordMessageItem) =
|
||||||
|
self.QObject.setup
|
||||||
|
|
||||||
|
proc delete*(self: DiscordMessageItem) =
|
||||||
|
self.QObject.delete
|
||||||
|
|
||||||
|
proc newDiscordMessageItem*(
|
||||||
|
id: string,
|
||||||
|
timestamp: string,
|
||||||
|
timestampEdited: string,
|
||||||
|
content: string,
|
||||||
|
author: DiscordMessageAuthor
|
||||||
|
): DiscordMessageItem =
|
||||||
|
new(result, delete)
|
||||||
|
result.setup
|
||||||
|
result.id = id
|
||||||
|
result.timestamp = timestamp
|
||||||
|
result.timestampEdited = timestampEdited
|
||||||
|
result.content = content
|
||||||
|
result.author = author
|
||||||
|
|
||||||
|
proc `$`*(self: DiscordMessageItem): string =
|
||||||
|
result = fmt"""DiscordMessageItem(
|
||||||
|
id: {$self.id},
|
||||||
|
timestamp: {$self.timestamp},
|
||||||
|
timestampEdited: {$self.timestampEdited},
|
||||||
|
content: {$self.content},
|
||||||
|
)"""
|
||||||
|
|
||||||
|
proc id*(self: DiscordMessageItem): string {.inline.} =
|
||||||
|
self.id
|
||||||
|
|
||||||
|
QtProperty[string] id:
|
||||||
|
read = id
|
||||||
|
|
||||||
|
proc timestamp*(self: DiscordMessageItem): string {.inline.} =
|
||||||
|
self.timestamp
|
||||||
|
|
||||||
|
QtProperty[string] timestamp:
|
||||||
|
read = timestamp
|
||||||
|
|
||||||
|
proc timestampEdited*(self: DiscordMessageItem): string {.inline.} =
|
||||||
|
self.timestampEdited
|
||||||
|
|
||||||
|
QtProperty[string] timestampEdited:
|
||||||
|
read = timestampEdited
|
||||||
|
|
||||||
|
proc content*(self: DiscordMessageItem): string {.inline.} =
|
||||||
|
self.content
|
||||||
|
|
||||||
|
QtProperty[string] content:
|
||||||
|
read = content
|
||||||
|
|
||||||
|
proc author*(self: DiscordMessageItem): DiscordMessageAuthor {.inline.} =
|
||||||
|
self.author
|
|
@ -1,6 +1,7 @@
|
||||||
import json, strformat
|
import json, strformat, strutils
|
||||||
import ../../../app_service/common/types
|
import ../../../app_service/common/types
|
||||||
import ../../../app_service/service/contacts/dto/contacts
|
import ../../../app_service/service/contacts/dto/contacts
|
||||||
|
import ../../../app_service/service/message/dto/message
|
||||||
|
|
||||||
export types.ContentType
|
export types.ContentType
|
||||||
import message_reaction_model, message_reaction_item, message_transaction_parameters_item
|
import message_reaction_model, message_reaction_item, message_transaction_parameters_item
|
||||||
|
@ -64,6 +65,7 @@ type
|
||||||
mentionedUsersPks: seq[string]
|
mentionedUsersPks: seq[string]
|
||||||
senderTrustStatus: TrustStatus
|
senderTrustStatus: TrustStatus
|
||||||
senderEnsVerified: bool
|
senderEnsVerified: bool
|
||||||
|
messageAttachments: seq[string]
|
||||||
|
|
||||||
proc initItem*(
|
proc initItem*(
|
||||||
id,
|
id,
|
||||||
|
@ -90,7 +92,8 @@ proc initItem*(
|
||||||
transactionParameters: TransactionParametersItem,
|
transactionParameters: TransactionParametersItem,
|
||||||
mentionedUsersPks: seq[string],
|
mentionedUsersPks: seq[string],
|
||||||
senderTrustStatus: TrustStatus,
|
senderTrustStatus: TrustStatus,
|
||||||
senderEnsVerified: bool
|
senderEnsVerified: bool,
|
||||||
|
discordMessage: DiscordMessage
|
||||||
): Item =
|
): Item =
|
||||||
result = Item()
|
result = Item()
|
||||||
result.id = id
|
result.id = id
|
||||||
|
@ -124,6 +127,23 @@ proc initItem*(
|
||||||
result.gapTo = 0
|
result.gapTo = 0
|
||||||
result.senderTrustStatus = senderTrustStatus
|
result.senderTrustStatus = senderTrustStatus
|
||||||
result.senderEnsVerified = senderEnsVerified
|
result.senderEnsVerified = senderEnsVerified
|
||||||
|
result.messageAttachments = @[]
|
||||||
|
|
||||||
|
if ContentType.DiscordMessage == contentType:
|
||||||
|
result.messageText = discordMessage.content
|
||||||
|
result.senderDisplayName = discordMessage.author.name
|
||||||
|
result.senderIcon = discordMessage.author.localUrl
|
||||||
|
result.timestamp = parseInt(discordMessage.timestamp)*1000
|
||||||
|
|
||||||
|
if result.senderIcon == "":
|
||||||
|
result.senderIcon = discordMessage.author.avatarUrl
|
||||||
|
|
||||||
|
if discordMessage.timestampEdited != "":
|
||||||
|
result.timestamp = parseInt(discordMessage.timestampEdited)*1000
|
||||||
|
|
||||||
|
for attachment in discordMessage.attachments:
|
||||||
|
if attachment.contentType.contains("image"):
|
||||||
|
result.messageAttachments.add(attachment.localUrl)
|
||||||
|
|
||||||
proc `$`*(self: Item): string =
|
proc `$`*(self: Item): string =
|
||||||
result = fmt"""Item(
|
result = fmt"""Item(
|
||||||
|
@ -276,6 +296,9 @@ proc addReaction*(self: Item, emojiId: EmojiId, didIReactWithThisEmoji: bool, us
|
||||||
proc removeReaction*(self: Item, emojiId: EmojiId, reactionId: string, didIRemoveThisReaction: bool) =
|
proc removeReaction*(self: Item, emojiId: EmojiId, reactionId: string, didIRemoveThisReaction: bool) =
|
||||||
self.reactionsModel.removeReaction(emojiId, reactionId, didIRemoveThisReaction)
|
self.reactionsModel.removeReaction(emojiId, reactionId, didIRemoveThisReaction)
|
||||||
|
|
||||||
|
proc messageAttachments*(self: Item): seq[string] {.inline.} =
|
||||||
|
self.messageAttachments
|
||||||
|
|
||||||
proc links*(self: Item): seq[string] {.inline.} =
|
proc links*(self: Item): seq[string] {.inline.} =
|
||||||
self.links
|
self.links
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ type
|
||||||
MentionedUsersPks
|
MentionedUsersPks
|
||||||
SenderTrustStatus
|
SenderTrustStatus
|
||||||
SenderEnsVerified
|
SenderEnsVerified
|
||||||
|
MessageAttachments
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type
|
type
|
||||||
|
@ -116,7 +117,8 @@ QtObject:
|
||||||
ModelRole.TransactionParameters.int: "transactionParameters",
|
ModelRole.TransactionParameters.int: "transactionParameters",
|
||||||
ModelRole.MentionedUsersPks.int: "mentionedUsersPks",
|
ModelRole.MentionedUsersPks.int: "mentionedUsersPks",
|
||||||
ModelRole.SenderTrustStatus.int: "senderTrustStatus",
|
ModelRole.SenderTrustStatus.int: "senderTrustStatus",
|
||||||
ModelRole.SenderEnsVerified.int: "senderEnsVerified"
|
ModelRole.SenderEnsVerified.int: "senderEnsVerified",
|
||||||
|
ModelRole.MessageAttachments.int: "messageAttachments"
|
||||||
}.toTable
|
}.toTable
|
||||||
|
|
||||||
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
||||||
|
@ -211,6 +213,8 @@ QtObject:
|
||||||
result = newQVariant(item.mentionedUsersPks.join(" "))
|
result = newQVariant(item.mentionedUsersPks.join(" "))
|
||||||
of ModelRole.SenderEnsVerified:
|
of ModelRole.SenderEnsVerified:
|
||||||
result = newQVariant(item.senderEnsVerified)
|
result = newQVariant(item.senderEnsVerified)
|
||||||
|
of ModelRole.MessageAttachments:
|
||||||
|
result = newQVariant(item.messageAttachments.join(" "))
|
||||||
|
|
||||||
proc updateItemAtIndex(self: Model, index: int) =
|
proc updateItemAtIndex(self: Model, index: int) =
|
||||||
let ind = self.createIndex(index, 0, nil)
|
let ind = self.createIndex(index, 0, nil)
|
||||||
|
|
|
@ -14,6 +14,7 @@ type
|
||||||
Community = 9
|
Community = 9
|
||||||
Gap = 10
|
Gap = 10
|
||||||
Edit = 11
|
Edit = 11
|
||||||
|
DiscordMessage = 12
|
||||||
|
|
||||||
type
|
type
|
||||||
StatusType* {.pure.} = enum
|
StatusType* {.pure.} = enum
|
||||||
|
|
|
@ -96,6 +96,15 @@ type DiscordImportError* = object
|
||||||
code*: int
|
code*: int
|
||||||
message*: string
|
message*: string
|
||||||
|
|
||||||
|
type DiscordImportTaskProgress* = object
|
||||||
|
`type`*: string
|
||||||
|
progress*: float
|
||||||
|
errors*: seq[DiscordImportError]
|
||||||
|
errorsCount*: int
|
||||||
|
warningsCount*: int
|
||||||
|
stopped*: bool
|
||||||
|
state*: string
|
||||||
|
|
||||||
proc toCommunityAdminSettingsDto*(jsonObj: JsonNode): CommunityAdminSettingsDto =
|
proc toCommunityAdminSettingsDto*(jsonObj: JsonNode): CommunityAdminSettingsDto =
|
||||||
result = CommunityAdminSettingsDto()
|
result = CommunityAdminSettingsDto()
|
||||||
discard jsonObj.getProp("pinMessageAllMembersEnabled", result.pinMessageAllMembersEnabled)
|
discard jsonObj.getProp("pinMessageAllMembersEnabled", result.pinMessageAllMembersEnabled)
|
||||||
|
@ -118,6 +127,21 @@ proc toDiscordImportError*(jsonObj: JsonNode): DiscordImportError =
|
||||||
discard jsonObj.getProp("code", result.code)
|
discard jsonObj.getProp("code", result.code)
|
||||||
discard jsonObj.getProp("message", result.message)
|
discard jsonObj.getProp("message", result.message)
|
||||||
|
|
||||||
|
proc toDiscordImportTaskProgress*(jsonObj: JsonNode): DiscordImportTaskProgress =
|
||||||
|
result = DiscordImportTaskProgress()
|
||||||
|
result.`type` = jsonObj{"type"}.getStr()
|
||||||
|
result.progress = jsonObj{"progress"}.getFloat()
|
||||||
|
result.stopped = jsonObj{"stopped"}.getBool()
|
||||||
|
result.errorsCount = jsonObj{"errorsCount"}.getInt()
|
||||||
|
result.warningsCount = jsonObj{"warningsCount"}.getInt()
|
||||||
|
result.state = jsonObj{"state"}.getStr()
|
||||||
|
|
||||||
|
var importErrorsObj: JsonNode
|
||||||
|
if(jsonObj.getProp("errors", importErrorsObj) and importErrorsObj.kind == JArray):
|
||||||
|
for error in importErrorsObj:
|
||||||
|
let importError = error.toDiscordImportError()
|
||||||
|
result.errors.add(importError)
|
||||||
|
|
||||||
proc toCommunityDto*(jsonObj: JsonNode): CommunityDto =
|
proc toCommunityDto*(jsonObj: JsonNode): CommunityDto =
|
||||||
result = CommunityDto()
|
result = CommunityDto()
|
||||||
discard jsonObj.getProp("id", result.id)
|
discard jsonObj.getProp("id", result.id)
|
||||||
|
|
|
@ -80,6 +80,15 @@ type
|
||||||
errors*: Table[string, DiscordImportError]
|
errors*: Table[string, DiscordImportError]
|
||||||
errorsCount*: int
|
errorsCount*: int
|
||||||
|
|
||||||
|
DiscordImportProgressArgs* = ref object of Args
|
||||||
|
communityId*: string
|
||||||
|
communityName*: string
|
||||||
|
tasks*: seq[DiscordImportTaskProgress]
|
||||||
|
progress*: float
|
||||||
|
errorsCount*: int
|
||||||
|
warningsCount*: int
|
||||||
|
stopped*: bool
|
||||||
|
|
||||||
# Signals which may be emitted by this service:
|
# Signals which may be emitted by this service:
|
||||||
const SIGNAL_COMMUNITY_JOINED* = "communityJoined"
|
const SIGNAL_COMMUNITY_JOINED* = "communityJoined"
|
||||||
const SIGNAL_COMMUNITY_MY_REQUEST_ADDED* = "communityMyRequestAdded"
|
const SIGNAL_COMMUNITY_MY_REQUEST_ADDED* = "communityMyRequestAdded"
|
||||||
|
@ -107,6 +116,8 @@ const SIGNAL_COMMUNITY_MUTED* = "communityMuted"
|
||||||
const SIGNAL_CATEGORY_MUTED* = "categoryMuted"
|
const SIGNAL_CATEGORY_MUTED* = "categoryMuted"
|
||||||
const SIGNAL_CATEGORY_UNMUTED* = "categoryUnmuted"
|
const SIGNAL_CATEGORY_UNMUTED* = "categoryUnmuted"
|
||||||
const SIGNAL_DISCORD_CATEGORIES_AND_CHANNELS_EXTRACTED* = "discordCategoriesAndChannelsExtracted"
|
const SIGNAL_DISCORD_CATEGORIES_AND_CHANNELS_EXTRACTED* = "discordCategoriesAndChannelsExtracted"
|
||||||
|
const SIGNAL_DISCORD_COMMUNITY_IMPORT_FINISHED* = "discordCommunityImportFinished"
|
||||||
|
const SIGNAL_DISCORD_COMMUNITY_IMPORT_PROGRESS* = "discordCommunityImportProgress"
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type
|
type
|
||||||
|
@ -198,6 +209,22 @@ QtObject:
|
||||||
errorsCount: receivedData.errorsCount
|
errorsCount: receivedData.errorsCount
|
||||||
))
|
))
|
||||||
|
|
||||||
|
self.events.on(SignalType.DiscordCommunityImportFinished.event) do(e: Args):
|
||||||
|
var receivedData = DiscordCommunityImportFinishedSignal(e)
|
||||||
|
self.events.emit(SIGNAL_DISCORD_COMMUNITY_IMPORT_FINISHED, CommunityIdArgs(communityId: receivedData.communityId))
|
||||||
|
|
||||||
|
self.events.on(SignalType.DiscordCommunityImportProgress.event) do(e: Args):
|
||||||
|
var receivedData = DiscordCommunityImportProgressSignal(e)
|
||||||
|
self.events.emit(SIGNAL_DISCORD_COMMUNITY_IMPORT_PROGRESS, DiscordImportProgressArgs(
|
||||||
|
communityId: receivedData.communityId,
|
||||||
|
communityName: receivedData.communityName,
|
||||||
|
tasks: receivedData.tasks,
|
||||||
|
progress: receivedData.progress,
|
||||||
|
errorsCount: receivedData.errorsCount,
|
||||||
|
warningsCount: receivedData.warningsCount,
|
||||||
|
stopped: receivedData.stopped
|
||||||
|
))
|
||||||
|
|
||||||
proc updateMissingFields(chatDto: var ChatDto, chat: ChatDto) =
|
proc updateMissingFields(chatDto: var ChatDto, chat: ChatDto) =
|
||||||
# This proc sets fields of `chatDto` which are available only for community channels.
|
# This proc sets fields of `chatDto` which are available only for community channels.
|
||||||
chatDto.position = chat.position
|
chatDto.position = chat.position
|
||||||
|
@ -648,6 +675,49 @@ QtObject:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error "Error leaving community", msg = e.msg, communityId
|
error "Error leaving community", msg = e.msg, communityId
|
||||||
|
|
||||||
|
proc requestImportDiscordCommunity*(
|
||||||
|
self: Service,
|
||||||
|
name: string,
|
||||||
|
description: string,
|
||||||
|
introMessage: string,
|
||||||
|
outroMessage: string,
|
||||||
|
access: int,
|
||||||
|
color: string,
|
||||||
|
tags: string,
|
||||||
|
imageUrl: string,
|
||||||
|
aX: int, aY: int, bX: int, bY: int,
|
||||||
|
historyArchiveSupportEnabled: bool,
|
||||||
|
pinMessageAllMembersEnabled: bool,
|
||||||
|
filesToImport: seq[string],
|
||||||
|
fromTimestamp: int) =
|
||||||
|
try:
|
||||||
|
var image = singletonInstance.utils.formatImagePath(imageUrl)
|
||||||
|
var tagsString = tags
|
||||||
|
if len(tagsString) == 0:
|
||||||
|
tagsString = "[]"
|
||||||
|
|
||||||
|
let response = status_go.requestImportDiscordCommunity(
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
introMessage,
|
||||||
|
outroMessage,
|
||||||
|
access,
|
||||||
|
color,
|
||||||
|
tagsString,
|
||||||
|
image,
|
||||||
|
aX, aY, bX, bY,
|
||||||
|
historyArchiveSupportEnabled,
|
||||||
|
pinMessageAllMembersEnabled,
|
||||||
|
filesToImport,
|
||||||
|
fromTimestamp)
|
||||||
|
|
||||||
|
if response.error != nil:
|
||||||
|
let error = Json.decode($response.error, RpcError)
|
||||||
|
raise newException(RpcException, "Error creating community: " & error.message)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
error "Error creating community", msg = e.msg
|
||||||
|
|
||||||
proc createCommunity*(
|
proc createCommunity*(
|
||||||
self: Service,
|
self: Service,
|
||||||
name: string,
|
name: string,
|
||||||
|
@ -1226,3 +1296,9 @@ QtObject:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error "Error extracting discord channels and categories", msg = e.msg
|
error "Error extracting discord channels and categories", msg = e.msg
|
||||||
|
|
||||||
|
proc requestCancelDiscordCommunityImport*(self: Service, communityId: string) =
|
||||||
|
try:
|
||||||
|
discard status_go.requestCancelDiscordCommunityImport(communityId)
|
||||||
|
except Exception as e:
|
||||||
|
error "Error extracting discord channels and categories", msg = e.msg
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,29 @@ type QuotedMessage* = object
|
||||||
text*: string
|
text*: string
|
||||||
parsedText*: seq[ParsedText]
|
parsedText*: seq[ParsedText]
|
||||||
|
|
||||||
|
type DiscordMessageAttachment* = object
|
||||||
|
id*: string
|
||||||
|
fileUrl*: string
|
||||||
|
fileName*: string
|
||||||
|
localUrl*: string
|
||||||
|
contentType*: string
|
||||||
|
|
||||||
|
type DiscordMessageAuthor* = object
|
||||||
|
id*: string
|
||||||
|
name*: string
|
||||||
|
nickname*: string
|
||||||
|
avatarUrl*: string
|
||||||
|
localUrl*: string
|
||||||
|
|
||||||
|
type DiscordMessage* = object
|
||||||
|
id*: string
|
||||||
|
`type`*: string
|
||||||
|
timestamp*: string
|
||||||
|
timestampEdited*: string
|
||||||
|
content*: string
|
||||||
|
author*: DiscordMessageAuthor
|
||||||
|
attachments*: seq[DiscordMessageAttachment]
|
||||||
|
|
||||||
type Sticker* = object
|
type Sticker* = object
|
||||||
hash*: string
|
hash*: string
|
||||||
url*: string
|
url*: string
|
||||||
|
@ -60,6 +83,7 @@ type MessageDto* = object
|
||||||
seen*: bool
|
seen*: bool
|
||||||
outgoingStatus*: string
|
outgoingStatus*: string
|
||||||
quotedMessage*: QuotedMessage
|
quotedMessage*: QuotedMessage
|
||||||
|
discordMessage*: DiscordMessage
|
||||||
rtl*: bool
|
rtl*: bool
|
||||||
parsedText*: seq[ParsedText]
|
parsedText*: seq[ParsedText]
|
||||||
lineCount*: int
|
lineCount*: int
|
||||||
|
@ -91,6 +115,41 @@ proc toParsedText*(jsonObj: JsonNode): ParsedText =
|
||||||
for childObj in childrenArr:
|
for childObj in childrenArr:
|
||||||
result.children.add(toParsedText(childObj))
|
result.children.add(toParsedText(childObj))
|
||||||
|
|
||||||
|
proc toDiscordMessageAuthor*(jsonObj: JsonNode): DiscordMessageAuthor =
|
||||||
|
result = DiscordMessageAuthor()
|
||||||
|
discard jsonObj.getProp("id", result.id)
|
||||||
|
discard jsonObj.getProp("name", result.name)
|
||||||
|
discard jsonObj.getProp("nickname", result.nickname)
|
||||||
|
discard jsonObj.getProp("avatarUrl", result.avatarUrl)
|
||||||
|
discard jsonObj.getProp("localUrl", result.localUrl)
|
||||||
|
|
||||||
|
|
||||||
|
proc toDiscordMessageAttachment*(jsonObj: JsonNOde): DiscordMessageAttachment =
|
||||||
|
result = DiscordMessageAttachment()
|
||||||
|
discard jsonObj.getProp("id", result.id)
|
||||||
|
discard jsonObj.getProp("url", result.fileUrl)
|
||||||
|
discard jsonObj.getProp("localUrl", result.localUrl)
|
||||||
|
discard jsonObj.getProp("fileName", result.fileName)
|
||||||
|
discard jsonObj.getProp("contentType", result.contentType)
|
||||||
|
|
||||||
|
proc toDiscordMessage*(jsonObj: JsonNode): DiscordMessage =
|
||||||
|
result = DiscordMessage()
|
||||||
|
discard jsonObj.getProp("id", result.id)
|
||||||
|
discard jsonObj.getProp("type", result.type)
|
||||||
|
discard jsonObj.getProp("timestamp", result.timestamp)
|
||||||
|
discard jsonObj.getProp("timestampEdited", result.timestampEdited)
|
||||||
|
discard jsonObj.getProp("content", result.content)
|
||||||
|
|
||||||
|
var discordMessageAuthorObj: JsonNode
|
||||||
|
if(jsonObj.getProp("author", discordMessageAuthorObj)):
|
||||||
|
result.author = toDiscordMessageAuthor(discordMessageAuthorObj)
|
||||||
|
|
||||||
|
result.attachments = @[]
|
||||||
|
var attachmentsArr: JsonNode
|
||||||
|
if(jsonObj.getProp("attachments", attachmentsArr) and attachmentsArr.kind == JArray):
|
||||||
|
for attachment in attachmentsArr:
|
||||||
|
result.attachments.add(toDiscordMessageAttachment(attachment))
|
||||||
|
|
||||||
proc toQuotedMessage*(jsonObj: JsonNode): QuotedMessage =
|
proc toQuotedMessage*(jsonObj: JsonNode): QuotedMessage =
|
||||||
result = QuotedMessage()
|
result = QuotedMessage()
|
||||||
discard jsonObj.getProp("from", result.from)
|
discard jsonObj.getProp("from", result.from)
|
||||||
|
@ -151,6 +210,10 @@ proc toMessageDto*(jsonObj: JsonNode): MessageDto =
|
||||||
if(jsonObj.getProp("quotedMessage", quotedMessageObj)):
|
if(jsonObj.getProp("quotedMessage", quotedMessageObj)):
|
||||||
result.quotedMessage = toQuotedMessage(quotedMessageObj)
|
result.quotedMessage = toQuotedMessage(quotedMessageObj)
|
||||||
|
|
||||||
|
var discordMessageObj: JsonNode
|
||||||
|
if(jsonObj.getProp("discordMessage", discordMessageObj)):
|
||||||
|
result.discordMessage = toDiscordMessage(discordMessageObj)
|
||||||
|
|
||||||
var stickerObj: JsonNode
|
var stickerObj: JsonNode
|
||||||
if(jsonObj.getProp("sticker", stickerObj)):
|
if(jsonObj.getProp("sticker", stickerObj)):
|
||||||
result.sticker = toSticker(stickerObj)
|
result.sticker = toSticker(stickerObj)
|
||||||
|
|
|
@ -296,11 +296,13 @@ QtObject:
|
||||||
if (receivedData.emojiReactions.len > 0):
|
if (receivedData.emojiReactions.len > 0):
|
||||||
self.handleEmojiReactionsUpdate(receivedData.emojiReactions)
|
self.handleEmojiReactionsUpdate(receivedData.emojiReactions)
|
||||||
|
|
||||||
self.events.on(SignalType.HistoryArchiveDownloaded.event) do(e: Args):
|
self.events.on(SignalType.DownloadingHistoryArchivesFinished.event) do(e: Args):
|
||||||
var receivedData = HistoryArchivesSignal(e)
|
var receivedData = HistoryArchivesSignal(e)
|
||||||
if now().toTime().toUnix()-receivedData.begin <= WEEK_AS_MILLISECONDS:
|
self.handleMessagesReload(receivedData.communityId)
|
||||||
# we don't need to reload the messages for archives older than 7 days
|
|
||||||
self.handleMessagesReload(receivedData.communityId)
|
self.events.on(SignalType.DiscordCommunityImportFinished.event) do(e: Args):
|
||||||
|
var receivedData = DiscordCommunityImportFinishedSignal(e)
|
||||||
|
self.handleMessagesReload(receivedData.communityId)
|
||||||
|
|
||||||
proc initialMessagesFetched(self: Service, chatId: string): bool =
|
proc initialMessagesFetched(self: Service, chatId: string): bool =
|
||||||
return self.msgCursor.hasKey(chatId)
|
return self.msgCursor.hasKey(chatId)
|
||||||
|
|
|
@ -123,6 +123,45 @@ proc editCommunity*(
|
||||||
"pinMessageAllMembersEnabled": pinMessageAllMembersEnabled
|
"pinMessageAllMembersEnabled": pinMessageAllMembersEnabled
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
proc requestImportDiscordCommunity*(
|
||||||
|
name: string,
|
||||||
|
description: string,
|
||||||
|
introMessage: string,
|
||||||
|
outroMessage: string,
|
||||||
|
access: int,
|
||||||
|
color: string,
|
||||||
|
tags: string,
|
||||||
|
imageUrl: string,
|
||||||
|
aX: int, aY: int, bX: int, bY: int,
|
||||||
|
historyArchiveSupportEnabled: bool,
|
||||||
|
pinMessageAllMembersEnabled: bool,
|
||||||
|
filesToImport: seq[string],
|
||||||
|
fromTimestamp: int
|
||||||
|
): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||||
|
result = callPrivateRPC("requestImportDiscordCommunity".prefix, %*[{
|
||||||
|
# TODO this will need to be renamed membership (small m)
|
||||||
|
"Membership": access,
|
||||||
|
"name": name,
|
||||||
|
"description": description,
|
||||||
|
"introMessage": introMessage,
|
||||||
|
"outroMessage": outroMessage,
|
||||||
|
"ensOnly": false, # TODO ensOnly is no longer supported. Remove this when we remove it in status-go
|
||||||
|
"color": color,
|
||||||
|
"tags": parseJson(tags),
|
||||||
|
"image": imageUrl,
|
||||||
|
"imageAx": aX,
|
||||||
|
"imageAy": aY,
|
||||||
|
"imageBx": bX,
|
||||||
|
"imageBy": bY,
|
||||||
|
"historyArchiveSupportEnabled": historyArchiveSupportEnabled,
|
||||||
|
"pinMessageAllMembersEnabled": pinMessageAllMembersEnabled,
|
||||||
|
"from": fromTimestamp,
|
||||||
|
"filesToImport": filesToImport
|
||||||
|
}])
|
||||||
|
|
||||||
|
proc requestCancelDiscordCommunityImport*(communityId: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||||
|
result = callPrivateRPC("requestCancelDiscordCommunityImport".prefix, %*[communityId])
|
||||||
|
|
||||||
proc createCommunityChannel*(
|
proc createCommunityChannel*(
|
||||||
communityId: string,
|
communityId: string,
|
||||||
name: string,
|
name: string,
|
||||||
|
|
|
@ -284,6 +284,7 @@ Item {
|
||||||
editModeOn: model.editMode
|
editModeOn: model.editMode
|
||||||
isEdited: model.isEdited
|
isEdited: model.isEdited
|
||||||
linkUrls: model.links
|
linkUrls: model.links
|
||||||
|
messageAttachments: model.messageAttachments
|
||||||
transactionParams: model.transactionParameters
|
transactionParams: model.transactionParameters
|
||||||
hasMention: model.mentionedUsersPks.split(" ").includes(root.rootStore.userProfileInst.pubKey)
|
hasMention: model.mentionedUsersPks.split(" ").includes(root.rootStore.userProfileInst.pubKey)
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ StatusSectionLayout {
|
||||||
property var importCommunitiesPopup: importCommunitiesPopupComponent
|
property var importCommunitiesPopup: importCommunitiesPopupComponent
|
||||||
property var createCommunitiesPopup: createCommunitiesPopupComponent
|
property var createCommunitiesPopup: createCommunitiesPopupComponent
|
||||||
property int contentPrefferedWidth: 100
|
property int contentPrefferedWidth: 100
|
||||||
|
property var discordImportProgressPopup: discordImportProgressDialog
|
||||||
|
|
||||||
notificationCount: root.communitiesStore.unreadNotificationsCount
|
notificationCount: root.communitiesStore.unreadNotificationsCount
|
||||||
onNotificationButtonClicked: Global.openActivityCenterPopup()
|
onNotificationButtonClicked: Global.openActivityCenterPopup()
|
||||||
|
@ -219,9 +220,14 @@ StatusSectionLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CommunityBanner {
|
CommunityBanner {
|
||||||
text: qsTr("Import existing Discord community into Status")
|
property bool importInProgress: root.communitiesStore.discordImportInProgress && !root.communitiesStore.discordImportCancelled
|
||||||
|
text: importInProgress ?
|
||||||
|
qsTr("'%1' import in progress...").arg(root.communitiesStore.discordImportCommunityName) :
|
||||||
|
qsTr("Import existing Discord community into Status")
|
||||||
buttonText: qsTr("Import existing")
|
buttonText: qsTr("Import existing")
|
||||||
icon.name: "download"
|
icon.name: "download"
|
||||||
|
buttonTooltipText: qsTr("Your current import must finished or be cancelled before a new import can be started.")
|
||||||
|
buttonLoading: importInProgress
|
||||||
onButtonClicked: {
|
onButtonClicked: {
|
||||||
chooseCommunityCreationTypePopup.close()
|
chooseCommunityCreationTypePopup.close()
|
||||||
Global.openPopup(createCommunitiesPopupComponent, {isDiscordImport: true})
|
Global.openPopup(createCommunitiesPopupComponent, {isDiscordImport: true})
|
||||||
|
@ -230,4 +236,11 @@ StatusSectionLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: discordImportProgressDialog
|
||||||
|
DiscordImportProgressDialog {
|
||||||
|
store: root.communitiesStore
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,9 +125,16 @@ StatusStackModal {
|
||||||
}
|
}
|
||||||
IssuePill {
|
IssuePill {
|
||||||
type: root.store.discordImportErrorsCount ? IssuePill.Type.Error : IssuePill.Type.Warning
|
type: root.store.discordImportErrorsCount ? IssuePill.Type.Error : IssuePill.Type.Warning
|
||||||
count: root.store.discordImportErrorsCount ? root.store.discordImportErrorsCount :
|
count: {
|
||||||
root.store.discordImportWarningsCount ? root.store.discordImportWarningsCount : 0
|
if (root.store.discordImportErrorsCount > 0) {
|
||||||
visible: count
|
return root.store.discordImportErrorsCount
|
||||||
|
}
|
||||||
|
if (root.store.discordImportWarningsCount > 0) {
|
||||||
|
return root.store.discordImportWarningsCount
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
visible: !!count
|
||||||
}
|
}
|
||||||
Item { Layout.fillWidth: true }
|
Item { Layout.fillWidth: true }
|
||||||
StatusButton {
|
StatusButton {
|
||||||
|
@ -243,6 +250,20 @@ StatusStackModal {
|
||||||
|
|
||||||
readonly property bool canGoNext: root.store.discordChannelsModel.hasSelectedItems
|
readonly property bool canGoNext: root.store.discordChannelsModel.hasSelectedItems
|
||||||
readonly property var nextAction: function () {
|
readonly property var nextAction: function () {
|
||||||
|
d.requestImportDiscordCommunity()
|
||||||
|
// replace ourselves with the progress dialog, no way back
|
||||||
|
root.leftButtons[0].visible = false
|
||||||
|
root.backgroundColor = Theme.palette.baseColor4
|
||||||
|
root.replace(progressComponent)
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: progressComponent
|
||||||
|
DiscordImportProgressContents {
|
||||||
|
width: root.availableWidth
|
||||||
|
store: root.store
|
||||||
|
onClose: root.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -456,34 +477,46 @@ StatusStackModal {
|
||||||
QtObject {
|
QtObject {
|
||||||
id: d
|
id: d
|
||||||
|
|
||||||
|
function _getCommunityConfig() {
|
||||||
|
return {
|
||||||
|
name: StatusQUtils.Utils.filterXSS(nameInput.input.text),
|
||||||
|
description: StatusQUtils.Utils.filterXSS(descriptionTextInput.input.text),
|
||||||
|
introMessage: StatusQUtils.Utils.filterXSS(introMessageInput.input.text),
|
||||||
|
outroMessage: StatusQUtils.Utils.filterXSS(outroMessageInput.input.text),
|
||||||
|
color: colorPicker.color.toString().toUpperCase(),
|
||||||
|
tags: communityTagsPicker.selectedTags,
|
||||||
|
image: {
|
||||||
|
src: logoPicker.source,
|
||||||
|
AX: logoPicker.cropRect.x,
|
||||||
|
AY: logoPicker.cropRect.y,
|
||||||
|
BX: logoPicker.cropRect.x + logoPicker.cropRect.width,
|
||||||
|
BY: logoPicker.cropRect.y + logoPicker.cropRect.height,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
historyArchiveSupportEnabled: options.archiveSupportEnabled,
|
||||||
|
checkedMembership: options.requestToJoinEnabled ? Constants.communityChatOnRequestAccess : Constants.communityChatPublicAccess,
|
||||||
|
pinMessagesAllowedForMembers: options.pinMessagesEnabled
|
||||||
|
},
|
||||||
|
bannerJsonStr: JSON.stringify({imagePath: String(bannerPicker.source).replace("file://", ""), cropRect: bannerPicker.cropRect})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function createCommunity() {
|
function createCommunity() {
|
||||||
const error = store.createCommunity({
|
const error = root.store.createCommunity(_getCommunityConfig())
|
||||||
name: StatusQUtils.Utils.filterXSS(nameInput.input.text),
|
|
||||||
description: StatusQUtils.Utils.filterXSS(descriptionTextInput.input.text),
|
|
||||||
introMessage: StatusQUtils.Utils.filterXSS(introMessageInput.input.text),
|
|
||||||
outroMessage: StatusQUtils.Utils.filterXSS(outroMessageInput.input.text),
|
|
||||||
color: colorPicker.color.toString().toUpperCase(),
|
|
||||||
tags: communityTagsPicker.selectedTags,
|
|
||||||
image: {
|
|
||||||
src: logoPicker.source,
|
|
||||||
AX: logoPicker.cropRect.x,
|
|
||||||
AY: logoPicker.cropRect.y,
|
|
||||||
BX: logoPicker.cropRect.x + logoPicker.cropRect.width,
|
|
||||||
BY: logoPicker.cropRect.y + logoPicker.cropRect.height,
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
historyArchiveSupportEnabled: options.archiveSupportEnabled,
|
|
||||||
checkedMembership: options.requestToJoinEnabled ? Constants.communityChatOnRequestAccess : Constants.communityChatPublicAccess,
|
|
||||||
pinMessagesAllowedForMembers: options.pinMessagesEnabled
|
|
||||||
},
|
|
||||||
bannerJsonStr: JSON.stringify({imagePath: String(bannerPicker.source).replace("file://", ""), cropRect: bannerPicker.cropRect})
|
|
||||||
})
|
|
||||||
if (error) {
|
if (error) {
|
||||||
errorDialog.text = error.error
|
errorDialog.text = error.error
|
||||||
errorDialog.open()
|
errorDialog.open()
|
||||||
}
|
}
|
||||||
root.close()
|
root.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function requestImportDiscordCommunity() {
|
||||||
|
const error = root.store.requestImportDiscordCommunity(_getCommunityConfig(), datePicker.selectedDate.valueOf()/1000)
|
||||||
|
if (error) {
|
||||||
|
errorDialog.text = error.error
|
||||||
|
errorDialog.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageDialog {
|
MessageDialog {
|
||||||
|
|
|
@ -0,0 +1,356 @@
|
||||||
|
import QtQuick 2.14
|
||||||
|
import QtQuick.Controls 2.14
|
||||||
|
import QtQuick.Layouts 1.14
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
import shared.popups 1.0
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Components 0.1
|
||||||
|
import StatusQ.Popups.Dialog 0.1
|
||||||
|
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
import "../controls"
|
||||||
|
|
||||||
|
StatusScrollView {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var store
|
||||||
|
|
||||||
|
signal close()
|
||||||
|
|
||||||
|
implicitWidth: childrenRect.width
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
enum ImportStatus {
|
||||||
|
Unknown,
|
||||||
|
InProgress,
|
||||||
|
Stopped,
|
||||||
|
StoppedWithErrors,
|
||||||
|
CompletedWithWarnings,
|
||||||
|
CompletedSuccessfully
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property list<StatusBaseButton> rightButtons: [
|
||||||
|
StatusButton {
|
||||||
|
visible: d.status === DiscordImportProgressContents.ImportStatus.CompletedWithWarnings ||
|
||||||
|
d.status === DiscordImportProgressContents.ImportStatus.StoppedWithErrors
|
||||||
|
type: StatusButton.Danger
|
||||||
|
font.weight: Font.Medium
|
||||||
|
text: qsTr("Delete community & restart import")
|
||||||
|
onClicked: {
|
||||||
|
// TODO display a confirmation and open CreateCommunityPopup again
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
StatusButton {
|
||||||
|
visible: d.status === DiscordImportProgressContents.ImportStatus.InProgress
|
||||||
|
type: StatusButton.Danger
|
||||||
|
font.weight: Font.Medium
|
||||||
|
text: qsTr("Cancel import")
|
||||||
|
onClicked: {
|
||||||
|
Global.openPopup(cancelConfirmationPopupCmp)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
StatusButton {
|
||||||
|
visible: d.status === DiscordImportProgressContents.ImportStatus.Stopped // TODO find out exactly when to display this button
|
||||||
|
type: StatusButton.Danger
|
||||||
|
font.weight: Font.Medium
|
||||||
|
text: qsTr("Restart import")
|
||||||
|
onClicked: {
|
||||||
|
// TODO open CreateCommunityPopup again
|
||||||
|
root.store.resetDiscordImport()
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
StatusButton {
|
||||||
|
visible: d.status === DiscordImportProgressContents.ImportStatus.InProgress
|
||||||
|
font.weight: Font.Medium
|
||||||
|
text: qsTr("Hide window")
|
||||||
|
onClicked: root.close()
|
||||||
|
},
|
||||||
|
StatusButton {
|
||||||
|
visible: d.status === DiscordImportProgressContents.ImportStatus.CompletedSuccessfully ||
|
||||||
|
d.status === DiscordImportProgressContents.ImportStatus.CompletedWithWarnings
|
||||||
|
font.weight: Font.Medium
|
||||||
|
text: qsTr("Visit your new community")
|
||||||
|
onClicked: {
|
||||||
|
root.close()
|
||||||
|
root.store.setActiveCommunity(root.store.discordImportCommunityId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
readonly property var helperInfo: {
|
||||||
|
"import.communityCreation": {
|
||||||
|
icon: "network",
|
||||||
|
text: qsTr("Setting up your community")
|
||||||
|
},
|
||||||
|
"import.channelsCreation": {
|
||||||
|
icon: "channel",
|
||||||
|
text: qsTr("Importing channels")
|
||||||
|
},
|
||||||
|
"import.importMessages": {
|
||||||
|
icon: "receive",
|
||||||
|
text: qsTr("Importing messages")
|
||||||
|
},
|
||||||
|
"import.downloadAssets": {
|
||||||
|
icon: "image",
|
||||||
|
text: qsTr("Downloading assets")
|
||||||
|
},
|
||||||
|
"import.initializeCommunity": {
|
||||||
|
icon: "communities",
|
||||||
|
text: qsTr("Initializing community")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property int importProgress: root.store.discordImportProgress // FIXME for now it is 0..100
|
||||||
|
readonly property bool importStopped: root.store.discordImportProgressStopped
|
||||||
|
readonly property bool hasErrors: root.store.discordImportErrorsCount
|
||||||
|
readonly property bool hasWarnings: root.store.discordImportWarningsCount
|
||||||
|
|
||||||
|
readonly property int status: {
|
||||||
|
if (importStopped) {
|
||||||
|
if (hasErrors)
|
||||||
|
return DiscordImportProgressContents.ImportStatus.StoppedWithErrors
|
||||||
|
return DiscordImportProgressContents.ImportStatus.Stopped
|
||||||
|
}
|
||||||
|
if (importProgress >= 100) {
|
||||||
|
if (hasWarnings)
|
||||||
|
return DiscordImportProgressContents.ImportStatus.CompletedWithWarnings
|
||||||
|
return DiscordImportProgressContents.ImportStatus.CompletedSuccessfully
|
||||||
|
}
|
||||||
|
if (importProgress > 0 && importProgress < 100)
|
||||||
|
return DiscordImportProgressContents.ImportStatus.InProgress
|
||||||
|
return DiscordImportProgressContents.ImportStatus.Unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSubtaskDescription(progress, stopped, state) {
|
||||||
|
if (progress >= 1.0)
|
||||||
|
return qsTr("✓ Complete")
|
||||||
|
if (progress > 0 && stopped)
|
||||||
|
return qsTr("Import stopped...")
|
||||||
|
if (importStopped)
|
||||||
|
return ""
|
||||||
|
if (progress <= 0.0)
|
||||||
|
return qsTr("Pending...")
|
||||||
|
|
||||||
|
return state == "import.taskState.saving" ?
|
||||||
|
qsTr("Saving... This can take a moment, almost done!") :
|
||||||
|
qsTr("Working...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: subtaskComponent
|
||||||
|
ColumnLayout {
|
||||||
|
id: subtaskDelegate
|
||||||
|
spacing: 40
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
readonly property int errorsAndWarningsCount: model.errorsCount + model.warningsCount
|
||||||
|
readonly property string type: model.type
|
||||||
|
readonly property var errors: model.errors
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: subtaskRow
|
||||||
|
spacing: 12
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 42
|
||||||
|
|
||||||
|
StatusRoundIcon {
|
||||||
|
id: subtaskIcon
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.preferredWidth: 40
|
||||||
|
Layout.preferredHeight: 40
|
||||||
|
asset.name: d.helperInfo[model.type].icon
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 4
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
StatusBaseText {
|
||||||
|
font.pixelSize: 15
|
||||||
|
text: d.helperInfo[model.type].text
|
||||||
|
}
|
||||||
|
StatusBaseText {
|
||||||
|
font.pixelSize: 12
|
||||||
|
color: {
|
||||||
|
if (model.progress >= 1)
|
||||||
|
return Theme.palette.successColor1
|
||||||
|
if (model.progress > 0 && d.hasErrors)
|
||||||
|
return Theme.palette.dangerColor1
|
||||||
|
return Theme.palette.baseColor1
|
||||||
|
}
|
||||||
|
text: d.getSubtaskDescription(model.progress, model.stopped, model.state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item { Layout.fillWidth: true }
|
||||||
|
StatusBaseText {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
font.pixelSize: 13
|
||||||
|
font.weight: Font.Medium
|
||||||
|
visible: subtaskProgressBar.visible
|
||||||
|
text: qsTr("%1%").arg(Math.round(model.progress*100))
|
||||||
|
}
|
||||||
|
StatusProgressBar {
|
||||||
|
id: subtaskProgressBar
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.preferredWidth: 130
|
||||||
|
Layout.preferredHeight: 10
|
||||||
|
visible: value > 0 && value <= 1 && d.status !== DiscordImportProgressContents.ImportStatus.StoppedWithErrors
|
||||||
|
fillColor: Theme.palette.primaryColor1
|
||||||
|
backgroundColor: Theme.palette.directColor8
|
||||||
|
value: model.progress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: subtaskIcon.width + subtaskRow.spacing
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: SortFilterProxyModel {
|
||||||
|
sourceModel: subtaskDelegate.errors
|
||||||
|
sorters: RoleSorter { roleName: "code"; sortOrder: Qt.DescendingOrder } // errors first
|
||||||
|
}
|
||||||
|
delegate: IssuePill {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
horizontalPadding: 12
|
||||||
|
verticalPadding: 8
|
||||||
|
bgCornerRadius: 8
|
||||||
|
visible: text
|
||||||
|
type: model.code === Constants.DiscordImportErrorCode.Error ? IssuePill.Type.Error : IssuePill.Type.Warning
|
||||||
|
text: model.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: subtaskDelegate.errorsAndWarningsCount > 3
|
||||||
|
Layout.fillWidth: true
|
||||||
|
sourceComponent: IssuePill {
|
||||||
|
width: parent.width
|
||||||
|
horizontalPadding: 12
|
||||||
|
verticalPadding: 8
|
||||||
|
bgCornerRadius: 8
|
||||||
|
visible: text
|
||||||
|
type: IssuePill.Type.Warning
|
||||||
|
text: qsTr("%1 more issue(s) downloading assets").arg(errorsAndWarningsCount - 3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StatusDialogDivider {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: -24 // compensate for Control.horizontalPadding -> full width
|
||||||
|
Layout.rightMargin: -24 // compensate for Control.horizontalPadding -> full width
|
||||||
|
visible: !parent.Positioner.isLastItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 20
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.preferredWidth: 36
|
||||||
|
Layout.preferredHeight: 36
|
||||||
|
sourceSize: Qt.size(36, 36)
|
||||||
|
source: Style.svg("contact") // TODO community icon
|
||||||
|
}
|
||||||
|
StatusBaseText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pixelSize: 15
|
||||||
|
text: {
|
||||||
|
switch (d.status) {
|
||||||
|
case DiscordImportProgressContents.ImportStatus.InProgress:
|
||||||
|
return qsTr("Importing ‘%1’ from Discord...").arg(root.store.discordImportCommunityName)
|
||||||
|
case DiscordImportProgressContents.ImportStatus.Stopped:
|
||||||
|
return qsTr("Importing ‘%1’ from Discord stopped...").arg(root.store.discordImportCommunityName)
|
||||||
|
case DiscordImportProgressContents.ImportStatus.StoppedWithErrors:
|
||||||
|
return qsTr("Importing ‘%1’ stopped due to a critical issue...").arg(root.store.discordImportCommunityName)
|
||||||
|
case DiscordImportProgressContents.ImportStatus.CompletedWithWarnings:
|
||||||
|
return qsTr("‘%1’ was imported with %n issue(s).", "", root.store.discordImportWarningsCount).arg(root.store.discordImportCommunityName)
|
||||||
|
case DiscordImportProgressContents.ImportStatus.CompletedSuccessfully:
|
||||||
|
return qsTr("‘%1’ was successfully imported from Discord.").arg(root.store.discordImportCommunityName)
|
||||||
|
default:
|
||||||
|
return qsTr("Your Discord community import is in progress...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item { Layout.fillWidth: true }
|
||||||
|
IssuePill {
|
||||||
|
type: d.hasErrors ? IssuePill.Type.Error : IssuePill.Type.Warning
|
||||||
|
count: d.hasErrors ? root.store.discordImportErrorsCount :
|
||||||
|
d.hasWarnings ? root.store.discordImportWarningsCount : 0
|
||||||
|
visible: count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Control {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
horizontalPadding: 24
|
||||||
|
verticalPadding: 40
|
||||||
|
background: Rectangle {
|
||||||
|
radius: 16
|
||||||
|
color: Theme.palette.indirectColor1
|
||||||
|
border.width: 1
|
||||||
|
border.color: Theme.palette.directColor8
|
||||||
|
}
|
||||||
|
contentItem: Column {
|
||||||
|
spacing: 40
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.store.discordImportTasks
|
||||||
|
delegate: subtaskComponent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
font.pixelSize: 13
|
||||||
|
text: d.status === DiscordImportProgressContents.ImportStatus.InProgress ?
|
||||||
|
qsTr("This process can take a while. Feel free to hide this window and use Status normally in the meantime. We’ll notify you when the Community is ready for you.") :
|
||||||
|
qsTr("If there were any issues with your import you can upload new JSON files via the community page at any time.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: cancelConfirmationPopupCmp
|
||||||
|
ConfirmationDialog {
|
||||||
|
id: cancelConfirmationPopup
|
||||||
|
header.title: qsTr("Are you sure you want to cancel the import?")
|
||||||
|
confirmationText: qsTr("Your new Status community will be deleted and all information entered will be lost.")
|
||||||
|
showCancelButton: true
|
||||||
|
cancelBtnType: "default"
|
||||||
|
confirmButtonLabel: qsTr("Delete community")
|
||||||
|
cancelButtonLabel: qsTr("Continue importing")
|
||||||
|
onConfirmButtonClicked: {
|
||||||
|
root.store.requestCancelDiscordCommunityImport(root.store.discordImportCommunityId)
|
||||||
|
cancelConfirmationPopup.close()
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
onCancelButtonClicked: {
|
||||||
|
cancelConfirmationPopup.close()
|
||||||
|
}
|
||||||
|
onClosed: {
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
import QtQuick 2.14
|
||||||
|
import QtQuick.Controls 2.14
|
||||||
|
import QtQuick.Layouts 1.14
|
||||||
|
import QtQml.Models 2.14
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Components 0.1
|
||||||
|
import StatusQ.Popups.Dialog 0.1
|
||||||
|
|
||||||
|
import "../controls"
|
||||||
|
|
||||||
|
StatusDialog {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var store
|
||||||
|
|
||||||
|
title: qsTr("Import a community from Discord into Status")
|
||||||
|
|
||||||
|
horizontalPadding: 16
|
||||||
|
verticalPadding: 20
|
||||||
|
width: 640
|
||||||
|
|
||||||
|
onClosed: destroy()
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
const buttons = contents.rightButtons
|
||||||
|
for (let i = 0; i < buttons.length; i++) {
|
||||||
|
footer.rightButtons.append(buttons[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: StatusDialogFooter {
|
||||||
|
id: footer
|
||||||
|
rightButtons: ObjectModel {}
|
||||||
|
}
|
||||||
|
|
||||||
|
background: StatusDialogBackground {
|
||||||
|
color: Theme.palette.baseColor4
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: DiscordImportProgressContents {
|
||||||
|
id: contents
|
||||||
|
width: root.availableWidth
|
||||||
|
store: root.store
|
||||||
|
onClose: root.close()
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,13 @@ QtObject {
|
||||||
property bool discordDataExtractionInProgress: root.communitiesModuleInst.discordDataExtractionInProgress
|
property bool discordDataExtractionInProgress: root.communitiesModuleInst.discordDataExtractionInProgress
|
||||||
property int discordImportErrorsCount: root.communitiesModuleInst.discordImportErrorsCount
|
property int discordImportErrorsCount: root.communitiesModuleInst.discordImportErrorsCount
|
||||||
property int discordImportWarningsCount: root.communitiesModuleInst.discordImportWarningsCount
|
property int discordImportWarningsCount: root.communitiesModuleInst.discordImportWarningsCount
|
||||||
|
property int discordImportProgress: root.communitiesModuleInst.discordImportProgress
|
||||||
|
property bool discordImportInProgress: root.communitiesModuleInst.discordImportInProgress
|
||||||
|
property bool discordImportCancelled: root.communitiesModuleInst.discordImportCancelled
|
||||||
|
property bool discordImportProgressStopped: root.communitiesModuleInst.discordImportProgressStopped
|
||||||
|
property string discordImportCommunityId: root.communitiesModuleInst.discordImportCommunityId
|
||||||
|
property string discordImportCommunityName: root.communitiesModuleInst.discordImportCommunityName
|
||||||
|
property var discordImportTasks: root.communitiesModuleInst.discordImportTasks
|
||||||
property string locale: localAppSettings.language
|
property string locale: localAppSettings.language
|
||||||
property var advancedModule: profileSectionModule.advancedModule
|
property var advancedModule: profileSectionModule.advancedModule
|
||||||
property bool isCommunityHistoryArchiveSupportEnabled: advancedModule? advancedModule.isCommunityHistoryArchiveSupportEnabled : false
|
property bool isCommunityHistoryArchiveSupportEnabled: advancedModule? advancedModule.isCommunityHistoryArchiveSupportEnabled : false
|
||||||
|
@ -105,5 +112,40 @@ QtObject {
|
||||||
|
|
||||||
function toggleDiscordChannel(id, selected) {
|
function toggleDiscordChannel(id, selected) {
|
||||||
root.communitiesModuleInst.toggleDiscordChannel(id, selected)
|
root.communitiesModuleInst.toggleDiscordChannel(id, selected)
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestCancelDiscordCommunityImport(id) {
|
||||||
|
root.communitiesModuleInst.requestCancelDiscordCommunityImport(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetDiscordImport() {
|
||||||
|
root.communitiesModuleInst.resetDiscordImport(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestImportDiscordCommunity(args = {
|
||||||
|
name: "",
|
||||||
|
description: "",
|
||||||
|
introMessage: "",
|
||||||
|
outroMessage: "",
|
||||||
|
color: "",
|
||||||
|
tags: "",
|
||||||
|
image: {
|
||||||
|
src: "",
|
||||||
|
AX: 0,
|
||||||
|
AY: 0,
|
||||||
|
BX: 0,
|
||||||
|
BY: 0,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
historyArchiveSupportEnabled: false,
|
||||||
|
checkedMembership: false,
|
||||||
|
pinMessagesAllowedForMembers: false
|
||||||
|
}
|
||||||
|
}, from = 0) {
|
||||||
|
return communitiesModuleInst.requestImportDiscordCommunity(
|
||||||
|
args.name, args.description, args.introMessage, args.outroMessage, args.options.checkedMembership,
|
||||||
|
args.color, args.tags,
|
||||||
|
args.image.src, args.image.AX, args.image.AY, args.image.BX, args.image.BY,
|
||||||
|
args.options.historyArchiveSupportEnabled, args.options.pinMessagesAllowedForMembers, from);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -498,6 +498,57 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ModuleWarning {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
readonly property int progress: communitiesPortalLayoutContainer.communitiesStore.discordImportProgress
|
||||||
|
readonly property bool inProgress: progress > 0 && progress < 100
|
||||||
|
readonly property bool finished: progress >= 100
|
||||||
|
readonly property bool cancelled: communitiesPortalLayoutContainer.communitiesStore.discordImportCancelled
|
||||||
|
readonly property bool stopped: communitiesPortalLayoutContainer.communitiesStore.discordImportProgressStopped
|
||||||
|
readonly property int errors: communitiesPortalLayoutContainer.communitiesStore.discordImportErrorsCount
|
||||||
|
readonly property int warnings: communitiesPortalLayoutContainer.communitiesStore.discordImportWarningsCount
|
||||||
|
readonly property string communityId: communitiesPortalLayoutContainer.communitiesStore.discordImportCommunityId
|
||||||
|
readonly property string communityName: communitiesPortalLayoutContainer.communitiesStore.discordImportCommunityName
|
||||||
|
|
||||||
|
active: !cancelled && (inProgress || finished || stopped)
|
||||||
|
type: errors ? ModuleWarning.Type.Danger : ModuleWarning.Type.Success
|
||||||
|
text: {
|
||||||
|
if (finished || stopped) {
|
||||||
|
if (errors)
|
||||||
|
return qsTr("The import of ‘%1’ from Discord to Status was stopped: <a href='#'>Critical issues found</a>").arg(communityName)
|
||||||
|
|
||||||
|
let result = qsTr("‘%1’ was successfully imported from Discord to Status").arg(communityName) + " <a href='#'>"
|
||||||
|
if (warnings)
|
||||||
|
result += qsTr("Details (%1)").arg(qsTr("%n issue(s)", "", warnings))
|
||||||
|
else
|
||||||
|
result += qsTr("Details")
|
||||||
|
result += "</a>"
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
if (inProgress) {
|
||||||
|
let result = qsTr("Importing ‘%1’ from Discord to Status").arg(communityName) + " <a href='#'>"
|
||||||
|
if (warnings)
|
||||||
|
result += qsTr("Check progress (%1)").arg(qsTr("%n issue(s)", "", warnings))
|
||||||
|
else
|
||||||
|
result += qsTr("Check progress")
|
||||||
|
result += "</a>"
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onLinkActivated: Global.openPopup(communitiesPortalLayoutContainer.discordImportProgressPopup)
|
||||||
|
progressValue: progress
|
||||||
|
closeBtnVisible: finished || stopped
|
||||||
|
buttonText: finished && !errors ? qsTr("Visit your Community") : ""
|
||||||
|
onClicked: function() {
|
||||||
|
communitiesPortalLayoutContainer.communitiesStore.setActiveCommunity(communityId)
|
||||||
|
}
|
||||||
|
onCloseClicked: {
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: connectedBannerComponent
|
id: connectedBannerComponent
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ Loader {
|
||||||
property string image
|
property string image
|
||||||
property bool showRing: true
|
property bool showRing: true
|
||||||
property bool interactive: true
|
property bool interactive: true
|
||||||
|
property bool disabled: false
|
||||||
|
|
||||||
property int colorId: Utils.colorIdForPubkey(pubkey)
|
property int colorId: Utils.colorIdForPubkey(pubkey)
|
||||||
property var colorHash: Utils.getColorHashAsJson(pubkey)
|
property var colorHash: Utils.getColorHashAsJson(pubkey)
|
||||||
|
@ -44,10 +45,12 @@ Loader {
|
||||||
active: root.interactive
|
active: root.interactive
|
||||||
|
|
||||||
sourceComponent: MouseArea {
|
sourceComponent: MouseArea {
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: hoverEnabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
hoverEnabled: true
|
hoverEnabled: !root.disabled
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.clicked()
|
if (!root.disabled) {
|
||||||
|
root.clicked()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ Item {
|
||||||
property string displayName
|
property string displayName
|
||||||
property string localName
|
property string localName
|
||||||
property bool amISender
|
property bool amISender
|
||||||
|
property bool disabled
|
||||||
|
|
||||||
signal clickMessage(bool isProfileClick)
|
signal clickMessage(bool isProfileClick)
|
||||||
|
|
||||||
|
@ -24,15 +25,15 @@ Item {
|
||||||
color: text.startsWith("@") || root.amISender || localName !== "" ? Style.current.blue : Style.current.secondaryText
|
color: text.startsWith("@") || root.amISender || localName !== "" ? Style.current.blue : Style.current.secondaryText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
font.pixelSize: Style.current.secondaryTextFontSize
|
font.pixelSize: Style.current.secondaryTextFontSize
|
||||||
font.underline: root.isHovered
|
font.underline: root.isHovered && !root.disabled
|
||||||
readOnly: true
|
readOnly: true
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
MouseArea {
|
MouseArea {
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: hoverEnabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: !root.disabled
|
||||||
onEntered: {
|
onEntered: {
|
||||||
root.isHovered = true
|
root.isHovered = true
|
||||||
}
|
}
|
||||||
|
@ -40,7 +41,9 @@ Item {
|
||||||
root.isHovered = false
|
root.isHovered = false
|
||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.clickMessage(true);
|
if (!root.disabled) {
|
||||||
|
root.clickMessage(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ Rectangle {
|
||||||
property alias text: bannerText.text
|
property alias text: bannerText.text
|
||||||
property alias buttonText: bannerButton.text
|
property alias buttonText: bannerButton.text
|
||||||
property alias icon: bannerIcon.asset
|
property alias icon: bannerIcon.asset
|
||||||
|
property string buttonTooltipText: ""
|
||||||
|
property bool buttonLoading: false
|
||||||
|
|
||||||
implicitWidth: 272
|
implicitWidth: 272
|
||||||
implicitHeight: 168
|
implicitHeight: 168
|
||||||
|
@ -74,7 +76,17 @@ Rectangle {
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.bottomMargin: 16
|
anchors.bottomMargin: 16
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
onClicked: root.buttonClicked()
|
onClicked: {
|
||||||
|
if (!root.buttonLoading) {
|
||||||
|
root.buttonClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loading: root.buttonLoading
|
||||||
|
|
||||||
|
StatusQControls.StatusToolTip {
|
||||||
|
text: root.buttonTooltipText
|
||||||
|
visible: !!root.buttonTooltipText && bannerButton.loading && bannerButton.hovered
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import QtQuick 2.13
|
import QtQuick 2.14
|
||||||
import QtQuick.Controls 2.13
|
import QtQuick.Controls 2.14
|
||||||
import QtQuick.Layouts 1.13
|
import QtQuick.Layouts 1.13
|
||||||
import QtGraphicalEffects 1.13
|
import QtGraphicalEffects 1.13
|
||||||
|
|
||||||
|
@ -7,8 +7,9 @@ import StatusQ.Core 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
|
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
import "../"
|
|
||||||
import "./"
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
@ -20,8 +21,10 @@ Item {
|
||||||
|
|
||||||
property bool active: false
|
property bool active: false
|
||||||
property int type: ModuleWarning.Danger
|
property int type: ModuleWarning.Danger
|
||||||
|
property int progressValue: -1 // 0..100, -1 not visible
|
||||||
property string text: ""
|
property string text: ""
|
||||||
property alias buttonText: button.text
|
property alias buttonText: button.text
|
||||||
|
property alias closeBtnVisible: closeImg.visible
|
||||||
|
|
||||||
signal clicked()
|
signal clicked()
|
||||||
signal closeClicked()
|
signal closeClicked()
|
||||||
|
@ -49,6 +52,8 @@ Item {
|
||||||
closeButtonMouseArea.clicked(null)
|
closeButtonMouseArea.clicked(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signal linkActivated(string link)
|
||||||
|
|
||||||
implicitHeight: root.active ? content.implicitHeight : 0
|
implicitHeight: root.active ? content.implicitHeight : 0
|
||||||
visible: implicitHeight > 0
|
visible: implicitHeight > 0
|
||||||
|
|
||||||
|
@ -123,14 +128,24 @@ Item {
|
||||||
id: layout
|
id: layout
|
||||||
|
|
||||||
spacing: 12
|
spacing: 12
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.centerIn: parent
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
StatusBaseText {
|
StatusBaseText {
|
||||||
text: root.text
|
text: root.text
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
font.pixelSize: 13
|
font.pixelSize: 13
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
color: Theme.palette.indirectColor1
|
color: Theme.palette.indirectColor1
|
||||||
|
linkColor: color
|
||||||
|
onLinkActivated: root.linkActivated(link)
|
||||||
|
HoverHandler {
|
||||||
|
id: handler1
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
cursorShape: handler1.hovered && parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
|
@ -140,9 +155,9 @@ Item {
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.clicked()
|
root.clicked()
|
||||||
}
|
}
|
||||||
contentItem: Text {
|
contentItem: StatusBaseText {
|
||||||
text: button.text
|
text: button.text
|
||||||
font.pixelSize: 12
|
font.pixelSize: 13
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
font.family: Style.current.baseFont.name
|
font.family: Style.current.baseFont.name
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
@ -163,6 +178,42 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: progressBar.left
|
||||||
|
anchors.rightMargin: Style.current.halfPadding
|
||||||
|
text: qsTr("%1%").arg(progressBar.value)
|
||||||
|
visible: progressBar.visible
|
||||||
|
font.pixelSize: 12
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: Theme.palette.white
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressBar {
|
||||||
|
id: progressBar
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: closeImg.left
|
||||||
|
anchors.rightMargin: Style.current.bigPadding
|
||||||
|
from: 0
|
||||||
|
to: 100
|
||||||
|
visible: root.progressValue > -1
|
||||||
|
value: root.progressValue
|
||||||
|
background: Rectangle {
|
||||||
|
implicitWidth: 64
|
||||||
|
implicitHeight: 8
|
||||||
|
radius: 8
|
||||||
|
color: "transparent"
|
||||||
|
border.width: 1
|
||||||
|
border.color: Theme.palette.white
|
||||||
|
}
|
||||||
|
contentItem: Rectangle {
|
||||||
|
width: progressBar.width*progressBar.position
|
||||||
|
implicitHeight: 8
|
||||||
|
radius: 8
|
||||||
|
color: Theme.palette.white
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StatusIcon {
|
StatusIcon {
|
||||||
id: closeImg
|
id: closeImg
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
@ -185,5 +236,4 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ StatusModal {
|
||||||
property var executeCancel
|
property var executeCancel
|
||||||
property string confirmButtonObjectName: ""
|
property string confirmButtonObjectName: ""
|
||||||
property string btnType: "warn"
|
property string btnType: "warn"
|
||||||
|
property string cancelBtnType: "warn"
|
||||||
property string confirmButtonLabel: qsTr("Confirm")
|
property string confirmButtonLabel: qsTr("Confirm")
|
||||||
property string rejectButtonLabel: qsTr("Reject")
|
property string rejectButtonLabel: qsTr("Reject")
|
||||||
property string cancelButtonLabel: qsTr("Cancel")
|
property string cancelButtonLabel: qsTr("Cancel")
|
||||||
|
@ -81,6 +82,14 @@ StatusModal {
|
||||||
id: cancelButton
|
id: cancelButton
|
||||||
visible: showCancelButton
|
visible: showCancelButton
|
||||||
text: confirmationDialog.cancelButtonLabel
|
text: confirmationDialog.cancelButtonLabel
|
||||||
|
type: {
|
||||||
|
switch (confirmationDialog.cancelBtnType) {
|
||||||
|
case "warn":
|
||||||
|
return StatusBaseButton.Type.Danger
|
||||||
|
default:
|
||||||
|
return StatusBaseButton.Type.Normal
|
||||||
|
}
|
||||||
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (executeCancel && typeof executeCancel === "function") {
|
if (executeCancel && typeof executeCancel === "function") {
|
||||||
executeCancel()
|
executeCancel()
|
||||||
|
@ -106,7 +115,7 @@ StatusModal {
|
||||||
case "warn":
|
case "warn":
|
||||||
return StatusBaseButton.Type.Danger
|
return StatusBaseButton.Type.Danger
|
||||||
default:
|
default:
|
||||||
return StatusBaseButton.Type.Primary
|
return StatusBaseButton.Type.Normal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
text: confirmationDialog.confirmButtonLabel
|
text: confirmationDialog.confirmButtonLabel
|
||||||
|
|
|
@ -62,6 +62,7 @@ Loader {
|
||||||
property string messagePinnedBy: ""
|
property string messagePinnedBy: ""
|
||||||
property var reactionsModel: []
|
property var reactionsModel: []
|
||||||
property string linkUrls: ""
|
property string linkUrls: ""
|
||||||
|
property string messageAttachments: ""
|
||||||
property var transactionParams
|
property var transactionParams
|
||||||
|
|
||||||
// External behavior changers
|
// External behavior changers
|
||||||
|
@ -101,7 +102,7 @@ Loader {
|
||||||
property double prevMsgTimestamp: prevMessageAsJsonObj ? prevMessageAsJsonObj.timestamp : 0
|
property double prevMsgTimestamp: prevMessageAsJsonObj ? prevMessageAsJsonObj.timestamp : 0
|
||||||
property double nextMsgTimestamp: nextMessageAsJsonObj ? nextMessageAsJsonObj.timestamp : 0
|
property double nextMsgTimestamp: nextMessageAsJsonObj ? nextMessageAsJsonObj.timestamp : 0
|
||||||
|
|
||||||
property bool shouldRepeatHeader: ((messageTimestamp - prevMsgTimestamp) / 60 / 1000) > Constants.repeatHeaderInterval
|
property bool shouldRepeatHeader: ((messageTimestamp - prevMsgTimestamp) / 60 / 1000) > Constants.repeatHeaderInterval || isDiscordMessage
|
||||||
|
|
||||||
property bool hasMention: false
|
property bool hasMention: false
|
||||||
property bool stickersLoaded: false
|
property bool stickersLoaded: false
|
||||||
|
@ -111,11 +112,12 @@ Loader {
|
||||||
property int stickerPack: -1
|
property int stickerPack: -1
|
||||||
|
|
||||||
property bool isEmoji: messageContentType === Constants.messageContentType.emojiType
|
property bool isEmoji: messageContentType === Constants.messageContentType.emojiType
|
||||||
property bool isImage: messageContentType === Constants.messageContentType.imageType
|
property bool isImage: messageContentType === Constants.messageContentType.imageType || (isDiscordMessage && messageImage != "")
|
||||||
property bool isAudio: messageContentType === Constants.messageContentType.audioType
|
property bool isAudio: messageContentType === Constants.messageContentType.audioType
|
||||||
property bool isStatusMessage: messageContentType === Constants.messageContentType.systemMessagePrivateGroupType
|
property bool isStatusMessage: messageContentType === Constants.messageContentType.systemMessagePrivateGroupType
|
||||||
property bool isSticker: messageContentType === Constants.messageContentType.stickerType
|
property bool isSticker: messageContentType === Constants.messageContentType.stickerType
|
||||||
property bool isText: messageContentType === Constants.messageContentType.messageType || messageContentType === Constants.messageContentType.editType
|
property bool isDiscordMessage: messageContentType === Constants.messageContentType.discordMessageType
|
||||||
|
property bool isText: messageContentType === Constants.messageContentType.messageType || messageContentType === Constants.messageContentType.editType || isDiscordMessage
|
||||||
property bool isMessage: isEmoji || isImage || isSticker || isText || isAudio
|
property bool isMessage: isEmoji || isImage || isSticker || isText || isAudio
|
||||||
|| messageContentType === Constants.messageContentType.communityInviteType || messageContentType === Constants.messageContentType.transactionType
|
|| messageContentType === Constants.messageContentType.communityInviteType || messageContentType === Constants.messageContentType.transactionType
|
||||||
|
|
||||||
|
@ -411,7 +413,7 @@ Loader {
|
||||||
loadingImageText: qsTr("Loading image...")
|
loadingImageText: qsTr("Loading image...")
|
||||||
errorLoadingImageText: qsTr("Error loading the image")
|
errorLoadingImageText: qsTr("Error loading the image")
|
||||||
resendText: qsTr("Resend")
|
resendText: qsTr("Resend")
|
||||||
pinnedMsgInfoText: qsTr("Pinned by")
|
pinnedMsgInfoText: root.isDiscordMessage ? qsTr("Pinned") : qsTr("Pinned by")
|
||||||
reactionIcons: [
|
reactionIcons: [
|
||||||
Style.svg("emojiReactions/heart"),
|
Style.svg("emojiReactions/heart"),
|
||||||
Style.svg("emojiReactions/thumbsUp"),
|
Style.svg("emojiReactions/thumbsUp"),
|
||||||
|
@ -427,7 +429,7 @@ Loader {
|
||||||
isEdited: root.isEdited
|
isEdited: root.isEdited
|
||||||
hasMention: root.hasMention
|
hasMention: root.hasMention
|
||||||
isPinned: root.pinnedMessage
|
isPinned: root.pinnedMessage
|
||||||
pinnedBy: root.pinnedMessage ? Utils.getContactDetailsAsJson(root.messagePinnedBy).displayName : ""
|
pinnedBy: root.pinnedMessage && !root.isDiscordMessage ? Utils.getContactDetailsAsJson(root.messagePinnedBy).displayName : ""
|
||||||
hasExpired: root.isExpired
|
hasExpired: root.isExpired
|
||||||
reactionsModel: root.reactionsModel
|
reactionsModel: root.reactionsModel
|
||||||
|
|
||||||
|
@ -457,7 +459,8 @@ Loader {
|
||||||
return Utils.setColorAlpha(Style.current.blue, 0.1);
|
return Utils.setColorAlpha(Style.current.blue, 0.1);
|
||||||
return "transparent";
|
return "transparent";
|
||||||
}
|
}
|
||||||
|
profileClickable: !root.isDiscordMessage
|
||||||
|
messageAttachments: root.messageAttachments
|
||||||
|
|
||||||
timestampString: Utils.formatShortTime(timestamp,
|
timestampString: Utils.formatShortTime(timestamp,
|
||||||
localAccountSensitiveSettings.is24hTimeFormat)
|
localAccountSensitiveSettings.is24hTimeFormat)
|
||||||
|
@ -548,6 +551,7 @@ Loader {
|
||||||
|
|
||||||
messageDetails: StatusMessageDetails {
|
messageDetails: StatusMessageDetails {
|
||||||
contentType: delegate.contentType
|
contentType: delegate.contentType
|
||||||
|
messageOriginInfo: isDiscordMessage ? qsTr("Imported from discord") : ""
|
||||||
messageText: root.messageText
|
messageText: root.messageText
|
||||||
messageContent: {
|
messageContent: {
|
||||||
switch (delegate.contentType)
|
switch (delegate.contentType)
|
||||||
|
@ -557,6 +561,9 @@ Loader {
|
||||||
case StatusMessage.ContentType.Image:
|
case StatusMessage.ContentType.Image:
|
||||||
return root.messageImage;
|
return root.messageImage;
|
||||||
}
|
}
|
||||||
|
if (root.isDiscordMessage && root.messageImage != "") {
|
||||||
|
return root.messageImage
|
||||||
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -571,9 +578,11 @@ Loader {
|
||||||
width: 40
|
width: 40
|
||||||
height: 40
|
height: 40
|
||||||
name: root.senderIcon || ""
|
name: root.senderIcon || ""
|
||||||
|
assetSettings.isImage: root.isDiscordMessage
|
||||||
pubkey: root.senderId
|
pubkey: root.senderId
|
||||||
colorId: Utils.colorIdForPubkey(root.senderId)
|
colorId: Utils.colorIdForPubkey(root.senderId)
|
||||||
colorHash: Utils.getColorHashAsJson(root.senderId)
|
colorHash: Utils.getColorHashAsJson(root.senderId)
|
||||||
|
showRing: !root.isDiscordMessage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -602,6 +611,8 @@ Loader {
|
||||||
width: 20
|
width: 20
|
||||||
height: 20
|
height: 20
|
||||||
name: delegate.replyMessage ? delegate.replyMessage.senderIcon : ""
|
name: delegate.replyMessage ? delegate.replyMessage.senderIcon : ""
|
||||||
|
assetSettings.isImage: delegate.replyMessage && delegate.replyMessage.messageContentType == Constants.discordMessageType
|
||||||
|
showRing: delegate.replyMessage && delegate.replyMessage.messageContentType != Constants.discordMessageType
|
||||||
pubkey: delegate.replySenderId
|
pubkey: delegate.replySenderId
|
||||||
colorId: Utils.colorIdForPubkey(delegate.replySenderId)
|
colorId: Utils.colorIdForPubkey(delegate.replySenderId)
|
||||||
colorHash: Utils.getColorHashAsJson(delegate.replySenderId)
|
colorHash: Utils.getColorHashAsJson(delegate.replySenderId)
|
||||||
|
|
|
@ -232,6 +232,7 @@ QtObject {
|
||||||
readonly property int communityInviteType: 9
|
readonly property int communityInviteType: 9
|
||||||
readonly property int gapType: 10
|
readonly property int gapType: 10
|
||||||
readonly property int editType: 11
|
readonly property int editType: 11
|
||||||
|
readonly property int discordMessageType: 12
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property QtObject profilePicturesVisibility: QtObject {
|
readonly property QtObject profilePicturesVisibility: QtObject {
|
||||||
|
|
Loading…
Reference in New Issue