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
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
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
2023-09-01 07:58:48 +00:00
import AppLayouts . Communities . controls 1.0
2023-09-14 13:34:36 +00:00
import AppLayouts . Communities . panels 1.0
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
2023-09-01 07:58:48 +00:00
property var communitiesStore
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
2023-09-01 07:58:48 +00:00
2022-01-31 18:57:05 +00:00
property string chatId: ""
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: ""
2022-03-10 19:28:37 +00:00
property string channelColor: ""
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
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
2022-08-01 14:17:28 +00:00
signal createCommunityChannel ( string chName , string chDescription , string chEmoji , string chColor , string chCategoryId )
signal editCommunityChannel ( string chName , string chDescription , string chEmoji , string chColor , string chCategoryId )
2022-08-05 06:47:19 +00:00
signal deleteCommunityChannel ( )
2021-06-02 19:43:33 +00:00
2023-09-01 07:58:48 +00:00
width: 640
2023-05-08 15:10:47 +00:00
QtObject {
id: d
2023-09-01 07:58:48 +00:00
function isFormValid ( ) {
return nameInput . valid && descriptionTextArea . valid &&
Utils . validateAndReturnError ( colorDialog . color . toString ( ) . toUpperCase ( ) , communityColorValidator ) === ""
}
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 {
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
}
}
}
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
}
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" ) :
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 ++
2022-03-07 14:56:05 +00:00
}
2023-09-01 07:58:48 +00:00
}
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 ( )
}
2022-03-07 14:56:05 +00:00
}
}
2023-09-01 07:58:48 +00:00
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 ( )
2020-12-11 20:29:46 +00:00
}
2023-09-01 07:58:48 +00:00
readonly property StatusButton deleteChannelButton: StatusButton {
objectName: "deleteCommunityChannelBtn"
2023-09-14 13:34:36 +00:00
visible: isEdit && isDeleteable && ! isDiscordImport && typeof ( replaceItem ) === "undefined"
2023-09-01 07:58:48 +00:00
text: qsTr ( "Delete channel" )
type: StatusBaseButton . Type . Danger
onClicked: root . deleteCommunityChannel ( )
}
2020-12-11 20:29:46 +00:00
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
}
colorDialog . color = root . channelColor
} 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: [
2023-05-31 20:58:23 +00:00
ColumnLayout {
2023-09-01 07:58:48 +00:00
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 ++
}
2021-07-07 12:45:11 +00:00
2023-09-01 07:58:48 +00:00
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
}
}
2023-05-31 20:58:23 +00:00
2023-09-01 07:58:48 +00:00
Rectangle {
2023-05-31 20:58:23 +00:00
Layout.fillWidth: true
2023-09-01 07:58:48 +00:00
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
}
2022-05-12 17:18:25 +00:00
}
2021-03-29 12:28:41 +00:00
}
2023-09-01 07:58:48 +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
}
2022-03-10 17:01:17 +00:00
}
2023-09-01 07:58:48 +00:00
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
}
}
2023-05-08 15:10:47 +00:00
}
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
} ,
2020-12-11 20:29:46 +00:00
2023-09-01 07:58:48 +00:00
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 )
2022-03-10 19:28:37 +00:00
}
2023-09-01 07:58:48 +00:00
Component {
id: progressComponent
DiscordImportProgressContents {
width: root . availableWidth
store: root . communitiesStore
importingSingleChannel: true
onClose: root . close ( )
}
2022-03-10 19:28:37 +00:00
}
2022-05-09 16:12:17 +00:00
Item {
2023-05-31 20:58:23 +00:00
Layout.fillWidth: true
2023-09-01 07:58:48 +00:00
Layout.fillHeight: true
visible: ! root . communitiesStore . discordChannelsModel . count
Loader {
anchors.centerIn: parent
active: parent . visible
sourceComponent: StatusLoadingIndicator {
width: 50
height: 50
}
}
2022-05-09 16:12:17 +00:00
}
2023-09-01 07:58:48 +00:00
ColumnLayout {
spacing: 12
visible: root . communitiesStore . discordChannelsModel . count
2022-03-10 19:28:37 +00:00
2023-09-01 07:58:48 +00:00
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
}
2022-03-10 19:28:37 +00:00
2023-09-01 07:58:48 +00:00
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
}
}
2022-03-10 19:28:37 +00:00
2023-09-01 07:58:48 +00:00
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 )
}
}
}
}
}
}
}
}
]
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
readonly property bool canGoNext: d . isFormValid ( )
property ScrollBar vScrollBar: ScrollBar . vertical
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
2022-03-10 19:28:37 +00:00
}
}
2023-09-01 07:58:48 +00:00
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
}
]
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.preferredHeight: 16
Layout.fillWidth: true
2022-03-10 19:28:37 +00:00
}
2023-09-14 13:34:36 +00:00
ColorPicker {
id: colorDialog
2023-09-01 07:58:48 +00:00
Layout.fillWidth: true
2023-09-14 13:34:36 +00:00
title: qsTr ( "Channel colour" )
color: root . isEdit && root . channelColor ? root.channelColor : Theme . palette . primaryColor1
onPick: root . replace ( colorPanel )
Component {
id: colorPanel
ColorPanel {
title: qsTr ( "Channel colour" )
buttonText: qsTr ( "Select Colour" )
Component.onCompleted: color = colorDialog . color
onAccepted: {
colorDialog . color = color
root . replaceItem = undefined
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
2023-09-14 13:34:36 +00:00
Item {
Layout.preferredHeight: 16
Layout.fillWidth: true
2023-09-01 07:58:48 +00:00
}
2020-12-11 20:29:46 +00:00
2023-09-01 07:58:48 +00:00
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
}
]
2022-08-01 14:17:28 +00:00
}
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
}
}