diff options
Diffstat (limited to 'eventsview-plugins/eventsview-plugin-fediverse/FediverseFeedItem.qml')
| -rw-r--r-- | eventsview-plugins/eventsview-plugin-fediverse/FediverseFeedItem.qml | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/eventsview-plugins/eventsview-plugin-fediverse/FediverseFeedItem.qml b/eventsview-plugins/eventsview-plugin-fediverse/FediverseFeedItem.qml new file mode 100644 index 0000000..16dc191 --- /dev/null +++ b/eventsview-plugins/eventsview-plugin-fediverse/FediverseFeedItem.qml @@ -0,0 +1,362 @@ +/* + * SPDX-FileCopyrightText: 2013 - 2026 Jolla Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +import QtQuick 2.0 +import Sailfish.Silica 1.0 +import Sailfish.Share 1.0 +import Sailfish.TextLinking 1.0 +import org.nemomobile.lipstick 0.1 +import "shared" + +SocialMediaFeedItem { + id: item + + property variant imageList + property string resolvedStatusUrl: model && model.url ? model.url.toString() : "" + property string postId + property QtObject postActions + property int likeCount: model && model.favouritesCount ? model.favouritesCount : 0 + property int commentCount: model && model.repliesCount ? model.repliesCount : 0 + property int boostCount: model && model.reblogsCount ? model.reblogsCount : 0 + property bool favourited: model ? !!model.favourited : false + property bool reblogged: model ? !!model.reblogged : false + property int _likeCountOverride: -1 + property int _boostCountOverride: -1 + property int _favouritedOverride: -1 + property int _rebloggedOverride: -1 + property bool isFavourited: _favouritedOverride >= 0 ? _favouritedOverride === 1 : favourited + property bool isReblogged: _rebloggedOverride >= 0 ? _rebloggedOverride === 1 : reblogged + readonly property bool housekeeping: Lipstick.compositor.eventsLayer.housekeeping + readonly property bool lockScreenActive: Lipstick.compositor.lockScreenLayer.deviceIsLocked + property bool _pendingOpenActionMenu: false + property bool _contextMenuOpen: false + property var _actionMenu + property real _contextMenuHeight: (_contextMenuOpen && _actionMenu) ? _actionMenu.height : 0 + + property string _booster: model && model.boostedBy ? model.boostedBy.toString() : "" + property string _displayName: model && model.name ? model.name.toString() : "" + property string _accountName: model && model.accountName ? model.accountName.toString() : "" + property string _bodyText: model && model.body ? model.body.toString() : "" + //: Action label shown in Fediverse interaction menu. + //% "Favourite" + readonly property string _favouriteActionText: qsTrId("lipstick-jolla-home-la-fediverse_favourite") + //: Action label shown in Fediverse interaction menu when the post is already favourited. + //% "Unfavourite" + readonly property string _unfavouriteActionText: qsTrId("lipstick-jolla-home-la-fediverse_unfavourite") + //: Action label shown in Fediverse interaction menu. + //% "Boost" + readonly property string _boostActionText: qsTrId("lipstick-jolla-home-la-fediverse_boost") + //: Action label shown in Fediverse interaction menu when the post is already boosted. + //% "Undo boost" + readonly property string _unboostActionText: qsTrId("lipstick-jolla-home-la-fediverse_unboost") + //: Action label shown in Fediverse interaction menu. + //% "Share" + readonly property string _shareActionText: qsTrId("lipstick-jolla-home-la-fediverse_share") + //: Link title used when sharing a Fediverse post. + //% "Post from Fediverse" + readonly property string _shareLinkTitle: qsTrId("lipstick-jolla-home-la-fediverse_share_link_title") + property var _shareAction: ShareAction { + title: item._shareActionText + } + + timestamp: model.timestamp + onRefreshTimeCountChanged: formattedTime = Format.formatDate(model.timestamp, Format.TimeElapsed) + onLockScreenActiveChanged: { + if (lockScreenActive && _actionMenu) { + _actionMenu.close() + } + } + onPressAndHold: function(mouse) { + if (mouse) { + mouse.accepted = true + } + _pendingOpenActionMenu = !lockScreenActive + && postActions + && actionPostId().length > 0 + && actionAccountId() >= 0 + openActionMenuTimer.restart() + } + onHousekeepingChanged: { + if (housekeeping && _pendingOpenActionMenu) { + Lipstick.compositor.eventsLayer.setHousekeeping(false) + } + } + Component.onDestruction: { + if (_actionMenu) { + _actionMenu.destroy() + _actionMenu = null + } + } + + avatar.y: item._booster.length > 0 + ? topMargin + boosterIcon.height + Theme.paddingSmall + : topMargin + contentHeight: Math.max(content.y + content.height, avatar.y + avatar.height) + bottomMargin + _contextMenuHeight + topMargin: item._booster.length > 0 ? Theme.paddingMedium : Theme.paddingLarge + userRemovable: false + + SocialReshareIcon { + id: boosterIcon + + anchors { + right: avatar.right + top: parent.top + topMargin: item.topMargin + } + visible: item._booster.length > 0 + highlighted: item.highlighted + iconSource: "image://theme/icon-s-repost" + } + + SocialReshareText { + anchors { + left: content.left + right: content.right + verticalCenter: boosterIcon.verticalCenter + } + highlighted: item.highlighted + text: item._booster.length > 0 + ? //: Shown above a post that is boosted by another user. %1 = name of user who boosted + //% "%1 boosted" + qsTrId("lipstick-jolla-home-la-boosted_by").arg(item._booster) + : "" + } + + Column { + id: content + + anchors { + left: avatar.right + leftMargin: Theme.paddingMedium + top: avatar.top + } + width: parent.width - x + + Label { + width: parent.width + truncationMode: TruncationMode.Fade + text: item._displayName + color: item.highlighted ? Theme.highlightColor : Theme.primaryColor + textFormat: Text.PlainText + } + + Label { + width: parent.width + truncationMode: TruncationMode.Fade + text: item._accountName.length > 0 && item._accountName.charAt(0) !== "@" + ? "@" + item._accountName + : item._accountName + font.pixelSize: Theme.fontSizeSmall + color: item.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor + textFormat: Text.PlainText + } + + LinkedText { + width: parent.width + elide: Text.ElideRight + wrapMode: Text.Wrap + font.pixelSize: Theme.fontSizeSmall + shortenUrl: true + color: item.highlighted ? Theme.highlightColor : Theme.primaryColor + linkColor: Theme.highlightColor + plainText: item._bodyText + } + + SocialPostMetadataRow { + id: metadataRow + + width: parent.width + highlighted: item.highlighted + commentCount: item.commentCount + likeCount: item._likeCountOverride >= 0 ? item._likeCountOverride : item.likeCount + repostCount: item._boostCountOverride >= 0 ? item._boostCountOverride : item.boostCount + liked: item.isFavourited + reposted: item.isReblogged + timeText: item.formattedTime + addBottomPadding: previewRow.visible + } + + SocialMediaPreviewRow { + id: previewRow + + width: parent.width + Theme.horizontalPageMargin // extend to right edge of notification area + imageList: item.imageList + downloader: item.downloader + accountId: item.accountId + connectedToNetwork: item.connectedToNetwork + highlighted: item.highlighted + eventsColumnMaxWidth: item.eventsColumnMaxWidth - item.avatar.width + } + } + + function actionPostId() { + if (item.postId.length > 0) { + return item.postId + } + return model && model.fediverseId ? model.fediverseId.toString() : "" + } + + function actionAccountId() { + var parsed = Number(item.accountId) + return isNaN(parsed) ? -1 : parsed + } + + function shareStatusUrl() { + return model && model.url ? model.url.toString() : "" + } + + function topLevelParent() { + var p = item + while (p && p.parent) { + p = p.parent + } + return p + } + + function openActionMenu() { + if (_actionMenu) { + _actionMenu.destroy() + _actionMenu = null + } + + var parentItem = topLevelParent() + _actionMenu = actionMenuComponent.createObject(parentItem) + if (_actionMenu) { + _actionMenu.open(item) + } + } + + Connections { + target: item.postActions ? item.postActions : null + + onActionSucceeded: { + if (accountId !== item.actionAccountId() || statusId !== item.actionPostId()) { + return + } + + if (favouritesCount >= 0) { + item._likeCountOverride = favouritesCount + } + if (reblogsCount >= 0) { + item._boostCountOverride = reblogsCount + } + item._favouritedOverride = favourited ? 1 : 0 + item._rebloggedOverride = reblogged ? 1 : 0 + item._contextMenuOpen = false + + if (item._accountDelegate) { + item._accountDelegate.sync() + } + } + + onActionFailed: { + if (accountId !== item.actionAccountId() || statusId !== item.actionPostId()) { + return + } + console.warn("Fediverse action failed:", action, errorMessage) + item._contextMenuOpen = false + } + } + + Component { + id: actionMenuComponent + + SocialInteractionContextMenu { + id: actionMenu + z: 10000 + mapSourceItem: _contentColumn + actionEnabled: item.postActions + && item.actionPostId().length > 0 + && item.actionAccountId() >= 0 + && !item.lockScreenActive + && !item.housekeeping + interactionItems: [ + { + name: "like", + // U+2605 BLACK STAR + symbol: "\u2605", + active: item.isFavourited, + inactiveText: item._favouriteActionText, + activeText: item._unfavouriteActionText + }, + { + name: "reblog", + // U+21BB CLOCKWISE OPEN CIRCLE ARROW + symbol: "\u21BB", + active: item.isReblogged, + inactiveText: item._boostActionText, + activeText: item._unboostActionText + }, + { + name: "share", + // U+260D OPPOSITION (ironic doncha think) + symbol: "\u260D", + active: false, + inactiveText: item._shareActionText, + activeText: item._shareActionText + } + ] + + onInteractionMenuOpened: item._contextMenuOpen = true + onInteractionMenuClosed: { + item._contextMenuOpen = false + destroy() + item._actionMenu = null + } + + onInteractionTriggered: function(actionName) { + if (!actionEnabled) { + return + } + var postId = item.actionPostId() + var accountId = item.actionAccountId() + if (actionName === "like") { + if (item.isFavourited) { + item.postActions.unfavourite(accountId, postId) + } else { + item.postActions.favourite(accountId, postId) + } + } else if (actionName === "reblog") { + if (item.isReblogged) { + item.postActions.unboost(accountId, postId) + } else { + item.postActions.boost(accountId, postId) + } + } else if (actionName === "share") { + var shareUrl = item.shareStatusUrl() + if (shareUrl.length === 0) { + return + } + item._shareAction.resources = [{ + "data": shareUrl, + "linkTitle": item._shareLinkTitle, + "type": "text/x-url" + }] + item._shareAction.trigger() + } + } + } + } + + Timer { + id: openActionMenuTimer + + interval: 0 + repeat: false + onTriggered: { + if (item.lockScreenActive) { + item._pendingOpenActionMenu = false + return + } + Lipstick.compositor.eventsLayer.setHousekeeping(false) + if (item._pendingOpenActionMenu) { + item._contextMenuOpen = false + item.openActionMenu() + } + item._pendingOpenActionMenu = false + } + } +} |
