2023-05-31 20:58:23 +00:00
import QtQuick 2.15
import QtQuick . Layouts 1.15
import QtQuick . Controls 2.15
2020-12-11 20:29:46 +00:00
import QtQuick . Dialogs 1.3
2024-02-06 09:31:36 +00:00
import QtQml 2.15
2023-05-31 20:58:23 +00:00
import QtQml . Models 2.15
2022-08-01 14:17:28 +00:00
2021-10-08 12:26:46 +00:00
import utils 1.0
2022-05-12 17:18:25 +00:00
import shared . panels 1.0
2020-12-11 20:29:46 +00:00
2024-02-06 09:31:36 +00:00
import StatusQ 0.1
2021-07-07 12:45:11 +00:00
import StatusQ . Core 0.1
import StatusQ . Core . Theme 0.1
2022-03-07 14:56:05 +00:00
import StatusQ . Core . Utils 0.1 as StatusQUtils
2021-07-07 12:45:11 +00:00
import StatusQ . Controls 0.1
2021-08-16 13:46:00 +00:00
import StatusQ . Controls . Validators 0.1
2021-07-07 12:45:11 +00:00
import StatusQ . Components 0.1
import StatusQ . Popups 0.1
2024-02-06 09:31:36 +00:00
import AppLayouts . Communities . views 1.0
2023-09-14 13:34:36 +00:00
import AppLayouts . Communities . panels 1.0
2024-02-06 09:31:36 +00:00
import AppLayouts . Communities . models 1.0
import AppLayouts . Communities . controls 1.0
2024-05-22 08:13:39 +00:00
import AppLayouts . Communities . stores 1.0 as CommunitiesStores
2023-09-01 07:58:48 +00:00
StatusStackModal {
2022-08-01 14:17:28 +00:00
id: root
2021-12-21 09:26:13 +00:00
2024-05-22 08:13:39 +00:00
property CommunitiesStores . CommunitiesStore communitiesStore
2023-09-01 07:58:48 +00:00
property bool isDiscordImport // creating new or importing from discord?
2021-05-28 02:55:50 +00:00
property bool isEdit: false
2022-08-05 06:47:19 +00:00
property bool isDeleteable: false
2024-02-06 09:31:36 +00:00
property bool viewOnlyCanAddReaction
2024-03-18 17:33:07 +00:00
property bool hideIfPermissionsNotMet: false
2024-07-19 18:45:40 +00:00
property bool ensCommunityPermissionsEnabled: false
2023-09-01 07:58:48 +00:00
2023-10-16 18:26:47 +00:00
property string communityId: ""
2024-02-06 09:31:36 +00:00
property string chatId: "_newChannel"
2022-01-31 13:33:42 +00:00
property string categoryId: ""
2021-12-21 09:26:13 +00:00
property string channelName: ""
property string channelDescription: ""
2022-03-07 14:56:05 +00:00
property string channelEmoji: ""
2024-05-03 15:51:25 +00:00
property string channelColor: d . communityDetails . color !== "" ? d.communityDetails.color : ""
2022-03-07 14:56:05 +00:00
property bool emojiPopupOpened: false
property var emojiPopup: null
2022-07-08 13:47:23 +00:00
readonly property int communityColorValidator: Utils . Validate . NoEmpty
2022-03-10 19:28:37 +00:00
| Utils . Validate . TextHexColor
2021-06-29 12:47:28 +00:00
2024-02-06 09:31:36 +00:00
property var activeCommunity
required property var assetsModel
required property var collectiblesModel
required property var permissionsModel
required property var channelsModel
2022-05-09 08:40:27 +00:00
readonly property int maxChannelNameLength: 24
2021-06-29 12:47:28 +00:00
readonly property int maxChannelDescLength: 140
2022-02-09 09:43:23 +00:00
2024-02-06 09:31:36 +00:00
// channel signals
2024-03-18 17:33:07 +00:00
signal createCommunityChannel ( string chName , string chDescription , string chEmoji , string chColor , string chCategoryId , bool viewOnlyCanAddReaction , bool hideIfPermissionsNotMet )
signal editCommunityChannel ( string chName , string chDescription , string chEmoji , string chColor , string chCategoryId , bool viewOnlyCanAddReaction , bool hideIfPermissionsNotMet )
2022-08-05 06:47:19 +00:00
signal deleteCommunityChannel ( )
2021-06-02 19:43:33 +00:00
2024-02-06 09:31:36 +00:00
// Permissions signals:
// permissions arg is a list of objects with the following properties:
// - key: string
// - id: string
// - permissionType: string
// - holdings: list of objects with the following properties:
// - key: string
// - type: string
// - amount: string
// - channels: list of objects with the following properties:
// - key: string
// - isPrivate: bool
signal addPermissions ( var permissions )
signal removePermissions ( var permissions )
signal editPermissions ( var permissions )
signal setHideIfPermissionsNotMet ( bool checked )
2023-09-01 07:58:48 +00:00
width: 640
2024-02-06 09:31:36 +00:00
leftPadding: 0
rightPadding: 0
currentIndex: d . currentPage
2024-03-04 22:36:52 +00:00
closePolicy: d . dirty && ! root . isDiscordImport ? Popup.NoAutoClose : ( Popup . CloseOnEscape | Popup . CloseOnPressOutside )
closeHandler: d . closeRequested
2024-02-06 09:31:36 +00:00
enum CurrentPage {
ChannelDetails , //0
ColorPicker , //1
ChannelPermissions , //2
DiscordImportUploadFile , //3
DiscordImportUploadStart //4
}
2023-09-01 07:58:48 +00:00
2023-05-08 15:10:47 +00:00
QtObject {
id: d
2024-03-04 22:36:52 +00:00
readonly property bool dirty: d . channelEditModel . dirtyPermissions ||
d . viewOnlyCanAddReaction !== root . viewOnlyCanAddReaction ||
d . hideIfPermissionsNotMet !== root . hideIfPermissionsNotMet ||
nameInput . input . text !== root . channelName ||
descriptionTextArea . text !== root . channelDescription ||
2024-05-03 15:51:25 +00:00
! Qt . colorEqual ( colorPanel . color , root . channelColor ) ||
2024-03-04 22:36:52 +00:00
nameInput . input . asset . emoji !== root . channelEmoji
2024-02-06 09:31:36 +00:00
property int currentPage: CreateChannelPopup . CurrentPage . ChannelDetails
readonly property QtObject communityDetails: QtObject {
readonly property string id: root.activeCommunity.id
readonly property string name: root . activeCommunity . name
readonly property string image: root . activeCommunity . image
readonly property string color: root . activeCommunity . color
readonly property bool owner: root . activeCommunity . memberRole === Constants . memberRole . owner
readonly property bool admin: root . activeCommunity . memberRole === Constants . memberRole . admin
readonly property bool tokenMaster: root . activeCommunity . memberRole === Constants . memberRole . tokenMaster
}
readonly property ChannelPermissionsModelEditor channelEditModel: ChannelPermissionsModelEditor {
channelId: root . chatId
name: nameInput . input . text
emoji: nameInput . input . asset . emoji
color: colorPanel . color . toString ( ) . toUpperCase ( )
channelsModel: root . channelsModel
permissionsModel: root . permissionsModel
newChannelMode: ! root . isEdit
property Connections rootConnection: Connections {
target: root
function onClosed ( ) {
d . channelEditModel . reset ( )
}
}
}
property bool viewOnlyCanAddReaction: root . viewOnlyCanAddReaction
property bool hideIfPermissionsNotMet: root . hideIfPermissionsNotMet
property bool colorPickerOpened: false
2023-09-01 07:58:48 +00:00
function isFormValid ( ) {
return nameInput . valid && descriptionTextArea . valid &&
2024-02-06 09:31:36 +00:00
Utils . validateAndReturnError ( colorPanel . color . toString ( ) . toUpperCase ( ) , communityColorValidator ) === ""
2023-09-01 07:58:48 +00:00
}
2023-05-08 15:10:47 +00:00
function openEmojiPopup ( leftSide = false ) {
root . emojiPopupOpened = true ;
root . emojiPopup . open ( ) ;
root . emojiPopup . emojiSize = StatusQUtils . Emoji . size . verySmall ;
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 ;
}
2023-09-01 07:58:48 +00:00
function _getChannelConfig ( ) {
return {
2023-10-16 18:26:47 +00:00
communityId: root . communityId ,
2023-09-01 07:58:48 +00:00
discordChannelId: root . communitiesStore . discordImportChannelId ,
categoryId: root . categoryId ,
name: StatusQUtils . Utils . filterXSS ( nameInput . input . text ) ,
description: StatusQUtils . Utils . filterXSS ( descriptionTextArea . text ) ,
2024-02-06 09:31:36 +00:00
color: colorPanel . color . toString ( ) . toUpperCase ( ) ,
2023-09-01 07:58:48 +00:00
emoji: StatusQUtils . Emoji . deparse ( nameInput . input . asset . emoji ) ,
options: {
// TODO
}
}
}
2020-12-11 20:29:46 +00:00
2023-09-01 07:58:48 +00:00
function requestImportDiscordChannel ( ) {
const error = root . communitiesStore . requestImportDiscordChannel ( _getChannelConfig ( ) , datePicker . selectedDate . valueOf ( ) / 1000 )
if ( error ) {
creatingError . text = error . error
creatingError . open ( )
2022-03-21 20:51:54 +00:00
}
2021-05-28 02:55:50 +00:00
}
2024-02-06 09:31:36 +00:00
function saveAndClose ( ) {
2024-03-04 22:36:52 +00:00
if ( ! d . isFormValid ( ) ) {
scrollView . scrollBackUp ( )
return
}
2024-02-06 09:31:36 +00:00
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 ,
colorPanel . color . toString ( ) . toUpperCase ( ) ,
2024-03-13 19:13:41 +00:00
root . categoryId ,
2024-03-18 17:33:07 +00:00
d . viewOnlyCanAddReaction ,
d . hideIfPermissionsNotMet )
2024-02-06 09:31:36 +00:00
} else {
root . editCommunityChannel ( StatusQUtils . Utils . filterXSS ( nameInput . input . text ) ,
StatusQUtils . Utils . filterXSS ( descriptionTextArea . text ) ,
emoji ,
colorPanel . color . toString ( ) . toUpperCase ( ) ,
2024-03-13 19:13:41 +00:00
root . categoryId ,
2024-03-18 17:33:07 +00:00
d . viewOnlyCanAddReaction ,
d . hideIfPermissionsNotMet )
2024-02-06 09:31:36 +00:00
}
if ( d . channelEditModel . dirtyPermissions ) {
var newPermissions = d . channelEditModel . getAddedPermissions ( ) ;
if ( newPermissions . length > 0 ) {
root . addPermissions ( newPermissions ) ;
}
var editedPermissions = d . channelEditModel . getEditedPermissions ( ) ;
if ( editedPermissions . length > 0 ) {
root . editPermissions ( editedPermissions ) ;
}
var removedPermissions = d . channelEditModel . getRemovedPermissions ( ) ;
if ( removedPermissions . length > 0 ) {
root . removePermissions ( removedPermissions ) ;
}
}
if ( root . hideIfPermissionsNotMet !== d . hideIfPermissionsNotMet ) {
root . setHideIfPermissionsNotMet ( d . hideIfPermissionsNotMet ) ;
}
// TODO Open the channel once we have designs for it
root . close ( )
}
2024-03-04 22:36:52 +00:00
function closeRequested ( ) {
if ( d . dirty && ! root . isDiscordImport )
closeConfirmation . open ( )
else
root . close ( )
}
}
StatusConfirmationDialog {
id: closeConfirmation
title: qsTr ( "Save changes to #%1 channel?" ) . arg ( root . channelName || nameInput . input . text )
body: qsTr ( "You have made changes to #%1 channel. If you close this dialog without saving these changes will be lost?" ) . arg ( root . channelName || nameInput . input . text )
acceptButtonText: qsTr ( "Save changes" )
rejectButtonText: qsTr ( "Close without saving" )
onAccepted: {
d . saveAndClose ( )
}
onRejected: {
root . close ( )
}
2020-12-11 20:29:46 +00:00
}
2021-07-07 12:45:11 +00:00
2023-09-01 07:58:48 +00:00
stackTitle: isDiscordImport ? qsTr ( "New Channel With Imported Chat History" ) :
2024-02-06 09:31:36 +00:00
! ! currentItem . stackTitleText ? currentItem.stackTitleText :
( isEdit ? qsTr ( "Edit #%1" ) . arg ( root . channelName ) : qsTr ( "New channel" ) )
2023-09-01 07:58:48 +00:00
nextButton: StatusButton {
2024-02-06 09:31:36 +00:00
objectName: "createOrEditCommunityChannelBtn"
visible: ! d . colorPickerOpened
2023-09-01 07:58:48 +00:00
enabled: typeof ( currentItem . canGoNext ) == "undefined" || currentItem . canGoNext
2024-02-06 09:31:36 +00:00
text: ! ! currentItem . nextButtonText ? currentItem.nextButtonText :
d . colorPickerOpened ? qsTr ( "Set channel color" ) : (
isDiscordImport ? qsTr ( "Import chat history" ) :
isEdit ? qsTr ( "Save changes" ) : qsTr ( "Create channel" ) )
2023-09-01 07:58:48 +00:00
loading: root . communitiesStore . discordDataExtractionInProgress
onClicked: {
2024-02-06 09:31:36 +00:00
let nextAction = currentItem . nextAction
if ( typeof ( nextAction ) == "function" ) {
2023-09-01 07:58:48 +00:00
return nextAction ( )
}
2022-03-07 14:56:05 +00:00
}
2023-09-01 07:58:48 +00:00
}
finishButton: StatusButton {
2024-02-06 09:31:36 +00:00
objectName: "createChannelNextBtn"
text: ( typeof currentItem . nextButtonText !== "undefined" ) ? currentItem.nextButtonText :
qsTr ( "Import chat history" )
2023-09-01 07:58:48 +00:00
enabled: typeof ( currentItem . canGoNext ) == "undefined" || currentItem . canGoNext
onClicked: {
2024-02-06 09:31:36 +00:00
const nextAction = currentItem . nextAction
if ( typeof ( nextAction ) == "function" ) {
2023-09-01 07:58:48 +00:00
return nextAction ( )
}
2022-03-07 14:56:05 +00:00
}
}
2024-02-06 09:31:36 +00:00
//TODO
onCurrentIndexChanged: {
d . colorPickerOpened = false ;
}
2022-03-07 14:56:05 +00:00
2023-09-01 07:58:48 +00:00
readonly property StatusButton clearFilesButton: StatusButton {
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 ( )
2020-12-11 20:29:46 +00:00
}
2023-09-01 07:58:48 +00:00
readonly property StatusButton deleteChannelButton: StatusButton {
objectName: "deleteCommunityChannelBtn"
2024-02-06 09:31:36 +00:00
height: 44
visible: isEdit && isDeleteable && ! isDiscordImport && ( d . currentPage === CreateChannelPopup . CurrentPage . ChannelDetails ) ||
! ! currentItem . deleteButtonText
text: ( d . currentPage === CreateChannelPopup . CurrentPage . ChannelPermissions ) ? currentItem.deleteButtonText : qsTr ( "Delete channel" )
enabled: ( d . currentPage === CreateChannelPopup . CurrentPage . ChannelPermissions ) ? currentItem.deleteButtonEnabled : true
2023-09-01 07:58:48 +00:00
type: StatusBaseButton . Type . Danger
2024-02-06 09:31:36 +00:00
onClicked: {
const nextAction = currentItem . nextDeleteAction
if ( typeof ( nextAction ) == "function" ) {
return nextAction ( )
} else {
root . deleteCommunityChannel ( ) ;
}
}
}
property Item backButton: StatusBackButton {
visible: d . currentPage !== CreateChannelPopup . CurrentPage . ChannelDetails
onClicked: {
d . currentPage = ( d . currentPage === CreateChannelPopup . CurrentPage . DiscordImportUploadStart ) ?
CreateChannelPopup.CurrentPage.DiscordImportUploadFile : CreateChannelPopup . CurrentPage . ChannelDetails
}
2024-02-08 16:59:53 +00:00
Layout.minimumWidth: implicitWidth
2023-09-01 07:58:48 +00:00
}
2020-12-11 20:29:46 +00:00
2024-02-06 09:31:36 +00:00
leftButtons: [ backButton ]
2023-09-01 07:58:48 +00:00
rightButtons: [ clearFilesButton , deleteChannelButton , nextButton , finishButton ]
2020-12-11 20:29:46 +00:00
2023-09-01 07:58:48 +00:00
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 ] )
}
}
2020-12-11 20:29:46 +00:00
2023-09-01 07:58:48 +00:00
nameInput . input . edit . forceActiveFocus ( Qt . MouseFocusReason )
if ( isEdit ) {
nameInput . text = root . channelName
descriptionTextArea . text = root . channelDescription
if ( root . channelEmoji ) {
nameInput . input . asset . emoji = root . channelEmoji
}
} else {
nameInput . input . asset . isLetterIdenticon = true ;
2020-12-11 20:29:46 +00:00
}
2023-09-01 07:58:48 +00:00
updateRightButtons ( )
}
readonly property list < Item > discordPages: [
2024-02-06 09:31:36 +00:00
Item {
id: fileListViewItem
2023-09-01 07:58:48 +00:00
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 ( ) {
2024-02-06 09:31:36 +00:00
if ( ! fileListViewItem . fileListModel . selectedFilesValid )
2023-09-01 07:58:48 +00:00
return root . communitiesStore . requestExtractChannelsAndCategories ( )
2024-02-06 09:31:36 +00:00
d . currentPage = CreateChannelPopup . CurrentPage . DiscordImportUploadStart ;
2023-09-01 07:58:48 +00:00
}
2024-02-06 09:31:36 +00:00
ColumnLayout {
id: fileListView
anchors.fill: parent
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 24
2021-07-07 12:45:11 +00:00
2024-02-06 09:31:36 +00:00
RowLayout {
2023-09-01 07:58:48 +00:00
Layout.fillWidth: true
2024-02-06 09:31:36 +00:00
spacing: 12
StatusBaseText {
Layout.fillWidth: true
maximumLineCount: 2
wrapMode: Text . Wrap
elide: Text . ElideRight
text: fileListViewItem . 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: fileListViewItem . 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 && ! fileListViewItem . fileListModelEmpty
}
StatusButton {
Layout.alignment: Qt . AlignRight
text: qsTr ( "Browse files" )
type: StatusBaseButton . Type . Primary
onClicked: fileDialog . open ( )
enabled: ! root . communitiesStore . discordDataExtractionInProgress
}
2023-09-01 07:58:48 +00:00
}
2023-05-31 20:58:23 +00:00
2024-02-06 09:31:36 +00:00
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: Theme . palette . baseColor4
2023-09-01 07:58:48 +00:00
2024-02-06 09:31:36 +00:00
ColumnLayout {
visible: fileListViewItem . fileListModelEmpty
anchors.top: parent . top
anchors.topMargin: 60
anchors.horizontalCenter: parent . horizontalCenter
spacing: 8
StatusRoundIcon {
Layout.alignment: Qt . AlignHCenter
asset.name: "info"
2023-09-01 07:58:48 +00:00
}
2024-02-06 09:31:36 +00:00
StatusBaseText {
id: infoText1
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 )
MouseArea {
anchors.fill: parent
acceptedButtons: Qt . NoButton
cursorShape: parent . hoveredLink ? Qt.PointingHandCursor : Qt . ArrowCursor
}
2023-09-01 07:58:48 +00:00
}
2024-02-06 09:31:36 +00:00
StatusBaseText {
id: infoText2
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 )
MouseArea {
anchors.fill: parent
acceptedButtons: Qt . NoButton
cursorShape: parent . hoveredLink ? Qt.PointingHandCursor : Qt . ArrowCursor
}
2023-09-01 07:58:48 +00:00
}
2022-05-12 17:18:25 +00:00
}
2023-09-01 07:58:48 +00:00
2024-02-06 09:31:36 +00:00
Component {
id: floatingDivComp
Rectangle {
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
width: ListView . view ? ListView.view.width : 0
height: 4
color: Theme . palette . directColor8
}
2022-05-12 17:18:25 +00:00
}
2023-09-01 07:58:48 +00:00
2024-02-06 09:31:36 +00:00
StatusListView {
visible: ! fileListViewItem . fileListModelEmpty
enabled: ! root . communitiesStore . discordDataExtractionInProgress
anchors.fill: parent
leftMargin: 8
rightMargin: 8
model: fileListViewItem . 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
2023-09-01 07:58:48 +00:00
Layout.fillWidth: true
2024-02-06 09:31:36 +00:00
Layout.topMargin: 8
StatusBaseText {
Layout.fillWidth: true
text: model . filePath
font.pixelSize: 13
elide: Text . ElideRight
wrapMode: Text . WordWrap
maximumLineCount: 2
}
2023-09-01 07:58:48 +00:00
2024-02-06 09:31:36 +00:00
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 )
}
2023-09-01 07:58:48 +00:00
}
2024-02-06 09:31:36 +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
}
2023-09-01 07:58:48 +00:00
}
}
2023-05-08 15:10:47 +00:00
}
2023-09-01 07:58:48 +00:00
}
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 )
2022-05-09 08:40:27 +00:00
}
2023-09-01 07:58:48 +00:00
}
2020-12-11 20:29:46 +00:00
}
2023-09-01 07:58:48 +00:00
} ,
2024-02-06 09:31:36 +00:00
Item {
2023-09-01 07:58:48 +00:00
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 )
2022-03-10 19:28:37 +00:00
}
2024-02-06 09:31:36 +00:00
ColumnLayout {
id: categoriesAndChannelsView
spacing: 24
anchors.fill: parent
anchors.leftMargin: 16
anchors.rightMargin: 16
2022-03-10 19:28:37 +00:00
2024-02-06 09:31:36 +00:00
Component {
id: progressComponent
DiscordImportProgressContents {
width: root . availableWidth
store: root . communitiesStore
importingSingleChannel: true
onClose: root . close ( )
2023-09-01 07:58:48 +00:00
}
}
2022-03-10 19:28:37 +00:00
2024-02-06 09:31:36 +00:00
Item {
2023-09-01 07:58:48 +00:00
Layout.fillWidth: true
2024-02-06 09:31:36 +00:00
Layout.fillHeight: true
visible: ! root . communitiesStore . discordChannelsModel . count
Loader {
anchors.centerIn: parent
active: parent . visible
sourceComponent: StatusLoadingIndicator {
width: 50
height: 50
}
}
2023-09-01 07:58:48 +00:00
}
2022-03-10 19:28:37 +00:00
2024-02-06 09:31:36 +00:00
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
2023-09-01 07:58:48 +00:00
}
2024-02-06 09:31:36 +00:00
RowLayout {
spacing: 20
2023-09-01 07:58:48 +00:00
Layout.fillWidth: true
2024-02-06 09:31:36 +00:00
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
}
2023-09-01 07:58:48 +00:00
}
2022-03-10 19:28:37 +00:00
2024-02-06 09:31:36 +00:00
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: Theme . palette . baseColor4
2023-09-01 07:58:48 +00:00
2024-02-06 09:31:36 +00:00
StatusListView {
anchors.fill: parent
anchors.margins: 16
model: root . communitiesStore . discordCategoriesModel
delegate: ColumnLayout {
width: ListView . view . width
spacing: 8
2023-09-01 07:58:48 +00:00
2024-02-06 09:31:36 +00:00
StatusBaseText {
readonly property string categoryId: model . id
id: categoryCheckbox
text: model . name
}
2023-09-01 07:58:48 +00:00
2024-02-06 09:31:36 +00:00
ColumnLayout {
spacing: 8
2023-09-01 07:58:48 +00:00
Layout.fillWidth: true
2024-02-06 09:31:36 +00:00
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 )
Component.onCompleted: {
if ( model . selected ) {
root . communitiesStore . toggleOneDiscordChannel ( model . id )
}
2023-10-16 18:26:47 +00:00
}
}
2023-09-01 07:58:48 +00:00
}
}
}
}
}
}
}
}
]
2022-03-10 19:28:37 +00:00
2023-09-01 07:58:48 +00:00
Connections {
enabled: root . opened && root . emojiPopupOpened
target: emojiPopup
function onEmojiSelected ( emojiText: string , atCursor: bool ) {
nameInput . input . asset . isLetterIdenticon = false ;
nameInput . input . asset . emoji = emojiText
}
function onClosed ( ) {
root . emojiPopupOpened = false
}
}
stackItems: [
StatusScrollView {
id: scrollView
2024-03-04 22:36:52 +00:00
readonly property bool canGoNext: d . isFormValid ( ) && ( root . isDiscordImport ? true : d . dirty )
2023-09-01 07:58:48 +00:00
property ScrollBar vScrollBar: ScrollBar . vertical
contentWidth: availableWidth
padding: 0
function scrollBackUp ( ) {
vScrollBar . setPosition ( 0 )
}
ColumnLayout {
id: content
width: scrollView . availableWidth
2024-02-06 09:31:36 +00:00
spacing: Style . current . padding
2023-09-01 07:58:48 +00:00
StatusInput {
id: nameInput
Layout.fillWidth: true
2024-02-06 09:31:36 +00:00
Layout.leftMargin: Style . current . padding
Layout.rightMargin: Style . current . padding
2023-09-01 07:58:48 +00:00
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
2022-03-10 19:28:37 +00:00
}
}
2024-05-03 15:51:25 +00:00
input.asset.color: colorPanel . color
2023-09-01 07:58:48 +00:00
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
}
]
2022-05-18 12:21:03 +00:00
}
2022-03-10 19:28:37 +00:00
2023-09-01 07:58:48 +00:00
Item {
Layout.fillWidth: true
2024-02-06 09:31:36 +00:00
Layout.preferredHeight: 82
Layout.leftMargin: Style . current . padding
Layout.rightMargin: Style . current . padding
StatusBaseText {
width: parent . width
anchors.top: parent . top
anchors.topMargin: Style . current . halfPadding
text: qsTr ( "Channel colour" )
}
StatusPickerButton {
id: colorSelectorButton
property string validationError: ""
width: parent . width
2024-05-03 15:51:25 +00:00
height: 44
2024-02-06 09:31:36 +00:00
anchors.bottom: parent . bottom
bgColor: colorPanel . colorSelected ? colorPanel.color : Theme . palette . baseColor2
contentColor: colorPanel . colorSelected ? Theme.palette.white : Theme . palette . baseColor1
text: colorPanel . colorSelected ? colorPanel . color . toString ( ) . toUpperCase ( ) : qsTr ( "Pick a colour" )
onClicked: { d . currentPage = CreateChannelPopup . CurrentPage . ColorPicker ; d . colorPickerOpened = true ; }
onTextChanged: {
if ( colorPanel . colorSelected ) {
validationError = Utils . validateAndReturnError ( text , communityColorValidator )
2023-09-01 07:58:48 +00:00
}
}
2022-08-01 14:17:28 +00:00
}
2023-09-14 13:34:36 +00:00
}
2023-09-01 07:58:48 +00:00
StatusInput {
id: descriptionTextArea
Layout.fillWidth: true
2024-02-06 09:31:36 +00:00
Layout.topMargin: Style . current . halfPadding
Layout.leftMargin: Style . current . padding
Layout.rightMargin: Style . current . padding
2023-09-01 07:58:48 +00:00
input.edit.objectName: "createOrEditCommunityChannelDescriptionInput"
input.verticalAlignment: TextEdit . AlignTop
label: qsTr ( "Description" )
charLimit: 140
placeholderText: qsTr ( "Describe the channel" )
input.multiline: true
2024-02-06 09:31:36 +00:00
minimumHeight: 108
maximumHeight: 108
2023-09-01 07:58:48 +00:00
validators: [
StatusMinLengthValidator {
minLength: 1
errorMessage: Utils . getErrorMessage ( descriptionTextArea . errors , qsTr ( "channel description" ) )
} ,
StatusRegularExpressionValidator {
regularExpression: Constants . regularExpressions . alphanumericalExpanded
errorMessage: Constants . errorMessages . alphanumericalExpandedRegExp
}
]
2022-08-01 14:17:28 +00:00
}
2024-02-06 09:31:36 +00:00
Separator {
Layout.fillWidth: true
visible: viewOnlyCanAddReactionCheckbox . visible
}
StatusCheckBox {
2024-07-11 10:51:56 +00:00
objectName: "hideChannelCheckbox"
2024-02-06 09:31:36 +00:00
id: viewOnlyCanAddReactionCheckbox
Layout.fillWidth: true
Layout.preferredHeight: 48
Layout.leftMargin: Style . current . padding
Layout.rightMargin: Style . current . padding
leftSide: false
text: qsTr ( "Hide channel from members who don't have permissions to view the channel" )
checked: d . hideIfPermissionsNotMet
onToggled: {
d . hideIfPermissionsNotMet = checked ;
}
}
Separator {
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
Layout.preferredHeight: 56
Layout.leftMargin: Style . current . padding
Layout.rightMargin: Style . current . padding
StatusBaseText {
text: qsTr ( "Permissions" )
}
Item { Layout.fillWidth: true }
StatusButton {
2024-07-11 10:51:56 +00:00
objectName: "addPermissionButton"
2024-02-06 09:31:36 +00:00
text: qsTr ( "Add permission" )
enabled: ! ! nameInput . text
property ListModel channelToAddPermission: ListModel { }
onClicked: {
channelToAddPermission . clear ( ) ;
channelToAddPermission . append ( { "key" : root . chatId , "name" : nameInput . text } ) ;
2024-02-07 10:30:46 +00:00
const properties = {
2024-02-06 09:31:36 +00:00
channelsToEditModel: channelToAddPermission ,
header: null ,
topPadding: - root . subHeaderPadding - 8 ,
leftPadding: 0 ,
viewWidth: scrollView . availableWidth - 32
} ;
2024-02-07 10:30:46 +00:00
editPermissionView . pushEditView ( properties ) ;
2024-02-06 09:31:36 +00:00
d . currentPage = CreateChannelPopup . CurrentPage . ChannelPermissions ;
}
}
}
PermissionsView {
Layout.fillWidth: true
Layout.alignment: Qt . AlignBottom
Layout.leftMargin: Style . current . padding
Layout.rightMargin: Style . current . padding
viewWidth: ( scrollView . availableWidth - 32 )
permissionsModel: d . channelEditModel . channelPermissionsModel
assetsModel: root . assetsModel
collectiblesModel: root . collectiblesModel
viewOnlyCanAddReaction: root . viewOnlyCanAddReaction
channelsModel: d . channelEditModel . liveChannelsModel
communityDetails: d . communityDetails
showChannelOptions: true
allowIntroPanel: false
onRemovePermissionRequested: {
console . assert ( d . channelEditModel . removePermission ( index ) )
}
onDuplicatePermissionRequested: {
const item = StatusQUtils . ModelUtils . get ( d . channelEditModel . channelPermissionsModel , index ) ;
const properties = {
holdingsToEditModel: item . holdingsListModel ,
channelsToEditModel: item . channelsListModel ,
permissionTypeToEdit: item . permissionType ,
isPrivateToEditValue: item . isPrivate ,
header: null ,
topPadding: - root . subHeaderPadding - 8 ,
leftPadding: 0 ,
rightPadding: 16 ,
viewWidth: scrollView . availableWidth - 32
}
editPermissionView . pushEditView ( properties ) ;
editPermissionView . currentItem . resetChanges ( )
d . currentPage = CreateChannelPopup . CurrentPage . ChannelPermissions ;
}
onEditPermissionRequested: {
const item = d . channelEditModel . channelPermissionsModel . get ( index ) ;
const requireHoldings = ( item . holdingsListModel . count ? ? item . holdingsListModel . rowCount ( ) ) > 0 ;
const properties = {
permissionKeyToEdit: item . key ,
holdingsToEditModel: item . holdingsListModel ,
channelsToEditModel: item . channelsListModel ,
permissionTypeToEdit: item . permissionType ,
isPrivateToEditValue: item . isPrivate ,
header: null ,
topPadding: - root . subHeaderPadding - 8 ,
leftPadding: 0 ,
rightPadding: 16 ,
viewWidth: scrollView . availableWidth - 32
}
editPermissionView . pushEditView ( properties ) ;
editPermissionView . currentItem . resetChanges ( )
d . currentPage = CreateChannelPopup . CurrentPage . ChannelPermissions ;
}
onUserRestrictionsToggled: {
d . viewOnlyCanAddReaction = checked ;
}
}
}
readonly property var nextAction: function ( ) {
if ( ! root . isDiscordImport ) {
d . saveAndClose ( )
} else {
d . currentPage = CreateChannelPopup . CurrentPage . DiscordImportUploadFile ;
}
}
} ,
ColorPanel {
id: colorPanel
readonly property string stackTitleText: qsTr ( "Channel Colour" )
readonly property string nextButtonText: qsTr ( "Select Channel Colour" )
padding: 0
leftPadding: 16
rightPadding: 16
height: Math . min ( parent . height , 624 )
2024-05-03 15:51:25 +00:00
property bool colorSelected: ! ! root . channelColor && root . channelColor != Theme . palette . primaryColor1
color: root . channelColor || Theme . palette . primaryColor1
2024-02-06 09:31:36 +00:00
onAccepted: {
colorSelected = true ; d . colorPickerOpened = false ; d . currentPage = CreateChannelPopup . CurrentPage . ChannelDetails ;
}
readonly property var nextAction: function ( ) {
accepted ( ) ;
}
} ,
PermissionsSettingsPanel {
id: editPermissionView
2024-03-12 08:36:03 +00:00
2024-02-06 09:31:36 +00:00
leftPadding: 16
rightPadding: 16
initialPage.header: null
initialPage.topPadding: 0
initialPage.leftPadding: 0
viewWidth: scrollView . availableWidth - 32
2024-02-07 10:30:46 +00:00
assetsModel: root . assetsModel
collectiblesModel: root . collectiblesModel
permissionsModel: d . channelEditModel . channelPermissionsModel
channelsModel: d . channelEditModel . liveChannelsModel
communityDetails: d . communityDetails
showChannelSelector: false
2024-07-19 18:45:40 +00:00
ensCommunityPermissionsEnabled: root . ensCommunityPermissionsEnabled
2024-02-07 10:30:46 +00:00
2024-02-06 09:31:36 +00:00
readonly property string nextButtonText: ! ! currentItem . permissionKeyToEdit ?
qsTr ( "Update permission" ) : qsTr ( "Create permission" )
readonly property string stackTitleText: ! ! currentItem . permissionKeyToEdit ?
qsTr ( "Edit #%1 permission" ) . arg ( nameInput . text ) : qsTr ( "New #%1 permission" ) . arg ( nameInput . text )
readonly property string deleteButtonText: ! ! currentItem . permissionKeyToEdit ?
qsTr ( "Revert changes" ) : ""
2024-02-07 10:30:46 +00:00
readonly property bool canGoNext: ! ! currentItem && ! ! currentItem . isSaveEnabled ? currentItem.isSaveEnabled : false
2024-02-06 09:31:36 +00:00
readonly property bool deleteButtonEnabled: editPermissionView . canGoNext
2024-02-07 10:30:46 +00:00
2024-02-06 09:31:36 +00:00
readonly property var nextDeleteAction: function ( ) {
if ( ! ! currentItem . permissionKeyToEdit ) {
currentItem . resetChanges ( ) ;
}
}
readonly property var nextAction: function ( ) {
if ( ! ! currentItem . permissionKeyToEdit ) {
currentItem . updatePermission ( ) ;
} else {
currentItem . createPermission ( ) ;
}
}
onCreatePermissionRequested: {
d . channelEditModel . appendPermission ( holdings , channels , permissionType , isPrivate )
d . currentPage = CreateChannelPopup . CurrentPage . ChannelDetails ;
}
onUpdatePermissionRequested: {
d . channelEditModel . editPermission ( key , permissionType , holdings , channels , isPrivate )
d . currentPage = CreateChannelPopup . CurrentPage . ChannelDetails ;
2021-07-07 12:45:11 +00:00
}
2020-12-11 20:29:46 +00:00
}
2023-09-01 07:58:48 +00:00
]
2021-07-07 12:45:11 +00:00
MessageDialog {
id: creatingError
2023-09-01 07:58:48 +00:00
title: qsTr ( "Error creating the channel" )
2021-07-07 12:45:11 +00:00
icon: StandardIcon . Critical
standardButtons: StandardButton . Ok
2020-12-11 20:29:46 +00:00
}
}