Warning, /network/tokodon/src/content/ui/main.qml is written in an unsupported language. File is not indexed.

0001 // SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
0002 // SPDX-FileCopyrightText: 2020 Han Young <hanyoung@protonmail.com>
0003 // SPDX-FileCopyrightText: 2020 Devin Lin <espidev@gmail.com>
0004 // SPDX-License-Identifier: GPL-3.0-only
0005 
0006 import QtQuick 2.15
0007 import org.kde.kirigami 2.19 as Kirigami
0008 import QtQuick.Controls 2.15 as QQC2
0009 import QtQuick.Layouts 1.15
0010 import QtQml.Models 2.15
0011 import org.kde.kmasto 1.0
0012 import org.kde.kirigamiaddons.delegates 1.0 as Delegates
0013 
0014 import "./StatusComposer"
0015 import "./StatusDelegate"
0016 
0017 Kirigami.ApplicationWindow {
0018     id: appwindow
0019 
0020     property bool isShowingFullScreenImage: false
0021 
0022     minimumWidth: Kirigami.Units.gridUnit * 15
0023     minimumHeight: Kirigami.Units.gridUnit * 20
0024 
0025     pageStack {
0026         defaultColumnWidth: appwindow.width
0027 
0028         globalToolBar {
0029             canContainHandles: true
0030             style: Kirigami.ApplicationHeaderStyle.ToolBar
0031             showNavigationButtons: if (applicationWindow().pageStack.currentIndex > 0
0032                 || applicationWindow().pageStack.currentIndex > 0) {
0033                 Kirigami.ApplicationHeaderStyle.ShowBackButton
0034             } else {
0035                 0
0036             }
0037         }
0038     }
0039 
0040     function startupAccountCheck() {
0041         if (AccountManager.hasAccounts) {
0042             pageStack.push(mainTimeline, {
0043                 name: 'home',
0044             });
0045         } else {
0046             pageStack.push('qrc:/content/ui/LoginPage.qml');
0047         }
0048     }
0049 
0050     Component.onCompleted: {
0051         if (AccountManager.isReady) {
0052             startupAccountCheck();
0053         }
0054     }
0055 
0056     Connections {
0057         target: AccountManager
0058 
0059         function onAccountSelected() {
0060             pageStack.pop(pageStack.get(0))
0061         }
0062 
0063         function onAccountRemoved() {
0064             if (!AccountManager.hasAccounts) {
0065                 pageStack.replace('qrc:/content/ui/LoginPage.qml');
0066                 globalDrawer.drawerOpen = false
0067             }
0068         }
0069 
0070         function onAccountsReloaded() {
0071             pageStack.replace(mainTimeline, {
0072                 name: "home"
0073             });
0074         }
0075 
0076         function onAccountsReady() {
0077             appwindow.startupAccountCheck();
0078         }
0079     }
0080 
0081     Connections {
0082         target: Controller
0083 
0084         function onOpenPost(id) {
0085             Navigation.openThread(id)
0086         }
0087 
0088         function onOpenAccount(id) {
0089             Navigation.openAccount(id)
0090         }
0091     }
0092 
0093     Connections {
0094         target: Navigation
0095 
0096         function onOpenStatusComposer() {
0097             pageStack.layers.push("./StatusComposer/StatusComposer.qml", {
0098                 purpose: StatusComposer.New
0099             });
0100         }
0101 
0102         function onReplyTo(inReplyTo, mentions, visibility, authorIdentity, post) {
0103             if (!mentions.includes(`@${authorIdentity.account}`)) {
0104                 mentions.push(`@${authorIdentity.account}`);
0105             }
0106             pageStack.layers.push("./StatusComposer/StatusComposer.qml", {
0107                 purpose: StatusComposer.Reply,
0108                 inReplyTo: inReplyTo,
0109                 mentions: mentions,
0110                 visibility: visibility,
0111                 previewPost: post
0112             });
0113         }
0114 
0115         function onOpenThread(postId) {
0116             if (!pageStack.currentItem.postId || pageStack.currentItem.postId !== postId) {
0117                 pageStack.push("qrc:/content/ui/ThreadPage.qml", {
0118                     postId: postId,
0119                 });
0120             }
0121         }
0122 
0123         function onOpenAccount(accountId) {
0124             if (!pageStack.currentItem.accountId || pageStack.currentItem.accountId !== accountId) {
0125                 pageStack.push('qrc:/content/ui/AccountInfo.qml', {
0126                     accountId: accountId,
0127                 });
0128             }
0129         }
0130 
0131         function onOpenTag(tag) {
0132             pageStack.push(tagModelComponent, {
0133                 hashtag: tag,
0134             })
0135         }
0136     }
0137 
0138     globalDrawer: Kirigami.OverlayDrawer {
0139         id: drawer
0140         enabled: AccountManager.hasAccounts && AccountManager.isReady
0141         edge: Qt.application.layoutDirection === Qt.RightToLeft ? Qt.RightEdge : Qt.LeftEdge
0142         modal: !enabled || Kirigami.Settings.isMobile || Kirigami.Settings.tabletMode || (applicationWindow().width < Kirigami.Units.gridUnit * 50 && !collapsed) // Only modal when not collapsed, otherwise collapsed won't show.
0143         z: modal ? Math.round(position * 10000000) : 100
0144         drawerOpen: !Kirigami.Settings.isMobile && enabled
0145         width: Kirigami.Units.gridUnit * 16
0146         Behavior on width {
0147             NumberAnimation {
0148                 duration: Kirigami.Units.longDuration
0149                 easing.type: Easing.InOutQuad
0150             }
0151         }
0152         Kirigami.Theme.colorSet: Kirigami.Theme.View
0153         Kirigami.Theme.inherit: false
0154 
0155         handleClosedIcon.source: modal ? null : "sidebar-expand-left"
0156         handleOpenIcon.source: modal ? null : "sidebar-collapse-left"
0157         handleVisible: modal && !isShowingFullScreenImage && enabled
0158         onModalChanged: drawerOpen = !modal;
0159 
0160         leftPadding: 0
0161         rightPadding: 0
0162         topPadding: 0
0163         bottomPadding: 0
0164 
0165         contentItem: ColumnLayout {
0166             spacing: 0
0167 
0168             QQC2.ToolBar {
0169                 Layout.fillWidth: true
0170                 Layout.preferredHeight: pageStack.globalToolBar.preferredHeight
0171                 Layout.bottomMargin: Kirigami.Units.smallSpacing / 2
0172 
0173                 leftPadding: 3
0174                 rightPadding: 3
0175                 topPadding: 3
0176                 bottomPadding: 3
0177 
0178                 visible: !Kirigami.Settings.isMobile
0179 
0180                 contentItem: SearchField {}
0181             }
0182 
0183             UserInfo {
0184                 Layout.fillWidth: true
0185             }
0186 
0187             Kirigami.Separator {
0188                 Layout.fillWidth: true
0189                 Layout.margins: Kirigami.Units.smallSpacing
0190             }
0191 
0192             QQC2.ButtonGroup {
0193                 id: pageButtonGroup
0194             }
0195 
0196             Repeater {
0197                 model: [homeAction, notificationAction, searchAction, followRequestAction, localTimelineAction, globalTimelineAction, exploreAction, conversationAction, favouritesAction, bookmarksAction]
0198                 Delegates.RoundedItemDelegate {
0199                     required property var modelData
0200                     QQC2.ButtonGroup.group: pageButtonGroup
0201 
0202                     padding: Kirigami.Units.largeSpacing
0203                     action: modelData
0204                     Layout.fillWidth: true
0205                     visible: modelData.visible
0206                 }
0207             }
0208 
0209             Item {
0210                 Layout.fillHeight: true
0211             }
0212 
0213             Delegates.RoundedItemDelegate {
0214                 icon.name: "lock"
0215                 onClicked: pageStack.pushDialogLayer('qrc:/content/ui/ModerationTools/ModerationToolPage.qml', {}, { title: i18n("Moderation Tools") })
0216                 text: i18nc("@action:button", "Open Moderation Tools")
0217                 visible: AccountManager.selectedAccount && (AccountManager.selectedAccount.identity.permission & AdminAccountInfo.ManageUsers)
0218                 padding: Kirigami.Units.largeSpacing
0219 
0220                 Layout.fillWidth: true
0221             }
0222 
0223             Delegates.RoundedItemDelegate {
0224                 icon.name: "settings-configure"
0225                 onClicked: pageStack.pushDialogLayer('qrc:/content/ui/Settings/SettingsPage.qml', {}, { title: i18n("Configure") })
0226                 text: i18n("Open settings")
0227                 padding: Kirigami.Units.largeSpacing
0228 
0229                 Layout.fillWidth: true
0230             }
0231         }
0232     }
0233 
0234     property Kirigami.Action homeAction: Kirigami.Action {
0235         icon.name: "go-home-large"
0236         text: i18n("Home")
0237         checkable: true
0238         checked: true
0239         onTriggered: {
0240             pageStack.clear();
0241             pageStack.push(mainTimeline, {
0242                 name: "home"
0243             });
0244             checked = true;
0245             if (Kirigami.Settings.isMobile || drawer.modal) {
0246                 drawer.drawerOpen = false;
0247             }
0248         }
0249     }
0250     property Kirigami.Action notificationAction: Kirigami.Action {
0251         icon.name: "notifications"
0252         text: i18n("Notifications")
0253         checkable: true
0254         onTriggered: {
0255             pageStack.clear();
0256             pageStack.push(notificationTimeline);
0257             checked = true;
0258             if (Kirigami.Settings.isMobile || drawer.modal) {
0259                 drawer.drawerOpen = false;
0260             }
0261         }
0262     }
0263     property Kirigami.Action followRequestAction: Kirigami.Action {
0264         icon.name: "list-add-user"
0265         text: i18n("Follow Requests")
0266         checkable: true
0267         visible: AccountManager.hasAccounts && AccountManager.selectedAccount && AccountManager.selectedAccount.hasFollowRequests
0268         onTriggered: {
0269             pageStack.clear();
0270             pageStack.push(socialGraphComponent, {
0271                 name: "request"
0272             });
0273             checked = true;
0274             if (Kirigami.Settings.isMobile || drawer.modal) {
0275                 drawer.drawerOpen = false;
0276             }
0277         }
0278     }
0279     property Kirigami.Action localTimelineAction: Kirigami.Action {
0280         icon.name: "system-users"
0281         text: i18n("Local")
0282         checkable: true
0283         onTriggered: {
0284             pageStack.clear();
0285             pageStack.push(mainTimeline, {
0286                 name: "public",
0287             });
0288             checked = true;
0289             if (Kirigami.Settings.isMobile || drawer.modal) {
0290                 drawer.drawerOpen = false;
0291             }
0292         }
0293     }
0294     property Kirigami.Action globalTimelineAction: Kirigami.Action {
0295         icon.name: "kstars_xplanet"
0296         text: i18n("Global")
0297         checkable: true
0298         onTriggered: {
0299             pageStack.clear();
0300             pageStack.push(mainTimeline, {
0301                 name: "federated",
0302             });
0303             checked = true;
0304             if (Kirigami.Settings.isMobile || drawer.modal) {
0305                 drawer.drawerOpen = false;
0306             }
0307         }
0308     }
0309 
0310     property Kirigami.Action conversationAction: Kirigami.Action {
0311         icon.name: "tokodon-chat-reply"
0312         text: i18n("Conversation")
0313         checkable: true
0314         onTriggered: {
0315             pageStack.clear();
0316             pageStack.push("qrc:/content/ui/ConversationPage.qml");
0317             checked = true;
0318             if (Kirigami.Settings.isMobile || drawer.modal) {
0319                 drawer.drawerOpen = false;
0320             }
0321         }
0322     }
0323 
0324     property Kirigami.Action favouritesAction: Kirigami.Action {
0325         icon.name: "favorite"
0326         text: i18n("Favourites")
0327         checkable: true
0328         onTriggered: {
0329             pageStack.clear();
0330             pageStack.push(mainTimeline, {
0331                 name: "favourites",
0332             });
0333             checked = true;
0334             if (Kirigami.Settings.isMobile || drawer.modal) {
0335                 drawer.drawerOpen = false;
0336             }
0337         }
0338     }
0339 
0340     property Kirigami.Action bookmarksAction: Kirigami.Action {
0341         icon.name: "bookmarks"
0342         text: i18n("Bookmarks")
0343         checkable: true
0344         onTriggered: {
0345             pageStack.clear();
0346             pageStack.push(mainTimeline, {
0347                 name: "bookmarks",
0348             });
0349             checked = true;
0350             if (Kirigami.Settings.isMobile || drawer.modal) {
0351                 drawer.drawerOpen = false;
0352             }
0353         }
0354     }
0355 
0356     property Kirigami.Action exploreAction: Kirigami.Action {
0357         icon.name: "kstars_planets"
0358         text: i18n("Explore")
0359         checkable: true
0360         onTriggered: {
0361             pageStack.clear();
0362             pageStack.push(exploreTimeline);
0363             checked = true;
0364             if (Kirigami.Settings.isMobile || drawer.modal) {
0365                 drawer.drawerOpen = false;
0366             }
0367         }
0368     }
0369 
0370     property Kirigami.Action searchAction: Kirigami.Action {
0371         icon.name: "search"
0372         text: i18n("Search")
0373         checkable: true
0374         visible: Kirigami.Settings.isMobile
0375         onTriggered: {
0376             pageStack.clear();
0377             pageStack.push("qrc:/content/ui/SearchPage.qml");
0378             checked = true;
0379             if (Kirigami.Settings.isMobile) {
0380                 drawer.drawerOpen = false;
0381             }
0382         }
0383     }
0384 
0385     property Kirigami.NavigationTabBar tabBar: Kirigami.NavigationTabBar {
0386         // Make sure we take in count drawer width
0387         visible: pageStack.layers.depth <= 1 && AccountManager.hasAccounts && !appwindow.wideScreen
0388         actions: [homeAction, notificationAction, localTimelineAction, globalTimelineAction]
0389     }
0390 
0391     footer: Kirigami.Settings.isMobile ? tabBar : null
0392 
0393     contextDrawer: Kirigami.ContextDrawer {
0394         id: contextDrawer
0395     }
0396 
0397     Component {
0398         id: mainTimeline
0399         TimelinePage {
0400             id: timelinePage
0401             property string name
0402             model: MainTimelineModel {
0403                 id: timelineModel
0404                 name: timelinePage.name
0405             }
0406         }
0407     }
0408 
0409     Component {
0410         id: socialGraphComponent
0411         SocialGraphPage {
0412             id: socialGraphPage
0413             property alias name: socialGraphModel.name
0414             property alias accountId: socialGraphModel.accountId
0415             model: SocialGraphModel {
0416                 id: socialGraphModel
0417                 name: socialGraphPage.name
0418                 accountId: socialGraphPage.accountId
0419             }
0420         }
0421     }
0422 
0423     Component {
0424         id: notificationTimeline
0425         NotificationPage { }
0426     }
0427 
0428     Component {
0429         id: exploreTimeline
0430         ExplorePage { }
0431     }
0432 
0433     property Item hoverLinkIndicator: QQC2.Control {
0434         parent: overlay.parent
0435         property alias text: linkText.text
0436         opacity: text.length > 0 ? 1 : 0
0437         visible: !Kirigami.Settings.isMobile && !text.startsWith("hashtag:") && !text.startsWith("account:")
0438 
0439         z: 999990
0440         x: 0
0441         y: parent.height - implicitHeight
0442         contentItem: QQC2.Label {
0443             id: linkText
0444         }
0445         Kirigami.Theme.colorSet: Kirigami.Theme.View
0446         background: Rectangle {
0447              color: Kirigami.Theme.backgroundColor
0448         }
0449     }
0450 
0451     Component {
0452         id: tagModelComponent
0453         TimelinePage {
0454             id: tagPage
0455             property string hashtag
0456             model: TagsTimelineModel {
0457                 hashtag: tagPage.hashtag
0458             }
0459         }
0460     }
0461 
0462     Timer {
0463         id: followRequestTimer
0464         running: AccountManager.hasAccounts
0465         interval: 1800000 // 30 minutes
0466         onTriggered: if (AccountManager.hasAccounts && AccountManager.selectedAccount) {
0467             AccountManager.selectedAccount.checkForFollowRequests();
0468         }
0469     }
0470 
0471     Rectangle {
0472         anchors.fill: parent
0473         visible: !AccountManager.isReady
0474         color: Kirigami.Theme.backgroundColor
0475 
0476         Kirigami.LoadingPlaceholder {
0477             anchors.centerIn: parent
0478         }
0479     }
0480 }