parent
b22b632b2d
commit
e106be9b12
|
@ -107,3 +107,10 @@ QtObject:
|
|||
let index = self.createIndex(idx, 0, nil)
|
||||
self.items[idx].selected = true
|
||||
self.dataChanged(index, index, @[ModelRole.Selected.int])
|
||||
|
||||
proc selectOneItem*(self: DiscordCategoriesModel, id: string) =
|
||||
for i in 0 ..< self.items.len:
|
||||
let index = self.createIndex(i, 0, nil)
|
||||
defer: index.delete
|
||||
self.items[i].selected = self.items[i].getId() == id
|
||||
self.dataChanged(index, index, @[ModelRole.Selected.int])
|
||||
|
|
|
@ -214,3 +214,11 @@ QtObject:
|
|||
self.items[idx].selected = true
|
||||
self.dataChanged(index, index, @[ModelRole.Selected.int])
|
||||
self.hasSelectedItemsChanged()
|
||||
|
||||
proc selectOneItem*(self: DiscordChannelsModel, id: string) =
|
||||
for i in 0 ..< self.items.len:
|
||||
let index = self.createIndex(i, 0, nil)
|
||||
defer: index.delete
|
||||
self.items[i].selected = self.items[i].getId() == id
|
||||
self.dataChanged(index, index, @[ModelRole.Selected.int])
|
||||
self.hasSelectedItemsChanged()
|
||||
|
|
|
@ -47,6 +47,8 @@ QtObject:
|
|||
discordDataExtractionInProgress: bool
|
||||
discordImportCommunityId: string
|
||||
discordImportCommunityName: string
|
||||
discordImportChannelId: string
|
||||
discordImportChannelName: string
|
||||
discordImportCommunityImage: string
|
||||
discordImportHasCommunityImage: bool
|
||||
downloadingCommunityHistoryArchives: bool
|
||||
|
@ -481,6 +483,8 @@ QtObject:
|
|||
self.setDiscordImportWarningsCount(0)
|
||||
self.setDiscordImportCommunityId("")
|
||||
self.setDiscordImportCommunityName("")
|
||||
self.discordImportChannelId = ""
|
||||
self.discordImportChannelName = ""
|
||||
self.setDiscordImportCommunityImage("")
|
||||
self.setDiscordImportHasCommunityImage(false)
|
||||
self.setDiscordImportInProgress(false)
|
||||
|
@ -609,6 +613,30 @@ QtObject:
|
|||
if self.discordChannelsModel.allChannelsByCategoryUnselected(item.getCategoryId()):
|
||||
self.discordCategoriesModel.unselectItem(item.getCategoryId())
|
||||
|
||||
proc discordImportChannelChanged*(self: View) {.signal.}
|
||||
|
||||
proc toggleOneDiscordChannel*(self: View, id: string) {.slot.} =
|
||||
let item = self.discordChannelsModel.getItem(id)
|
||||
self.discordChannelsModel.selectOneItem(id)
|
||||
self.discordCategoriesModel.selectOneItem(item.getCategoryId())
|
||||
self.discordImportChannelId = id
|
||||
self.discordImportChannelName = item.getName()
|
||||
self.discordImportChannelChanged()
|
||||
|
||||
proc getDiscordImportChannelId(self: View): string {.slot.} =
|
||||
return self.discordImportChannelId
|
||||
|
||||
QtProperty[string] discordImportChannelId:
|
||||
read = getDiscordImportChannelId
|
||||
notify = discordImportChannelChanged
|
||||
|
||||
proc getDiscordImportChannelName(self: View): string {.slot.} =
|
||||
return self.discordImportChannelName
|
||||
|
||||
QtProperty[string] discordImportChannelName:
|
||||
read = getDiscordImportChannelName
|
||||
notify = discordImportChannelChanged
|
||||
|
||||
proc tokenListModel*(self: View): TokenListModel =
|
||||
result = self.tokenListModel
|
||||
|
||||
|
@ -707,4 +735,4 @@ QtObject:
|
|||
|
||||
QtProperty[bool] requirementsCheckPending:
|
||||
read = getCheckingPermissionsInProgress
|
||||
notify = checkingPermissionsInProgressChanged
|
||||
notify = checkingPermissionsInProgressChanged
|
||||
|
|
|
@ -724,7 +724,7 @@ QtObject:
|
|||
return
|
||||
|
||||
if not self.communities.hasKey(communityId):
|
||||
error "requested community doesn't exists", communityId
|
||||
error "requested community doesn't exist", communityId
|
||||
return
|
||||
|
||||
return self.communities[communityId]
|
||||
|
@ -1004,10 +1004,10 @@ QtObject:
|
|||
|
||||
if response.error != nil:
|
||||
let error = Json.decode($response.error, RpcError)
|
||||
raise newException(RpcException, "Error creating community: " & error.message)
|
||||
raise newException(RpcException, "Error importing discord community: " & error.message)
|
||||
|
||||
except Exception as e:
|
||||
error "Error creating community", msg = e.msg
|
||||
error "Error importing discord community", msg = e.msg
|
||||
|
||||
proc createCommunity*(
|
||||
self: Service,
|
||||
|
@ -1941,7 +1941,7 @@ QtObject:
|
|||
try:
|
||||
discard status_go.requestCancelDiscordCommunityImport(communityId)
|
||||
except Exception as e:
|
||||
error "Error extracting discord channels and categories", msg = e.msg
|
||||
error "Error canceling discord community import", msg = e.msg
|
||||
|
||||
proc createOrEditCommunityTokenPermission*(self: Service, communityId: string, tokenPermission: CommunityTokenPermissionDto) =
|
||||
try:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import QtQuick 2.14
|
||||
import QtQuick.Controls 2.14
|
||||
import QtQuick.Layouts 1.14
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQml 2.15
|
||||
|
||||
import Storybook 1.0
|
||||
|
||||
|
@ -13,56 +14,161 @@ SplitView {
|
|||
|
||||
orientation: Qt.Vertical
|
||||
|
||||
property var dialog
|
||||
|
||||
function createAndOpenDialog() {
|
||||
dialog = dlgComponent.createObject(popupBg)
|
||||
dialog.open()
|
||||
}
|
||||
|
||||
Component.onCompleted: createAndOpenDialog()
|
||||
|
||||
Item {
|
||||
SplitView.fillWidth: true
|
||||
SplitView.fillHeight: true
|
||||
|
||||
PopupBackground {
|
||||
id: popupBg
|
||||
anchors.fill: parent
|
||||
|
||||
Button {
|
||||
anchors.centerIn: parent
|
||||
text: "Reopen"
|
||||
|
||||
onClicked: createAndOpenDialog()
|
||||
}
|
||||
}
|
||||
|
||||
CreateChannelPopup {
|
||||
anchors.centerIn: parent
|
||||
modal: false
|
||||
closePolicy: Popup.NoAutoClose
|
||||
Component {
|
||||
id: dlgComponent
|
||||
CreateChannelPopup {
|
||||
id: dialog
|
||||
anchors.centerIn: parent
|
||||
modal: false
|
||||
closePolicy: Popup.NoAutoClose
|
||||
destroyOnClose: true
|
||||
|
||||
isEdit: isEditCheckBox.checked
|
||||
isDeleteable: isDeleteableCheckBox.checked
|
||||
isEdit: isEditCheckBox.checked
|
||||
isDeleteable: isDeleteableCheckBox.checked
|
||||
isDiscordImport: isDiscordCheckBox.checked
|
||||
|
||||
emojiPopup: Popup {
|
||||
id: emojiPopup
|
||||
Binding on channelName {
|
||||
value: "test-channel"
|
||||
when: dialog.isEdit
|
||||
restoreMode: Binding.RestoreBindingOrValue
|
||||
}
|
||||
|
||||
parent: root
|
||||
Binding on channelDescription {
|
||||
value: "TEST TEST TEST"
|
||||
when: dialog.isEdit
|
||||
restoreMode: Binding.RestoreBindingOrValue
|
||||
}
|
||||
|
||||
property var emojiSize
|
||||
Binding on channelColor {
|
||||
value: "pink"
|
||||
when: dialog.isEdit
|
||||
restoreMode: Binding.RestoreBindingOrValue
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "😃"
|
||||
onClicked: {
|
||||
emojiPopup.emojiSelected(text, false)
|
||||
emojiPopup.close()
|
||||
communitiesStore: QtObject {
|
||||
property string discordImportChannelName
|
||||
readonly property bool discordImportInProgress: false
|
||||
readonly property bool discordDataExtractionInProgress: false
|
||||
readonly property int discordImportErrorsCount: 0
|
||||
readonly property int discordImportWarningsCount: 0
|
||||
readonly property int discordOldestMessageTimestamp: 0
|
||||
|
||||
property var discordFileList: ListModel {
|
||||
readonly property int selectedCount: count
|
||||
property bool selectedFilesValid
|
||||
}
|
||||
|
||||
property var discordCategoriesModel: ListModel {}
|
||||
|
||||
property var discordChannelsModel: ListModel {
|
||||
property bool hasSelectedItems
|
||||
readonly property int count: 32 // hide the parsing/loading spinner
|
||||
}
|
||||
|
||||
function setFileListItems(filePaths) {
|
||||
for (const filePath of filePaths) {
|
||||
discordFileList.append({"filePath": filePath, errorMessage: ""})
|
||||
}
|
||||
}
|
||||
|
||||
function removeFileListItem(path) {
|
||||
for (let i = 0; i < discordFileList.count; i++) {
|
||||
const item = discordFileList.get(i)
|
||||
if (item.filePath === path)
|
||||
discordFileList.remove(i)
|
||||
}
|
||||
}
|
||||
|
||||
function clearFileList() {
|
||||
discordFileList.clear()
|
||||
discordFileList.selectedFilesValid = false
|
||||
}
|
||||
|
||||
function clearDiscordCategoriesAndChannels() {
|
||||
discordCategoriesModel.clear()
|
||||
discordChannelsModel.clear()
|
||||
discordChannelsModel.hasSelectedItems = false
|
||||
}
|
||||
|
||||
function requestExtractChannelsAndCategories() {
|
||||
discordFileList.selectedFilesValid = true
|
||||
}
|
||||
|
||||
function toggleOneDiscordChannel(id) {
|
||||
logs.logEvent("toggleOneDiscordChannel", ["id"], arguments)
|
||||
}
|
||||
|
||||
function resetDiscordImport() {
|
||||
logs.logEvent("resetDiscordImport")
|
||||
}
|
||||
|
||||
function requestCancelDiscordChannelImport(id) {
|
||||
logs.logEvent("requestCancelDiscordChannelImport", ["id"], arguments)
|
||||
}
|
||||
|
||||
function requestImportDiscordChannel(args, timestamp) {
|
||||
logs.logEvent("requestImportDiscordChannel", ["args", "timestamp"], arguments)
|
||||
}
|
||||
}
|
||||
|
||||
signal emojiSelected(string emoji, bool atCu)
|
||||
emojiPopup: Popup {
|
||||
id: inner_emojiPopup
|
||||
|
||||
parent: root
|
||||
|
||||
property var emojiSize
|
||||
|
||||
Button {
|
||||
text: "😃"
|
||||
onClicked: {
|
||||
emojiPopup.emojiSelected(text, false)
|
||||
emojiPopup.close()
|
||||
}
|
||||
}
|
||||
|
||||
signal emojiSelected(string emoji, bool atCu)
|
||||
}
|
||||
|
||||
|
||||
onCreateCommunityChannel: function(chName, chDescription, chEmoji, chColor, chCategoryId) {
|
||||
logs.logEvent("onCreateCommunityChannel",
|
||||
["chName", "chDescription", "chEmoji", "chColor", "chCategoryId"], arguments)
|
||||
}
|
||||
|
||||
onEditCommunityChannel: function(chName, chDescription, chEmoji, chColor, chCategoryId) {
|
||||
logs.logEvent("onEditCommunityChannel",
|
||||
["chName", "chDescription", "chEmoji", "chColor", "chCategoryId"], arguments)
|
||||
}
|
||||
|
||||
onDeleteCommunityChannel: () => {
|
||||
logs.logEvent("onDeleteCommunityChannel")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onCreateCommunityChannel: function(chName, chDescription, chEmoji, chColor, chCategoryId) {
|
||||
logs.logEvent("onCreateCommunityChannel",
|
||||
["chName", "chDescription", "chEmoji", "chColor", "chCategoryId"], arguments)
|
||||
}
|
||||
|
||||
onEditCommunityChannel: function(chName, chDescription, chEmoji, chColor, chCategoryId) {
|
||||
logs.logEvent("onEditCommunityChannel",
|
||||
["chName", "chDescription", "chEmoji", "chColor", "chCategoryId"], arguments)
|
||||
}
|
||||
|
||||
onDeleteCommunityChannel: () => {
|
||||
logs.logEvent("onDeleteCommunityChannel")
|
||||
}
|
||||
|
||||
Component.onCompleted: open()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,15 +181,23 @@ SplitView {
|
|||
RowLayout {
|
||||
CheckBox {
|
||||
id: isEditCheckBox
|
||||
|
||||
text: "isEdit"
|
||||
onToggled: if (checked) isDiscordCheckBox.checked = false
|
||||
}
|
||||
CheckBox {
|
||||
id: isDeleteableCheckBox
|
||||
|
||||
enabled: isEditCheckBox.checked
|
||||
text: "isDeleteable"
|
||||
}
|
||||
CheckBox {
|
||||
id: isDiscordCheckBox
|
||||
enabled: !isEditCheckBox.checked
|
||||
text: "isDiscordImport"
|
||||
onToggled: {
|
||||
if (!!dialog && dialog.opened)
|
||||
dialog.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ StackLayout {
|
|||
property var createChatPropertiesStore
|
||||
readonly property var contactsStore: rootStore.contactsStore
|
||||
readonly property var permissionsStore: rootStore.permissionsStore
|
||||
property var communitiesStore
|
||||
|
||||
property var sectionItemModel
|
||||
|
||||
|
@ -118,6 +119,7 @@ StackLayout {
|
|||
contactsStore: root.contactsStore
|
||||
rootStore: root.rootStore
|
||||
createChatPropertiesStore: root.createChatPropertiesStore
|
||||
communitiesStore: root.communitiesStore
|
||||
sectionItemModel: root.sectionItemModel
|
||||
amIMember: chatItem.amIMember
|
||||
amISectionAdmin: root.sectionItemModel.memberRole === Constants.memberRole.owner ||
|
||||
|
|
|
@ -30,6 +30,7 @@ StatusSectionLayout {
|
|||
|
||||
property RootStore rootStore
|
||||
property var createChatPropertiesStore
|
||||
property var communitiesStore
|
||||
property var sectionItemModel
|
||||
|
||||
property var emojiPopup
|
||||
|
@ -238,6 +239,7 @@ StatusSectionLayout {
|
|||
communitySectionModule: root.rootStore.chatCommunitySectionModule
|
||||
communityData: sectionItemModel
|
||||
store: root.rootStore
|
||||
communitiesStore: root.communitiesStore
|
||||
emojiPopup: root.emojiPopup
|
||||
hasAddedContacts: root.hasAddedContacts
|
||||
onInfoButtonClicked: root.communityInfoButtonClicked()
|
||||
|
|
|
@ -14,15 +14,18 @@ import StatusQ.Controls 0.1
|
|||
import StatusQ.Controls.Validators 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
import StatusQ.Popups.Dialog 0.1
|
||||
|
||||
StatusDialog {
|
||||
import AppLayouts.Communities.controls 1.0
|
||||
|
||||
StatusStackModal {
|
||||
id: root
|
||||
width: 480
|
||||
height: 509
|
||||
|
||||
property var communitiesStore
|
||||
|
||||
property bool isDiscordImport // creating new or importing from discord?
|
||||
property bool isEdit: false
|
||||
property bool isDeleteable: false
|
||||
|
||||
property string chatId: ""
|
||||
property string categoryId: ""
|
||||
property string channelName: ""
|
||||
|
@ -41,8 +44,15 @@ StatusDialog {
|
|||
signal editCommunityChannel(string chName, string chDescription, string chEmoji, string chColor, string chCategoryId)
|
||||
signal deleteCommunityChannel()
|
||||
|
||||
width: 640
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
function isFormValid() {
|
||||
return nameInput.valid && descriptionTextArea.valid &&
|
||||
Utils.validateAndReturnError(colorDialog.color.toString().toUpperCase(), communityColorValidator) === ""
|
||||
}
|
||||
|
||||
function openEmojiPopup(leftSide = false) {
|
||||
root.emojiPopupOpened = true;
|
||||
root.emojiPopup.open();
|
||||
|
@ -50,17 +60,117 @@ StatusDialog {
|
|||
root.emojiPopup.x = leftSide ? root.x + Style.current.padding : (root.x + (root.width - root.emojiPopup.width - Style.current.padding));
|
||||
root.emojiPopup.y = root.y + root.header.height + root.topPadding + nameInput.height + Style.current.smallPadding;
|
||||
}
|
||||
|
||||
function _getChannelConfig() {
|
||||
return {
|
||||
discordChannelId: root.communitiesStore.discordImportChannelId,
|
||||
categoryId: root.categoryId,
|
||||
name: StatusQUtils.Utils.filterXSS(nameInput.input.text),
|
||||
description: StatusQUtils.Utils.filterXSS(descriptionTextArea.text),
|
||||
color: colorDialog.color.toString().toUpperCase(),
|
||||
emoji: StatusQUtils.Emoji.deparse(nameInput.input.asset.emoji),
|
||||
options: {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function requestImportDiscordChannel() {
|
||||
const error = root.communitiesStore.requestImportDiscordChannel(_getChannelConfig(), datePicker.selectedDate.valueOf()/1000)
|
||||
if (error) {
|
||||
creatingError.text = error.error
|
||||
creatingError.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
title: qsTr("New channel")
|
||||
padding: 0
|
||||
stackTitle: isDiscordImport ? qsTr("New Channel With Imported Chat History") :
|
||||
isEdit ? qsTr("Edit #%1").arg(root.channelName)
|
||||
: qsTr("New channel")
|
||||
|
||||
nextButton: StatusButton {
|
||||
objectName: "createChannelNextBtn"
|
||||
font.weight: Font.Medium
|
||||
text: typeof currentItem.nextButtonText !== "undefined" ? currentItem.nextButtonText : qsTr("Import chat history")
|
||||
enabled: typeof(currentItem.canGoNext) == "undefined" || currentItem.canGoNext
|
||||
loading: root.communitiesStore.discordDataExtractionInProgress
|
||||
onClicked: {
|
||||
const nextAction = currentItem.nextAction
|
||||
if (typeof(nextAction) == "function") {
|
||||
return nextAction()
|
||||
}
|
||||
root.currentIndex++
|
||||
}
|
||||
}
|
||||
|
||||
finishButton: StatusButton {
|
||||
objectName: "createOrEditCommunityChannelBtn"
|
||||
font.weight: Font.Medium
|
||||
text: isDiscordImport ? qsTr("Import chat history") : isEdit ? qsTr("Save changes") : qsTr("Create channel")
|
||||
enabled: typeof(currentItem.canGoNext) == "undefined" || currentItem.canGoNext
|
||||
onClicked: {
|
||||
let nextAction = currentItem.nextAction
|
||||
if (typeof (nextAction) == "function") {
|
||||
return nextAction()
|
||||
}
|
||||
if (!root.isDiscordImport) {
|
||||
if (!d.isFormValid()) {
|
||||
scrollView.scrollBackUp()
|
||||
return
|
||||
}
|
||||
let emoji = StatusQUtils.Emoji.deparse(nameInput.input.asset.emoji)
|
||||
|
||||
if (!isEdit) {
|
||||
root.createCommunityChannel(StatusQUtils.Utils.filterXSS(nameInput.input.text),
|
||||
StatusQUtils.Utils.filterXSS(descriptionTextArea.text),
|
||||
emoji,
|
||||
colorDialog.color.toString().toUpperCase(),
|
||||
root.categoryId)
|
||||
} else {
|
||||
root.editCommunityChannel(StatusQUtils.Utils.filterXSS(nameInput.input.text),
|
||||
StatusQUtils.Utils.filterXSS(descriptionTextArea.text),
|
||||
emoji,
|
||||
colorDialog.color.toString().toUpperCase(),
|
||||
root.categoryId)
|
||||
}
|
||||
// TODO Open the channel once we have designs for it
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readonly property StatusButton clearFilesButton: StatusButton {
|
||||
font.weight: Font.Medium
|
||||
text: qsTr("Clear all")
|
||||
type: StatusBaseButton.Type.Danger
|
||||
visible: typeof currentItem.isFileListView !== "undefined" && currentItem.isFileListView
|
||||
enabled: !fileListView.fileListModelEmpty && !root.communitiesStore.discordDataExtractionInProgress
|
||||
onClicked: root.communitiesStore.clearFileList()
|
||||
}
|
||||
|
||||
readonly property StatusButton deleteChannelButton: StatusButton {
|
||||
objectName: "deleteCommunityChannelBtn"
|
||||
visible: isEdit && isDeleteable && !isDiscordImport
|
||||
text: qsTr("Delete channel")
|
||||
type: StatusBaseButton.Type.Danger
|
||||
onClicked: root.deleteCommunityChannel()
|
||||
}
|
||||
|
||||
rightButtons: [clearFilesButton, deleteChannelButton, nextButton, finishButton]
|
||||
|
||||
onAboutToShow: {
|
||||
if (root.isDiscordImport) {
|
||||
if (!root.communitiesStore.discordImportInProgress) {
|
||||
root.communitiesStore.clearFileList()
|
||||
root.communitiesStore.clearDiscordCategoriesAndChannels()
|
||||
}
|
||||
for (let i = 0; i < discordPages.length; i++) {
|
||||
stackItems.push(discordPages[i])
|
||||
}
|
||||
}
|
||||
|
||||
onOpened: {
|
||||
nameInput.text = ""
|
||||
nameInput.input.asset.emoji = ""
|
||||
nameInput.input.edit.forceActiveFocus(Qt.MouseFocusReason)
|
||||
if (isEdit) {
|
||||
root.title = qsTr("Edit #%1").arg(root.channelName);
|
||||
nameInput.text = root.channelName
|
||||
descriptionTextArea.text = root.channelDescription
|
||||
if (root.channelEmoji) {
|
||||
|
@ -70,8 +180,300 @@ StatusDialog {
|
|||
} else {
|
||||
nameInput.input.asset.isLetterIdenticon = true;
|
||||
}
|
||||
|
||||
updateRightButtons()
|
||||
}
|
||||
|
||||
readonly property list<Item> discordPages: [
|
||||
ColumnLayout {
|
||||
id: fileListView
|
||||
spacing: 24
|
||||
|
||||
readonly property bool isFileListView: true
|
||||
|
||||
readonly property var fileListModel: root.communitiesStore.discordFileList
|
||||
readonly property bool fileListModelEmpty: !fileListModel.count
|
||||
|
||||
readonly property bool canGoNext: fileListModel.selectedCount
|
||||
|| (fileListModel.selectedCount && fileListModel.selectedFilesValid)
|
||||
readonly property string nextButtonText: fileListModel.selectedCount && fileListModel.selectedFilesValid ?
|
||||
qsTr("Proceed with (%1/%2) files").arg(fileListModel.selectedCount).arg(fileListModel.count) :
|
||||
fileListModel.selectedCount && fileListModel.selectedCount === fileListModel.count ? qsTr("Validate %n file(s)", "", fileListModel.selectedCount)
|
||||
: fileListModel.selectedCount ? qsTr("Validate (%1/%2) files").arg(fileListModel.selectedCount).arg(fileListModel.count)
|
||||
: qsTr("Start channel import")
|
||||
readonly property var nextAction: function () {
|
||||
if (!fileListView.fileListModel.selectedFilesValid)
|
||||
return root.communitiesStore.requestExtractChannelsAndCategories()
|
||||
|
||||
root.currentIndex++
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 12
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
maximumLineCount: 2
|
||||
wrapMode: Text.Wrap
|
||||
elide: Text.ElideRight
|
||||
text: fileListView.fileListModelEmpty ? qsTr("Select Discord channel JSON files to import") :
|
||||
root.communitiesStore.discordImportErrorsCount ? qsTr("Some of your community files cannot be used") :
|
||||
qsTr("Uncheck any files you would like to exclude from the import")
|
||||
}
|
||||
StatusBaseText {
|
||||
visible: fileListView.fileListModelEmpty && !issuePill.visible
|
||||
font.pixelSize: 12
|
||||
color: Theme.palette.baseColor1
|
||||
text: qsTr("(JSON file format only)")
|
||||
}
|
||||
IssuePill {
|
||||
id: issuePill
|
||||
type: root.communitiesStore.discordImportErrorsCount ? IssuePill.Type.Error : IssuePill.Type.Warning
|
||||
count: root.communitiesStore.discordImportErrorsCount || root.communitiesStore.discordImportWarningsCount || 0
|
||||
visible: !!count && !fileListView.fileListModelEmpty
|
||||
}
|
||||
StatusButton {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: qsTr("Browse files")
|
||||
type: StatusBaseButton.Type.Primary
|
||||
onClicked: fileDialog.open()
|
||||
enabled: !root.communitiesStore.discordDataExtractionInProgress
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
color: Theme.palette.baseColor4
|
||||
|
||||
ColumnLayout {
|
||||
visible: fileListView.fileListModelEmpty
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 60
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 8
|
||||
|
||||
StatusRoundIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
asset.name: "info"
|
||||
}
|
||||
StatusBaseText {
|
||||
Layout.topMargin: 8
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
text: qsTr("Export the Discord channel’s chat history data using %1").arg("<a href='https://github.com/Tyrrrz/DiscordChatExporter/releases/tag/2.40.4'>DiscordChatExporter</a>")
|
||||
onLinkActivated: Global.openLink(link)
|
||||
HoverHandler {
|
||||
id: handler1
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: handler1.hovered && parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
text: qsTr("Refer to this <a href='https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Readme.md'>documentation</a> if you have any queries")
|
||||
onLinkActivated: Global.openLink(link)
|
||||
HoverHandler {
|
||||
id: handler2
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: handler2.hovered && parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: floatingDivComp
|
||||
Rectangle {
|
||||
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
|
||||
width: ListView.view ? ListView.view.width : 0
|
||||
height: 4
|
||||
color: Theme.palette.directColor8
|
||||
}
|
||||
}
|
||||
|
||||
StatusListView {
|
||||
visible: !fileListView.fileListModelEmpty
|
||||
enabled: !root.communitiesStore.discordDataExtractionInProgress
|
||||
anchors.fill: parent
|
||||
leftMargin: 8
|
||||
rightMargin: 8
|
||||
model: fileListView.fileListModel
|
||||
header: !atYBeginning ? floatingDivComp : null
|
||||
headerPositioning: ListView.OverlayHeader
|
||||
footer: !atYEnd ? floatingDivComp : null
|
||||
footerPositioning: ListView.OverlayHeader
|
||||
delegate: ColumnLayout {
|
||||
width: ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin
|
||||
RowLayout {
|
||||
spacing: 20
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
text: model.filePath
|
||||
font.pixelSize: 13
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: 2
|
||||
}
|
||||
|
||||
StatusFlatRoundButton {
|
||||
Layout.preferredWidth: 32
|
||||
Layout.preferredHeight: 32
|
||||
type: StatusFlatRoundButton.Type.Secondary
|
||||
icon.name: "close"
|
||||
icon.color: Theme.palette.directColor1
|
||||
icon.width: 24
|
||||
icon.height: 24
|
||||
onClicked: root.communitiesStore.removeFileListItem(model.filePath)
|
||||
}
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
text: "%1 %2".arg("⚠").arg(model.errorMessage)
|
||||
visible: model.errorMessage
|
||||
font.pixelSize: 13
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideMiddle
|
||||
color: Theme.palette.dangerColor1
|
||||
verticalAlignment: Qt.AlignTop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: fileDialog
|
||||
title: qsTr("Choose files to import")
|
||||
selectMultiple: true
|
||||
nameFilters: [qsTr("JSON files (%1)").arg("*.json *.JSON")]
|
||||
onAccepted: {
|
||||
if (fileDialog.fileUrls.length > 0) {
|
||||
const files = []
|
||||
for (let i = 0; i < fileDialog.fileUrls.length; i++)
|
||||
files.push(decodeURI(fileDialog.fileUrls[i].toString()))
|
||||
root.communitiesStore.setFileListItems(files)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ColumnLayout {
|
||||
id: categoriesAndChannelsView
|
||||
spacing: 24
|
||||
|
||||
readonly property bool canGoNext: root.communitiesStore.discordChannelsModel.hasSelectedItems
|
||||
readonly property var nextAction: function () {
|
||||
d.requestImportDiscordChannel()
|
||||
// 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.communitiesStore
|
||||
importingSingleChannel: true
|
||||
onClose: root.close()
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
visible: !root.communitiesStore.discordChannelsModel.count
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
active: parent.visible
|
||||
sourceComponent: StatusLoadingIndicator {
|
||||
width: 50
|
||||
height: 50
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 12
|
||||
visible: root.communitiesStore.discordChannelsModel.count
|
||||
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Select the chat history you would like to import into #%1...").arg(StatusQUtils.Utils.filterXSS(nameInput.input.text))
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 20
|
||||
Layout.fillWidth: true
|
||||
StatusRadioButton {
|
||||
text: qsTr("Import all history")
|
||||
checked: true
|
||||
}
|
||||
StatusRadioButton {
|
||||
id: startDateRadio
|
||||
text: qsTr("Start date")
|
||||
}
|
||||
StatusDatePicker {
|
||||
id: datePicker
|
||||
Layout.fillWidth: true
|
||||
selectedDate: new Date(root.communitiesStore.discordOldestMessageTimestamp * 1000)
|
||||
enabled: startDateRadio.checked
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
color: Theme.palette.baseColor4
|
||||
|
||||
StatusListView {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
model: root.communitiesStore.discordCategoriesModel
|
||||
delegate: ColumnLayout {
|
||||
width: ListView.view.width
|
||||
spacing: 8
|
||||
|
||||
StatusBaseText {
|
||||
readonly property string categoryId: model.id
|
||||
id: categoryCheckbox
|
||||
text: model.name
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 24
|
||||
Repeater {
|
||||
Layout.fillWidth: true
|
||||
model: root.communitiesStore.discordChannelsModel
|
||||
delegate: StatusRadioButton {
|
||||
width: parent.width
|
||||
text: model.name
|
||||
checked: model.selected
|
||||
visible: model.categoryId === categoryCheckbox.categoryId
|
||||
onToggled: root.communitiesStore.toggleOneDiscordChannel(model.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Connections {
|
||||
enabled: root.opened && root.emojiPopupOpened
|
||||
target: emojiPopup
|
||||
|
@ -85,278 +487,157 @@ StatusDialog {
|
|||
}
|
||||
}
|
||||
|
||||
function isFormValid() {
|
||||
return (nameInput.valid &&
|
||||
descriptionTextArea.valid) &&
|
||||
Utils.validateAndReturnError(colorDialog.color.toString().toUpperCase(),
|
||||
communityColorValidator) === ""
|
||||
}
|
||||
stackItems: [
|
||||
StatusScrollView {
|
||||
id: scrollView
|
||||
|
||||
StatusScrollView {
|
||||
id: scrollView
|
||||
readonly property bool canGoNext: d.isFormValid()
|
||||
|
||||
property ScrollBar vScrollBar: ScrollBar.vertical
|
||||
property ScrollBar vScrollBar: ScrollBar.vertical
|
||||
|
||||
anchors.fill: parent
|
||||
contentWidth: availableWidth
|
||||
padding: 16
|
||||
contentWidth: availableWidth
|
||||
padding: 0
|
||||
|
||||
function scrollBackUp() {
|
||||
vScrollBar.setPosition(0)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
width: scrollView.availableWidth
|
||||
spacing: 0
|
||||
|
||||
StatusInput {
|
||||
id: nameInput
|
||||
Layout.fillWidth: true
|
||||
input.edit.objectName: "createOrEditCommunityChannelNameInput"
|
||||
label: qsTr("Channel name")
|
||||
charLimit: root.maxChannelNameLength
|
||||
placeholderText: qsTr("# Name the channel")
|
||||
|
||||
input.onTextChanged: {
|
||||
const cursorPosition = input.cursorPosition
|
||||
input.text = Utils.convertSpacesToDashes(input.text)
|
||||
input.cursorPosition = cursorPosition
|
||||
if (root.channelEmoji === "") {
|
||||
input.letterIconName = text;
|
||||
}
|
||||
}
|
||||
input.asset.color: colorDialog.color.toString()
|
||||
input.rightComponent: StatusRoundButton {
|
||||
objectName: "StatusChannelPopup_emojiButton"
|
||||
implicitWidth: 32
|
||||
implicitHeight: 32
|
||||
icon.width: 20
|
||||
icon.height: 20
|
||||
icon.name: "smiley"
|
||||
onClicked: {
|
||||
d.openEmojiPopup();
|
||||
}
|
||||
}
|
||||
onIconClicked: {
|
||||
d.openEmojiPopup(true);
|
||||
}
|
||||
|
||||
validators: [
|
||||
StatusMinLengthValidator {
|
||||
minLength: 1
|
||||
errorMessage: Utils.getErrorMessage(nameInput.errors, qsTr("channel name"))
|
||||
},
|
||||
StatusRegularExpressionValidator {
|
||||
regularExpression: Constants.regularExpressions.alphanumericalExpanded
|
||||
errorMessage: Constants.errorMessages.alphanumericalExpandedRegExp
|
||||
}
|
||||
]
|
||||
function scrollBackUp() {
|
||||
vScrollBar.setPosition(0)
|
||||
}
|
||||
|
||||
Item {
|
||||
id: spacer1
|
||||
height: 16
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Channel colour")
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
width: scrollView.availableWidth
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
id: spacer2
|
||||
height: 8
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
StatusInput {
|
||||
id: nameInput
|
||||
Layout.fillWidth: true
|
||||
input.edit.objectName: "createOrEditCommunityChannelNameInput"
|
||||
label: qsTr("Channel name")
|
||||
charLimit: root.maxChannelNameLength
|
||||
placeholderText: qsTr("# Name the channel")
|
||||
|
||||
Item {
|
||||
height: colorSelectorButton.height + 16
|
||||
Layout.fillWidth: true
|
||||
|
||||
StatusPickerButton {
|
||||
id: colorSelectorButton
|
||||
|
||||
property string validationError: ""
|
||||
|
||||
bgColor: colorDialog.colorSelected ? colorDialog.color : Theme.palette.baseColor2
|
||||
contentColor: colorDialog.colorSelected ? Theme.palette.white : Theme.palette.baseColor1
|
||||
text: colorDialog.colorSelected ?
|
||||
colorDialog.color.toString().toUpperCase() :
|
||||
qsTr("Pick a colour")
|
||||
|
||||
onClicked: colorDialog.open();
|
||||
onTextChanged: {
|
||||
if (colorDialog.colorSelected) {
|
||||
validationError = Utils.validateAndReturnError(text, communityColorValidator)
|
||||
input.onTextChanged: {
|
||||
const cursorPosition = input.cursorPosition
|
||||
input.text = Utils.convertSpacesToDashes(input.text)
|
||||
input.cursorPosition = cursorPosition
|
||||
if (root.channelEmoji === "") {
|
||||
input.letterIconName = text
|
||||
}
|
||||
}
|
||||
input.asset.color: colorDialog.color.toString()
|
||||
input.rightComponent: StatusRoundButton {
|
||||
objectName: "StatusChannelPopup_emojiButton"
|
||||
implicitWidth: 32
|
||||
implicitHeight: 32
|
||||
icon.width: 20
|
||||
icon.height: 20
|
||||
icon.name: "smiley"
|
||||
onClicked: d.openEmojiPopup()
|
||||
}
|
||||
onIconClicked: {
|
||||
d.openEmojiPopup(true);
|
||||
}
|
||||
|
||||
validators: [
|
||||
StatusMinLengthValidator {
|
||||
minLength: 1
|
||||
errorMessage: Utils.getErrorMessage(nameInput.errors, qsTr("channel name"))
|
||||
},
|
||||
StatusRegularExpressionValidator {
|
||||
regularExpression: Constants.regularExpressions.alphanumericalExpanded
|
||||
errorMessage: Constants.errorMessages.alphanumericalExpandedRegExp
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
StatusColorDialog {
|
||||
id: colorDialog
|
||||
anchors.centerIn: parent
|
||||
property bool colorSelected: root.isEdit && root.channelColor
|
||||
headerSettings.title: qsTr("Channel Colour")
|
||||
standardColors: Theme.palette.communityColorsArray
|
||||
color: root.isEdit && root.channelColor ? root.channelColor :
|
||||
Theme.palette.primaryColor1
|
||||
onAccepted: colorSelected = true
|
||||
Item {
|
||||
Layout.preferredHeight: 16
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
text: colorSelectorButton.validationError
|
||||
visible: !!text
|
||||
color: Theme.palette.dangerColor1
|
||||
anchors.top: colorSelectorButton.bottom
|
||||
anchors.topMargin: 4
|
||||
anchors.right: colorSelectorButton.right
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Channel colour")
|
||||
}
|
||||
}
|
||||
|
||||
StatusInput {
|
||||
id: descriptionTextArea
|
||||
Layout.fillWidth: true
|
||||
input.edit.objectName: "createOrEditCommunityChannelDescriptionInput"
|
||||
input.verticalAlignment: TextEdit.AlignTop
|
||||
label: qsTr("Description")
|
||||
charLimit: 140
|
||||
|
||||
placeholderText: qsTr("Describe the channel")
|
||||
input.multiline: true
|
||||
minimumHeight: 88
|
||||
maximumHeight: 88
|
||||
validators: [
|
||||
StatusMinLengthValidator {
|
||||
minLength: 1
|
||||
errorMessage: Utils.getErrorMessage(descriptionTextArea.errors, qsTr("channel description"))
|
||||
},
|
||||
StatusRegularExpressionValidator {
|
||||
regularExpression: Constants.regularExpressions.alphanumericalExpanded
|
||||
errorMessage: Constants.errorMessages.alphanumericalExpandedRegExp
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
/* TODO: use the code below to enable private channels and message limit */
|
||||
/* StatusListItem { */
|
||||
/* width: parent.width */
|
||||
/* height: 56 */
|
||||
/* sensor.enabled: false */
|
||||
/* title: qsTr("Private channel") */
|
||||
/* components: [ */
|
||||
/* StatusSwitch { */
|
||||
/* id: privateSwitch */
|
||||
/* } */
|
||||
/* ] */
|
||||
/* } */
|
||||
|
||||
/* StatusBaseText { */
|
||||
/* width: parent.width - 32 */
|
||||
/* anchors.left: parent.left */
|
||||
/* anchors.right: parent.right */
|
||||
/* anchors.rightMargin: 121 */
|
||||
/* anchors.leftMargin: 16 */
|
||||
/* color: Theme.palette.baseColor1 */
|
||||
/* wrapMode: Text.WordWrap */
|
||||
/* text: qsTr("By making a channel private, only members with selected permission will be able to access it") */
|
||||
/* } */
|
||||
|
||||
/* StatusModalDivider { */
|
||||
/* topPadding: 8 */
|
||||
/* bottomPadding: 8 */
|
||||
/* } */
|
||||
|
||||
/* StatusListItem { */
|
||||
/* width: parent.width */
|
||||
/* height: 56 */
|
||||
/* sensor.enabled: false */
|
||||
/* title: qsTr("Message limit") */
|
||||
/* components: [ */
|
||||
/* StatusSwitch {} */
|
||||
/* ] */
|
||||
/* } */
|
||||
|
||||
/* StatusBaseText { */
|
||||
/* width: parent.width - 32 */
|
||||
/* anchors.left: parent.left */
|
||||
/* anchors.right: parent.right */
|
||||
/* anchors.rightMargin: 121 */
|
||||
/* anchors.leftMargin: 16 */
|
||||
/* color: Theme.palette.baseColor1 */
|
||||
/* wrapMode: Text.WordWrap */
|
||||
/* text: qsTr("Limit channel members to sending one message per chose time interval") */
|
||||
/* } */
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
height: 8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: StatusDialogFooter {
|
||||
rightButtons: ObjectModel {
|
||||
StatusButton {
|
||||
objectName: "deleteCommunityChannelBtn"
|
||||
visible: isEdit && isDeleteable
|
||||
text: qsTr("Delete channel")
|
||||
type: StatusBaseButton.Type.Danger
|
||||
onClicked: {
|
||||
root.deleteCommunityChannel()
|
||||
Item {
|
||||
Layout.preferredHeight: 8
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
StatusButton {
|
||||
objectName: "createOrEditCommunityChannelBtn"
|
||||
enabled: isFormValid()
|
||||
text: isEdit ?
|
||||
qsTr("Save changes") :
|
||||
qsTr("Create channel")
|
||||
onClicked: {
|
||||
if (!isFormValid()) {
|
||||
scrollView.scrollBackUp()
|
||||
return
|
||||
}
|
||||
let error = "";
|
||||
let emoji = StatusQUtils.Emoji.deparse(nameInput.input.asset.emoji)
|
||||
|
||||
if (!isEdit) {
|
||||
//scrollView.communityColor.color.toString().toUpperCase()
|
||||
root.createCommunityChannel(StatusQUtils.Utils.filterXSS(nameInput.input.text),
|
||||
StatusQUtils.Utils.filterXSS(descriptionTextArea.text),
|
||||
emoji,
|
||||
colorDialog.color.toString().toUpperCase(),
|
||||
root.categoryId)
|
||||
} else {
|
||||
root.editCommunityChannel(StatusQUtils.Utils.filterXSS(nameInput.input.text),
|
||||
StatusQUtils.Utils.filterXSS(descriptionTextArea.text),
|
||||
emoji,
|
||||
colorDialog.color.toString().toUpperCase(),
|
||||
root.categoryId)
|
||||
Item {
|
||||
height: colorSelectorButton.height + 16
|
||||
Layout.fillWidth: true
|
||||
|
||||
StatusPickerButton {
|
||||
id: colorSelectorButton
|
||||
|
||||
property string validationError: ""
|
||||
|
||||
width: parent.width
|
||||
bgColor: colorDialog.colorSelected ? colorDialog.color : Theme.palette.baseColor2
|
||||
contentColor: colorDialog.colorSelected ? Theme.palette.white : Theme.palette.baseColor1
|
||||
text: colorDialog.colorSelected ? colorDialog.color.toString().toUpperCase() : qsTr("Pick a colour")
|
||||
|
||||
onClicked: colorDialog.open()
|
||||
onTextChanged: {
|
||||
if (colorDialog.colorSelected) {
|
||||
validationError = Utils.validateAndReturnError(text, communityColorValidator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
const errorJson = JSON.parse(error)
|
||||
creatingError.text = errorJson.error
|
||||
return creatingError.open()
|
||||
StatusColorDialog {
|
||||
id: colorDialog
|
||||
anchors.centerIn: parent
|
||||
property bool colorSelected: root.isEdit && root.channelColor
|
||||
headerSettings.title: qsTr("Channel Colour")
|
||||
standardColors: Theme.palette.communityColorsArray
|
||||
color: root.isEdit && root.channelColor ? root.channelColor : Theme.palette.primaryColor1
|
||||
onAccepted: colorSelected = true
|
||||
}
|
||||
|
||||
// TODO Open the community once we have designs for it
|
||||
root.close()
|
||||
StatusBaseText {
|
||||
text: colorSelectorButton.validationError
|
||||
visible: !!text
|
||||
color: Theme.palette.dangerColor1
|
||||
anchors.top: colorSelectorButton.bottom
|
||||
anchors.topMargin: 4
|
||||
anchors.right: colorSelectorButton.right
|
||||
}
|
||||
}
|
||||
|
||||
StatusInput {
|
||||
id: descriptionTextArea
|
||||
Layout.fillWidth: true
|
||||
input.edit.objectName: "createOrEditCommunityChannelDescriptionInput"
|
||||
input.verticalAlignment: TextEdit.AlignTop
|
||||
label: qsTr("Description")
|
||||
charLimit: 140
|
||||
|
||||
placeholderText: qsTr("Describe the channel")
|
||||
input.multiline: true
|
||||
minimumHeight: 88
|
||||
maximumHeight: 88
|
||||
validators: [
|
||||
StatusMinLengthValidator {
|
||||
minLength: 1
|
||||
errorMessage: Utils.getErrorMessage(descriptionTextArea.errors, qsTr("channel description"))
|
||||
},
|
||||
StatusRegularExpressionValidator {
|
||||
regularExpression: Constants.regularExpressions.alphanumericalExpanded
|
||||
errorMessage: Constants.errorMessages.alphanumericalExpandedRegExp
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
MessageDialog {
|
||||
id: creatingError
|
||||
title: qsTr("Error creating the community")
|
||||
title: qsTr("Error creating the channel")
|
||||
icon: StandardIcon.Critical
|
||||
standardButtons: StandardButton.Ok
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -107,32 +107,28 @@ StatusStackModal {
|
|||
Layout.fillWidth: true
|
||||
spacing: 12
|
||||
StatusBaseText {
|
||||
font.pixelSize: 15
|
||||
Layout.fillWidth: true
|
||||
maximumLineCount: 2
|
||||
wrapMode: Text.Wrap
|
||||
elide: Text.ElideRight
|
||||
text: fileListView.fileListModelEmpty ? qsTr("Select Discord JSON files to import") :
|
||||
root.store.discordImportErrorsCount ? qsTr("Some of your community files cannot be used") :
|
||||
qsTr("Uncheck any files you would like to exclude from the import")
|
||||
}
|
||||
StatusBaseText {
|
||||
visible: fileListView.fileListModelEmpty
|
||||
visible: fileListView.fileListModelEmpty && !issuePill.visible
|
||||
font.pixelSize: 12
|
||||
color: Theme.palette.baseColor1
|
||||
text: qsTr("(JSON file format only)")
|
||||
}
|
||||
IssuePill {
|
||||
type: root.store.discordImportErrorsCount ? IssuePill.Type.Error : IssuePill.Type.Warning
|
||||
count: {
|
||||
if (root.store.discordImportErrorsCount > 0) {
|
||||
return root.store.discordImportErrorsCount
|
||||
}
|
||||
if (root.store.discordImportWarningsCount > 0) {
|
||||
return root.store.discordImportWarningsCount
|
||||
}
|
||||
return 0
|
||||
}
|
||||
visible: !!count
|
||||
id: issuePill
|
||||
type: root.communitiesStore.discordImportErrorsCount ? IssuePill.Type.Error : IssuePill.Type.Warning
|
||||
count: root.communitiesStore.discordImportErrorsCount || root.communitiesStore.discordImportWarningsCount || 0
|
||||
visible: !!count && !fileListView.fileListModelEmpty
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
StatusButton {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: qsTr("Browse files")
|
||||
type: StatusBaseButton.Type.Primary
|
||||
onClicked: fileDialog.open()
|
||||
|
@ -160,8 +156,7 @@ StatusStackModal {
|
|||
Layout.topMargin: 8
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
text: qsTr("Export your Discord JSON data using %1")
|
||||
.arg("<a href='https://github.com/Tyrrrz/DiscordChatExporter'>DiscordChatExporter</a>")
|
||||
text: qsTr("Export your Discord JSON data using %1").arg("<a href='https://github.com/Tyrrrz/DiscordChatExporter/releases/tag/2.40.4'>DiscordChatExporter</a>")
|
||||
onLinkActivated: Global.openLink(link)
|
||||
HoverHandler {
|
||||
id: handler1
|
||||
|
@ -175,7 +170,7 @@ StatusStackModal {
|
|||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
text: qsTr("Refer to this <a href='https://github.com/Tyrrrz/DiscordChatExporter/wiki'>wiki</a> if you have any queries")
|
||||
text: qsTr("Refer to this <a href='https://github.com/Tyrrrz/DiscordChatExporter/blob/master/.docs/Readme.md'>documentation</a> if you have any queries")
|
||||
onLinkActivated: Global.openLink(link)
|
||||
HoverHandler {
|
||||
id: handler2
|
||||
|
|
|
@ -20,6 +20,8 @@ StatusScrollView {
|
|||
|
||||
property var store
|
||||
|
||||
property bool importingSingleChannel
|
||||
|
||||
signal close()
|
||||
|
||||
enum ImportStatus {
|
||||
|
@ -36,9 +38,9 @@ StatusScrollView {
|
|||
visible: d.status === DiscordImportProgressContents.ImportStatus.CompletedWithWarnings ||
|
||||
d.status === DiscordImportProgressContents.ImportStatus.StoppedWithErrors
|
||||
type: StatusButton.Danger
|
||||
text: qsTr("Delete community & restart import")
|
||||
text: root.importingSingleChannel ? qsTr("Delete channel & restart import") : qsTr("Delete community & restart import")
|
||||
onClicked: {
|
||||
// TODO display a confirmation and open CreateCommunityPopup again
|
||||
// TODO display a confirmation and restart the whole flow
|
||||
root.close()
|
||||
}
|
||||
},
|
||||
|
@ -68,10 +70,11 @@ StatusScrollView {
|
|||
StatusButton {
|
||||
visible: d.status === DiscordImportProgressContents.ImportStatus.CompletedSuccessfully ||
|
||||
d.status === DiscordImportProgressContents.ImportStatus.CompletedWithWarnings
|
||||
text: qsTr("Visit your new community")
|
||||
text: root.importingSingleChannel ? qsTr("Visit your new channel") : qsTr("Visit your new community")
|
||||
onClicked: {
|
||||
root.close()
|
||||
root.store.setActiveCommunity(root.store.discordImportCommunityId)
|
||||
if (!root.importingSingleChannel)
|
||||
root.store.setActiveCommunity(root.store.discordImportCommunityId)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -85,11 +88,11 @@ StatusScrollView {
|
|||
readonly property var helperInfo: {
|
||||
"import.communityCreation": {
|
||||
icon: "network",
|
||||
text: qsTr("Setting up your community")
|
||||
text: root.importingSingleChannel ? qsTr("Setting up your new channel") : qsTr("Setting up your community")
|
||||
},
|
||||
"import.channelsCreation": {
|
||||
icon: "channel",
|
||||
text: qsTr("Importing channels")
|
||||
text: root.importingSingleChannel ? qsTr("Importing Discord channel") : qsTr("Importing channels")
|
||||
},
|
||||
"import.importMessages": {
|
||||
icon: "receive",
|
||||
|
@ -101,7 +104,7 @@ StatusScrollView {
|
|||
},
|
||||
"import.initializeCommunity": {
|
||||
icon: "communities",
|
||||
text: qsTr("Initializing community")
|
||||
text: root.importingSingleChannel ? qsTr("Initializing channel") : qsTr("Initializing community")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,7 +140,7 @@ StatusScrollView {
|
|||
if (importStopped)
|
||||
return ""
|
||||
if (progress <= 0.0)
|
||||
return qsTr("Pending...")
|
||||
return qsTr("Pending...")
|
||||
|
||||
return qsTr("Importing from file %1 of %2...").arg(currentChunk).arg(totalChunksCount)
|
||||
}
|
||||
|
@ -279,17 +282,18 @@ StatusScrollView {
|
|||
text: {
|
||||
switch (d.status) {
|
||||
case DiscordImportProgressContents.ImportStatus.InProgress:
|
||||
return qsTr("Importing ‘%1’ from Discord...").arg(root.store.discordImportCommunityName)
|
||||
return qsTr("Importing ‘%1’ from Discord...").arg(root.importingSingleChannel ? root.store.discordImportChannelName : root.store.discordImportCommunityName)
|
||||
case DiscordImportProgressContents.ImportStatus.Stopped:
|
||||
return qsTr("Importing ‘%1’ from Discord stopped...").arg(root.store.discordImportCommunityName)
|
||||
return qsTr("Importing ‘%1’ from Discord stopped...").arg(root.importingSingleChannel ? root.store.discordImportChannelName : root.store.discordImportCommunityName)
|
||||
case DiscordImportProgressContents.ImportStatus.StoppedWithErrors:
|
||||
return qsTr("Importing ‘%1’ stopped due to a critical issue...").arg(root.store.discordImportCommunityName)
|
||||
return qsTr("Importing ‘%1’ stopped due to a critical issue...").arg(root.importingSingleChannel ? root.store.discordImportChannelName : root.store.discordImportCommunityName)
|
||||
case DiscordImportProgressContents.ImportStatus.CompletedWithWarnings:
|
||||
return qsTr("‘%1’ was imported with %n issue(s).", "", root.store.discordImportWarningsCount).arg(root.store.discordImportCommunityName)
|
||||
return qsTr("‘%1’ was imported with %n issue(s).", "", root.store.discordImportWarningsCount)
|
||||
.arg(root.importingSingleChannel ? root.store.discordImportChannelName : root.store.discordImportCommunityName)
|
||||
case DiscordImportProgressContents.ImportStatus.CompletedSuccessfully:
|
||||
return qsTr("‘%1’ was successfully imported from Discord.").arg(root.store.discordImportCommunityName)
|
||||
return qsTr("‘%1’ was successfully imported from Discord.").arg(root.importingSingleChannel ? root.store.discordImportChannelName : root.store.discordImportCommunityName)
|
||||
default:
|
||||
return qsTr("Your Discord community import is in progress...")
|
||||
return qsTr("Your Discord import is in progress...")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -329,7 +333,7 @@ StatusScrollView {
|
|||
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("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 %1 is ready for you.").arg(root.importingSingleChannel ? qsTr("Channel") : qsTr("Community")) :
|
||||
qsTr("If there were any issues with your import you can upload new JSON files via the community page at any time.")
|
||||
}
|
||||
}
|
||||
|
@ -339,13 +343,16 @@ StatusScrollView {
|
|||
ConfirmationDialog {
|
||||
id: cancelConfirmationPopup
|
||||
headerSettings.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.")
|
||||
confirmationText: qsTr("Your new Status %1 will be deleted and all information entered will be lost.").arg(root.importingSingleChannel ? qsTr("channel") : qsTr("community"))
|
||||
showCancelButton: true
|
||||
cancelBtnType: "default"
|
||||
confirmButtonLabel: qsTr("Delete community")
|
||||
cancelButtonLabel: qsTr("Continue importing")
|
||||
confirmButtonLabel: root.importingSingleChannel ? qsTr("Delete channel & cancel import") : qsTr("Delete community")
|
||||
cancelButtonLabel: root.importingSingleChannel ? qsTr("Cancel") : qsTr("Continue importing")
|
||||
onConfirmButtonClicked: {
|
||||
root.store.requestCancelDiscordCommunityImport(root.store.discordImportCommunityId)
|
||||
if (root.importingSingleChannel)
|
||||
root.store.requestCancelDiscordChannelImport(root.store.discordImportChannelName)
|
||||
else
|
||||
root.store.requestCancelDiscordCommunityImport(root.store.discordImportCommunityId)
|
||||
cancelConfirmationPopup.close()
|
||||
root.close()
|
||||
}
|
||||
|
|
|
@ -10,7 +10,10 @@ StatusDialog {
|
|||
|
||||
property var store
|
||||
|
||||
title: qsTr("Import a community from Discord into Status")
|
||||
property bool importingSingleChannel
|
||||
|
||||
title: importingSingleChannel ? qsTr("Import a channel from Discord into Status") :
|
||||
qsTr("Import a community from Discord into Status")
|
||||
|
||||
horizontalPadding: 16
|
||||
verticalPadding: 20
|
||||
|
@ -38,6 +41,7 @@ StatusDialog {
|
|||
id: contents
|
||||
anchors.fill: parent
|
||||
store: root.store
|
||||
importingSingleChannel: root.importingSingleChannel
|
||||
onClose: root.close()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ QtObject {
|
|||
property int discordImportProgressCurrentChunk: root.communitiesModuleInst.discordImportProgressCurrentChunk
|
||||
property string discordImportCommunityId: root.communitiesModuleInst.discordImportCommunityId
|
||||
property string discordImportCommunityName: root.communitiesModuleInst.discordImportCommunityName
|
||||
property string discordImportChannelId: root.communitiesModuleInst.discordImportChannelId
|
||||
property string discordImportChannelName: root.communitiesModuleInst.discordImportChannelName
|
||||
property url discordImportCommunityImage: root.communitiesModuleInst.discordImportCommunityImage
|
||||
property bool discordImportHasCommunityImage: root.communitiesModuleInst.discordImportHasCommunityImage
|
||||
property var discordImportTasks: root.communitiesModuleInst.discordImportTasks
|
||||
|
@ -161,6 +163,7 @@ QtObject {
|
|||
function removeFileListItem(filePath) {
|
||||
root.communitiesModuleInst.removeFileListItem(filePath)
|
||||
}
|
||||
|
||||
function setFileListItems(filePaths) {
|
||||
root.communitiesModuleInst.setFileListItems(filePaths)
|
||||
}
|
||||
|
@ -183,12 +186,20 @@ QtObject {
|
|||
|
||||
function toggleDiscordChannel(id, selected) {
|
||||
root.communitiesModuleInst.toggleDiscordChannel(id, selected)
|
||||
}
|
||||
}
|
||||
|
||||
function toggleOneDiscordChannel(id) {
|
||||
root.communitiesModuleInst.toggleOneDiscordChannel(id)
|
||||
}
|
||||
|
||||
function requestCancelDiscordCommunityImport(id) {
|
||||
root.communitiesModuleInst.requestCancelDiscordCommunityImport(id)
|
||||
}
|
||||
|
||||
function requestCancelDiscordChannelImport(id) {
|
||||
console.warn("!!! IMPLEMENT ME requestCancelDiscordChannelImport(id)") // FIXME
|
||||
}
|
||||
|
||||
function resetDiscordImport() {
|
||||
root.communitiesModuleInst.resetDiscordImport(false)
|
||||
}
|
||||
|
@ -220,6 +231,18 @@ QtObject {
|
|||
args.options.historyArchiveSupportEnabled, args.options.pinMessagesAllowedForMembers, from);
|
||||
}
|
||||
|
||||
function requestImportDiscordChannel(args = {
|
||||
discordChannelId: "",
|
||||
name: "",
|
||||
description: "",
|
||||
color: "",
|
||||
emoji: "",
|
||||
options: {
|
||||
// TODO
|
||||
}
|
||||
}, from = 0) {
|
||||
console.warn("!!! IMPLEMENT ME requestImportDiscordChannel") // FIXME
|
||||
}
|
||||
|
||||
readonly property Connections connections: Connections {
|
||||
target: communitiesModuleInst
|
||||
|
|
|
@ -36,6 +36,7 @@ Item {
|
|||
property var emojiPopup
|
||||
|
||||
property var store
|
||||
property var communitiesStore
|
||||
property bool hasAddedContacts: false
|
||||
property var communityData
|
||||
|
||||
|
@ -173,6 +174,13 @@ Item {
|
|||
onTriggered: Global.openPopup(createChannelPopup)
|
||||
}
|
||||
|
||||
StatusAction {
|
||||
objectName: "importCommunityChannelBtn"
|
||||
text: qsTr("Create channel via Discord import")
|
||||
icon.name: "download"
|
||||
onTriggered: Global.openPopup(createChannelPopup, {isDiscordImport: true})
|
||||
}
|
||||
|
||||
StatusAction {
|
||||
objectName: "createCommunityCategoryBtn"
|
||||
text: qsTr("Create category")
|
||||
|
@ -248,6 +256,13 @@ Item {
|
|||
onTriggered: Global.openPopup(createChannelPopup)
|
||||
}
|
||||
|
||||
StatusAction {
|
||||
objectName: "importCommunityChannelBtn"
|
||||
text: qsTr("Create channel via Discord import")
|
||||
icon.name: "download"
|
||||
onTriggered: Global.openPopup(createChannelPopup, {isDiscordImport: true})
|
||||
}
|
||||
|
||||
StatusAction {
|
||||
text: qsTr("Create category")
|
||||
icon.name: "channel-category"
|
||||
|
@ -523,7 +538,7 @@ Item {
|
|||
Component {
|
||||
id: createChannelPopup
|
||||
CreateChannelPopup {
|
||||
anchors.centerIn: parent
|
||||
communitiesStore: root.communitiesStore
|
||||
emojiPopup: root.emojiPopup
|
||||
onCreateCommunityChannel: function (chName, chDescription, chEmoji, chColor,
|
||||
chCategoryId) {
|
||||
|
|
|
@ -742,15 +742,18 @@ Item {
|
|||
readonly property int warnings: appMain.communitiesStore.discordImportWarningsCount
|
||||
readonly property string communityId: appMain.communitiesStore.discordImportCommunityId
|
||||
readonly property string communityName: appMain.communitiesStore.discordImportCommunityName
|
||||
readonly property string channelId: appMain.communitiesStore.discordImportChannelId
|
||||
readonly property string channelName: appMain.communitiesStore.discordImportChannelName
|
||||
readonly property string channelOrCommunityName: channelName || communityName
|
||||
|
||||
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)
|
||||
return qsTr("The import of ‘%1’ from Discord to Status was stopped: <a href='#'>Critical issues found</a>").arg(channelOrCommunityName)
|
||||
|
||||
let result = qsTr("‘%1’ was successfully imported from Discord to Status").arg(communityName) + " <a href='#'>"
|
||||
let result = qsTr("‘%1’ was successfully imported from Discord to Status").arg(channelOrCommunityName) + " <a href='#'>"
|
||||
if (warnings)
|
||||
result += qsTr("Details (%1)").arg(qsTr("%n issue(s)", "", warnings))
|
||||
else
|
||||
|
@ -759,7 +762,7 @@ Item {
|
|||
return result
|
||||
}
|
||||
if (inProgress) {
|
||||
let result = qsTr("Importing ‘%1’ from Discord to Status").arg(communityName) + " <a href='#'>"
|
||||
let result = qsTr("Importing ‘%1’ from Discord to Status").arg(channelOrCommunityName) + " <a href='#'>"
|
||||
if (warnings)
|
||||
result += qsTr("Check progress (%1)").arg(qsTr("%n issue(s)", "", warnings))
|
||||
else
|
||||
|
@ -770,16 +773,17 @@ Item {
|
|||
|
||||
return ""
|
||||
}
|
||||
onLinkActivated: popups.openDiscordImportProgressPopup()
|
||||
onLinkActivated: popups.openDiscordImportProgressPopup(!!channelId)
|
||||
progressValue: progress
|
||||
closeBtnVisible: finished || stopped
|
||||
buttonText: finished && !errors ? qsTr("Visit your Community") : ""
|
||||
buttonText: finished && !errors ? !!channelId ? qsTr("Visit your new channel") : qsTr("Visit your Community") : ""
|
||||
onClicked: function() {
|
||||
appMain.communitiesStore.setActiveCommunity(communityId)
|
||||
}
|
||||
onCloseClicked: {
|
||||
hide();
|
||||
if (!!channelId)
|
||||
rootStore.setActiveSectionChat(communityId, channelId)
|
||||
else
|
||||
appMain.communitiesStore.setActiveCommunity(communityId)
|
||||
}
|
||||
onCloseClicked: hide()
|
||||
}
|
||||
|
||||
ModuleWarning {
|
||||
|
@ -1247,6 +1251,7 @@ Item {
|
|||
stickersPopup: statusStickersPopupLoader.item
|
||||
sectionItemModel: model
|
||||
createChatPropertiesStore: appMain.createChatPropertiesStore
|
||||
communitiesStore: appMain.communitiesStore
|
||||
communitySettingsDisabled: production && appMain.rootStore.profileSectionStore.walletStore.areTestNetworksEnabled
|
||||
|
||||
rootStore: ChatStores.RootStore {
|
||||
|
|
|
@ -244,8 +244,8 @@ QtObject {
|
|||
openPopup(editSharedAddressesPopupComponent, {communityId: communityId, isEditMode: true})
|
||||
}
|
||||
|
||||
function openDiscordImportProgressPopup() {
|
||||
openPopup(discordImportProgressDialog)
|
||||
function openDiscordImportProgressPopup(importingSingleChannel) {
|
||||
openPopup(discordImportProgressDialog, {importingSingleChannel: importingSingleChannel})
|
||||
}
|
||||
|
||||
function openRemoveContactConfirmationPopup(displayName, publicKey) {
|
||||
|
|
Loading…
Reference in New Issue