2022-05-26 15:46:02 +00:00
import QtQuick 2.14
2022-08-18 14:25:06 +00:00
import QtQuick . Controls 2.14
2022-05-26 15:46:02 +00:00
import QtQuick . Layouts 1.14
2020-12-11 20:29:46 +00:00
import QtQuick . Dialogs 1.3
2021-09-28 15:04:06 +00:00
import utils 1.0
2021-10-27 21:27:49 +00:00
import shared . panels 1.0
import shared . popups 1.0
2020-12-11 20:29:46 +00:00
2021-07-16 10:11:03 +00:00
import StatusQ . Core 0.1
import StatusQ . Core . Theme 0.1
2022-07-05 10:12:27 +00:00
import StatusQ . Core . Utils 0.1 as StatusQUtils
2021-07-16 10:11:03 +00:00
import StatusQ . Components 0.1
import StatusQ . Controls 0.1
2021-08-17 10:49:13 +00:00
import StatusQ . Controls . Validators 0.1
2021-07-16 10:11:03 +00:00
import StatusQ . Popups 0.1
2023-06-23 06:17:04 +00:00
import AppLayouts . Communities . controls 1.0
2023-06-26 11:48:45 +00:00
import AppLayouts . Communities . panels 1.0
2022-06-16 09:41:21 +00:00
StatusStackModal {
2022-05-26 15:46:02 +00:00
id: root
2020-12-17 13:24:33 +00:00
2021-10-22 20:49:47 +00:00
property var store
2022-08-18 14:25:06 +00:00
property bool isDiscordImport // creating new or importing from discord?
2023-06-26 11:44:56 +00:00
property bool isDevBuild
2020-12-11 20:29:46 +00:00
2022-08-18 14:25:06 +00:00
stackTitle: isDiscordImport ? qsTr ( "Import a community from Discord into Status" ) :
qsTr ( "Create New Community" )
2022-06-28 10:55:33 +00:00
width: 640
nextButton: StatusButton {
2022-07-18 20:56:33 +00:00
objectName: "createCommunityNextBtn"
2022-08-18 15:29:55 +00:00
font.weight: Font . Medium
text: typeof currentItem . nextButtonText !== "undefined" ? currentItem.nextButtonText : qsTr ( "Next" )
enabled: typeof ( currentItem . canGoNext ) == "undefined" || currentItem . canGoNext
2022-08-18 15:59:12 +00:00
loading: root . store . discordDataExtractionInProgress
2022-08-18 15:29:55 +00:00
onClicked: {
let nextAction = currentItem . nextAction
if ( typeof ( nextAction ) == "function" ) {
return nextAction ( )
}
root . currentIndex ++
}
2022-06-28 10:55:33 +00:00
}
2021-07-16 10:11:03 +00:00
2022-06-16 09:41:21 +00:00
finishButton: StatusButton {
2022-07-18 20:56:33 +00:00
objectName: "createCommunityFinalBtn"
2022-08-18 15:29:55 +00:00
font.weight: Font . Medium
text: root . isDiscordImport ? qsTr ( "Start Discord import" ) : qsTr ( "Create Community" )
enabled: typeof ( currentItem . canGoNext ) == "undefined" || currentItem . canGoNext
onClicked: {
let nextAction = currentItem . nextAction
if ( typeof ( nextAction ) == "function" ) {
return nextAction ( )
}
if ( ! root . isDiscordImport )
d . createCommunity ( )
}
2022-06-16 09:41:21 +00:00
}
2020-12-11 20:29:46 +00:00
2022-08-18 15:59:12 +00:00
readonly property var clearFilesButton: StatusButton {
font.weight: Font . Medium
text: qsTr ( "Clear all" )
type: StatusBaseButton . Type . Danger
visible: root . currentItem . objectName === "discordFileListView" // no better way to address the current item in the stack :/
enabled: ! fileListView . fileListModelEmpty && ! root . store . discordDataExtractionInProgress
onClicked: root . store . clearFileList ( )
}
rightButtons: [ clearFilesButton , nextButton , finishButton ]
onAboutToShow: {
if ( root . isDiscordImport ) {
2022-10-20 07:50:35 +00:00
if ( ! root . store . discordImportInProgress ) {
root . store . clearFileList ( )
root . store . clearDiscordCategoriesAndChannels ( )
}
2022-08-18 15:59:12 +00:00
for ( let i = 0 ; i < discordPages . length ; i ++ ) {
stackItems . push ( discordPages [ i ] )
}
}
}
readonly property list < Item > discordPages: [
ColumnLayout {
id: fileListView
objectName: "discordFileListView" // !!! DON'T CHANGE, clearFilesButton depends on this
spacing: 24
readonly property var fileListModel: root . store . 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 ? qsTr ( "Validate (%1/%2) files" ) . arg ( fileListModel . selectedCount ) . arg ( fileListModel . count )
: qsTr ( "Import files" )
readonly property var nextAction: function ( ) {
if ( ! fileListView . fileListModel . selectedFilesValid ) {
return root . store . requestExtractChannelsAndCategories ( )
}
root . currentIndex ++
}
RowLayout {
Layout.fillWidth: true
spacing: 12
StatusBaseText {
font.pixelSize: 15
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
font.pixelSize: 12
color: Theme . palette . baseColor1
text: qsTr ( "(JSON file format only)" )
}
IssuePill {
type: root . store . discordImportErrorsCount ? IssuePill.Type.Error : IssuePill . Type . Warning
2022-09-15 07:31:38 +00:00
count: {
if ( root . store . discordImportErrorsCount > 0 ) {
return root . store . discordImportErrorsCount
}
if ( root . store . discordImportWarningsCount > 0 ) {
return root . store . discordImportWarningsCount
}
return 0
}
visible: ! ! count
2022-08-18 15:59:12 +00:00
}
Item { Layout.fillWidth: true }
StatusButton {
text: qsTr ( "Browse files" )
type: StatusBaseButton . Type . Primary
onClicked: fileDialog . open ( )
enabled: ! root . store . 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
2022-08-11 11:55:08 +00:00
asset.name: "info"
2022-08-18 15:59:12 +00:00
}
StatusBaseText {
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>" )
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/wiki'>wiki</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
}
}
}
StatusListView {
visible: ! fileListView . fileListModelEmpty
enabled: ! root . store . discordDataExtractionInProgress
anchors.fill: parent
anchors.margins: 16
model: fileListView . fileListModel
delegate: ColumnLayout {
width: ListView . view . width
2022-11-08 14:07:14 +00:00
RowLayout {
spacing: 20
2022-08-18 15:59:12 +00:00
Layout.fillWidth: true
2022-11-08 14:07:14 +00:00
Layout.topMargin: 8
StatusBaseText {
Layout.fillWidth: true
text: model . filePath
font.pixelSize: 13
elide: Text . ElideRight
wrapMode: Text . WordWrap
maximumLineCount: 2
}
StatusFlatRoundButton {
id: removeButton
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 . store . removeFileListItem ( model . filePath )
}
2022-08-18 15:59:12 +00:00
}
2022-11-08 14:07:14 +00:00
2022-08-18 15:59:12 +00:00
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" ) ]
onAccepted: {
if ( fileDialog . fileUrls . length > 0 ) {
let files = [ ]
for ( let i = 0 ; i < fileDialog . fileUrls . length ; i ++ )
files . push ( decodeURI ( fileDialog . fileUrls [ i ] . toString ( ) ) )
root . store . setFileListItems ( files )
}
}
}
} ,
ColumnLayout {
id: categoriesAndChannelsView
spacing: 24
readonly property bool canGoNext: root . store . discordChannelsModel . hasSelectedItems
readonly property var nextAction: function ( ) {
2022-09-15 07:31:38 +00:00
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 ( )
}
2022-08-18 15:59:12 +00:00
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
visible: ! root . store . discordChannelsModel . count
Loader {
anchors.centerIn: parent
active: parent . visible
sourceComponent: StatusLoadingIndicator {
width: 50
height: 50
}
}
}
ColumnLayout {
spacing: 12
visible: root . store . discordChannelsModel . count
StatusBaseText {
Layout.fillWidth: true
text: qsTr ( "Please select the categories and channels you would like to import" )
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 . store . 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 . store . discordCategoriesModel
delegate: ColumnLayout {
width: ListView . view . width
spacing: 8
StatusCheckBox {
readonly property string categoryId: model . id
id: categoryCheckbox
checked: model . selected
text: model . name
onToggled: root . store . toggleDiscordCategory ( categoryId , checked )
}
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.leftMargin: 24
Repeater {
Layout.fillWidth: true
model: root . store . discordChannelsModel
delegate: StatusCheckBox {
width: parent . width
text: model . name
checked: model . selected
visible: model . categoryId === categoryCheckbox . categoryId
onToggled: root . store . toggleDiscordChannel ( model . id , checked )
}
}
}
}
}
}
}
}
]
2022-08-03 00:07:27 +00:00
2022-06-16 09:41:21 +00:00
stackItems: [
2022-07-13 12:29:38 +00:00
StatusScrollView {
2022-05-26 15:46:02 +00:00
id: generalView
2023-02-06 18:08:55 +00:00
contentWidth: availableWidth
2023-06-29 21:52:16 +00:00
readonly property bool canGoNext: generalViewLayout . isNameValid && generalViewLayout . isDescriptionValid && ( root . isDevBuild || ( generalViewLayout . isLogoSelected && generalViewLayout . isBannerSelected ) )
2022-08-18 15:29:55 +00:00
2023-05-31 20:58:23 +00:00
padding: 0
2022-09-27 08:51:53 +00:00
clip: false
2023-05-31 20:58:23 +00:00
ScrollBar.vertical: StatusScrollBar {
parent: root
anchors.top: generalView . top
anchors.bottom: generalView . bottom
anchors.left: generalView . right
anchors.leftMargin: 1
}
2023-06-29 21:52:16 +00:00
EditCommunitySettingsForm {
2022-05-26 15:46:02 +00:00
id: generalViewLayout
2023-05-31 20:58:23 +00:00
width: generalView . availableWidth
2022-06-16 09:41:21 +00:00
2023-06-29 21:52:16 +00:00
nameLabel: qsTr ( "Name your community" )
descriptionLabel: qsTr ( "Give it a short description" )
2022-06-09 14:59:54 +00:00
2023-06-29 21:52:16 +00:00
tags: root . store . communityTags
2022-05-10 16:13:36 +00:00
}
2022-06-16 09:41:21 +00:00
} ,
2022-08-18 15:29:55 +00:00
2022-05-26 15:46:02 +00:00
ColumnLayout {
id: introOutroMessageView
2022-07-29 08:00:37 +00:00
spacing: 11
2022-08-18 15:29:55 +00:00
readonly property bool canGoNext: introMessageInput . valid && outroMessageInput . valid
2023-06-26 11:48:45 +00:00
IntroMessageInput {
2022-05-26 15:46:02 +00:00
id: introMessageInput
2022-07-18 20:56:33 +00:00
input.edit.objectName: "createCommunityIntroMessageInput"
2022-08-18 15:29:55 +00:00
input.tabNavItem: outroMessageInput . input . edit
2020-12-17 13:24:33 +00:00
2022-05-26 15:46:02 +00:00
Layout.fillWidth: true
Layout.fillHeight: true
2020-12-11 20:29:46 +00:00
2022-07-29 08:00:37 +00:00
minimumHeight: height
maximumHeight: ( height - Style . current . xlPadding )
2023-06-29 21:52:16 +00:00
label: qsTr ( "Community introduction and rules" )
2022-05-26 15:46:02 +00:00
}
2023-06-26 11:48:45 +00:00
OutroMessageInput {
2022-05-26 15:46:02 +00:00
id: outroMessageInput
2022-07-18 20:56:33 +00:00
input.edit.objectName: "createCommunityOutroMessageInput"
2022-08-18 15:29:55 +00:00
input.tabNavItem: introMessageInput . input . edit
2022-05-26 15:46:02 +00:00
Layout.fillWidth: true
2021-05-24 03:09:49 +00:00
}
2020-12-11 20:29:46 +00:00
}
2022-06-16 09:41:21 +00:00
]
2022-05-26 15:46:02 +00:00
QtObject {
id: d
2022-09-15 07:31:38 +00:00
function _getCommunityConfig ( ) {
return {
2023-06-29 21:52:16 +00:00
name: StatusQUtils . Utils . filterXSS ( generalViewLayout . name ) ,
description: StatusQUtils . Utils . filterXSS ( generalViewLayout . description ) ,
2022-09-15 07:31:38 +00:00
introMessage: StatusQUtils . Utils . filterXSS ( introMessageInput . input . text ) ,
outroMessage: StatusQUtils . Utils . filterXSS ( outroMessageInput . input . text ) ,
2023-06-29 21:52:16 +00:00
color: generalViewLayout . color . toString ( ) . toUpperCase ( ) ,
tags: generalViewLayout . selectedTags ,
2022-09-15 07:31:38 +00:00
image: {
2023-06-29 21:52:16 +00:00
src: generalViewLayout . logoImagePath ,
AX: generalViewLayout . logoCropRect . x ,
AY: generalViewLayout . logoCropRect . y ,
BX: generalViewLayout . logoCropRect . x + generalViewLayout . logoCropRect . width ,
BY: generalViewLayout . logoCropRect . y + generalViewLayout . logoCropRect . height ,
2022-09-15 07:31:38 +00:00
} ,
options: {
2023-06-29 21:52:16 +00:00
historyArchiveSupportEnabled: generalViewLayout . options . archiveSupportEnabled ,
checkedMembership: generalViewLayout . options . requestToJoinEnabled ? Constants.communityChatOnRequestAccess : Constants . communityChatPublicAccess ,
pinMessagesAllowedForMembers: generalViewLayout . options . pinMessagesEnabled ,
2023-07-21 21:05:42 +00:00
archiveSupporVisible: true
2022-09-15 07:31:38 +00:00
} ,
2023-06-29 21:52:16 +00:00
bannerJsonStr: JSON . stringify ( { imagePath: String ( generalViewLayout . bannerPath ) . replace ( "file://" , "" ) , cropRect: generalViewLayout . bannerCropRect } )
2022-09-15 07:31:38 +00:00
}
}
2022-05-26 15:46:02 +00:00
function createCommunity ( ) {
2022-09-15 07:31:38 +00:00
const error = root . store . createCommunity ( _getCommunityConfig ( ) )
2022-05-26 15:46:02 +00:00
if ( error ) {
errorDialog . text = error . error
errorDialog . open ( )
}
root . close ( )
}
2022-09-15 07:31:38 +00:00
function requestImportDiscordCommunity ( ) {
const error = root . store . requestImportDiscordCommunity ( _getCommunityConfig ( ) , datePicker . selectedDate . valueOf ( ) / 1000 )
if ( error ) {
errorDialog . text = error . error
errorDialog . open ( )
}
}
2022-05-26 15:46:02 +00:00
}
2021-07-16 10:11:03 +00:00
MessageDialog {
2022-05-26 15:46:02 +00:00
id: errorDialog
title: qsTr ( "Error creating the community" )
2021-07-16 10:11:03 +00:00
icon: StandardIcon . Critical
standardButtons: StandardButton . Ok
}
2020-12-11 20:29:46 +00:00
}