1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-07-15 04:55:23 +02:00

Donation campaign: add banner in smartlist

GitLab: #1334
Change-Id: I53b23eabab47389b9bea50f54afac28d41741b0b
This commit is contained in:
lcoursodon 2023-09-27 11:26:18 -04:00 committed by Andreas Traczyk
parent 6b9ce14ca9
commit b9d24298f7
7 changed files with 282 additions and 93 deletions

View file

@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="43" height="52.655" viewBox="0 0 43 52.655">
<defs>
<clipPath id="clip-path">
<rect id="Rectangle_268" data-name="Rectangle 268" width="38" height="24" transform="translate(-0.407 0.083)" fill="#fff" stroke="#707070" stroke-width="1"/>
</clipPath>
</defs>
<g id="Icon_Donate" transform="translate(-22 -189.345)">
<rect id="Rectangle_267" data-name="Rectangle 267" width="43" height="10" rx="5" transform="translate(22 232)" fill="#9eb3c3"/>
<path id="Path_459" data-name="Path 459" d="M9.674,17.083,8.562,16.07C4.609,12.486,2,10.122,2,7.221A4.18,4.18,0,0,1,6.221,3,4.6,4.6,0,0,1,9.674,4.6,4.6,4.6,0,0,1,13.128,3a4.18,4.18,0,0,1,4.221,4.221c0,2.9-2.609,5.265-6.562,8.856Z" transform="translate(22.407 199.828)" fill="#ff0045" opacity="0.3"/>
<path id="Path_460" data-name="Path 460" d="M6.953,12.088l-.718-.654C3.684,9.122,2,7.6,2,5.724A2.7,2.7,0,0,1,4.724,3,2.966,2.966,0,0,1,6.953,4.035,2.966,2.966,0,0,1,9.182,3a2.7,2.7,0,0,1,2.724,2.724c0,1.872-1.684,3.4-4.235,5.716Z" transform="translate(45.571 186.345)" fill="#ff0045" opacity="0.16"/>
<g id="Mask_Group_38" data-name="Mask Group 38" transform="translate(24.407 213.918)" clip-path="url(#clip-path)">
<path id="Path_270" data-name="Path 270" d="M12.649,22.542l-1.544-1.406C5.621,16.163,2,12.883,2,8.857A5.8,5.8,0,0,1,7.857,3a6.377,6.377,0,0,1,4.792,2.226A6.377,6.377,0,0,1,17.442,3,5.8,5.8,0,0,1,23.3,8.857c0,4.025-3.621,7.306-9.105,12.289Z" transform="translate(5.992 5.54)" fill="#ff0045"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -62,8 +62,8 @@ extern const QString defaultDownloadPath;
X(FlipSelf, true) \
X(ShowMardownOption, false) \
X(ChatViewEnterIsNewLine, false) \
X(ShowSendOption, false)
X(ShowSendOption, false) \
X(DonateVisibleDate, "2999-02-01 05:00")
/*
* A class to expose settings keys in both c++ and QML.
* Note: this is using a non-constructable class instead of a

View file

@ -20,6 +20,7 @@
pragma Singleton
import QtQuick
import net.jami.Adapters 1.1
import net.jami.Enums 1.1
Item {
property string qmlFilePrefix: "file:/"
@ -69,4 +70,9 @@ Item {
function clamp(val, min, max) {
return Math.min(Math.max(val, min), max);
}
function isDonationBannerVisible() {
// The banner is visible if the current date is after the date set in the settings
return new Date() > new Date(Date.parse(UtilsAdapter.getAppValue(Settings.Key.DonateVisibleDate)));
}
}

View file

@ -835,4 +835,9 @@ Item {
// Appearence
property string theme: qsTr("Theme")
property string zoomLevel: qsTr("Text zoom level")
//Donation campaign
property string donation: qsTr("Donate")
property string donationText: qsTr("If you enjoy using Jami and believe in our mission, would you make a donation?")
property string notNow: qsTr("Not now")
}

View file

@ -657,6 +657,11 @@ Item {
property color darkThemeCheckedColor: "#03B9E9"
property color darkThemeBorderColor: "#03B9E9"
// Donation campaign
property color donationButtonTextColor: "#005699"
property color donationBackgroundColor: "#D5E4EF"
property string donationUrl: "https://jami.net/donate/"
function setTheme(dark) {
darkTheme = dark;
}

View file

@ -0,0 +1,150 @@
/*
* Copyright (C) 2023 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
import net.jami.Enums 1.1
import net.jami.Models 1.1
import "../../commoncomponents"
import "../../settingsview/components"
Rectangle {
id: donation
property bool donationVisible: JamiQmlUtils.isDonationBannerVisible()
width: parent.width - 30
height: donationTextRect.height + 45 > donationIcon.height + 20 ? donationTextRect.height + 45 : donationIcon.height + 20
radius: 5
color: JamiTheme.donationBackgroundColor
GridLayout {
id: donationLayout
anchors.fill: parent
columns: 3
rows: 2
rowSpacing: 0
columnSpacing: 10
Rectangle {
id: donationIcon
Layout.row: 0
Layout.column: 0
Layout.rowSpan: 2
Layout.preferredHeight: 70
Layout.preferredWidth: 45
Layout.leftMargin: 10
Layout.topMargin: 10
Layout.bottomMargin: 15
color: JamiTheme.transparentColor
Image {
id: donationImage
height: parent.height
width: 50
anchors.centerIn: parent
source: JamiResources.icon_donate_svg
}
}
Rectangle {
id: donationTextRect
Layout.topMargin: 10
Layout.row: 0
Layout.column: 1
Layout.columnSpan: 2
Layout.preferredHeight: donationText.height
Layout.preferredWidth: parent.width - 74
Layout.bottomMargin: 5
color: JamiTheme.transparentColor
Text {
id: donationText
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
width: parent.width
height: contentHeight
text: JamiStrings.donationText
wrapMode: Text.WordWrap
font.pointSize: JamiTheme.textFontSize
}
}
Rectangle {
id: notNowRect
Layout.row: 1
Layout.column: 1
Layout.preferredHeight: 30
Layout.preferredWidth: (parent.width - 55) / 2
color: JamiTheme.transparentColor
Text {
id: notNowText
MouseArea {
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
onClicked: {
// When the user clicks on "Not now", we set the donation date to 7 days from now (1 for the test)
// TODO reset to 7 days
UtilsAdapter.setAppValue(Settings.Key.DonateVisibleDate, new Date(new Date().getTime() + 1 * 24 * 60 * 60 * 1000).toISOString().slice(0, 16).replace("T", " "));
donation.donationVisible = Qt.binding(() => JamiQmlUtils.isDonationBannerVisible());
}
}
text: JamiStrings.notNow
color: JamiTheme.donationButtonTextColor
anchors.top: parent.top
anchors.left: parent.left
font.pointSize: JamiTheme.textFontSize
}
}
Rectangle {
id: donateRect
Layout.row: 1
Layout.column: 2
Layout.preferredHeight: 30
Layout.preferredWidth: (parent.width - 50) / 2
color: JamiTheme.transparentColor
Text {
id: donateText
MouseArea {
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
onClicked: {
Qt.openUrlExternally(JamiTheme.donationUrl);
}
}
text: JamiStrings.donation
font.pointSize: JamiTheme.textFontSize
color: JamiTheme.donationButtonTextColor
anchors.top: parent.top
anchors.left: parent.left
}
}
}
}

View file

@ -16,21 +16,19 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
import net.jami.Enums 1.1
import net.jami.Models 1.1
import "../../commoncomponents"
import "../../settingsview/components"
SidePanelBase {
id: root
objectName: "SidePanel"
color: JamiTheme.backgroundColor
@ -39,7 +37,7 @@ SidePanelBase {
target: LRCInstance
function onCurrentAccountIdChanged() {
clearContactSearchBar()
clearContactSearchBar();
}
}
@ -47,8 +45,8 @@ SidePanelBase {
target: ConversationsAdapter
function onConversationReady() {
selectTab(SidePanelTabBar.Conversations)
clearContactSearchBar()
selectTab(SidePanelTabBar.Conversations);
clearContactSearchBar();
}
}
@ -56,46 +54,46 @@ SidePanelBase {
target: ConversationsAdapter
function onShowSearchStatus(status) {
searchStatusText.text = status
searchStatusText.text = status;
}
function onTextFilterChanged(text) {
// In the swarm details, "Go to conversation" can
// change the search bar. Be sure to be synced
contactSearchBar.textContent = text
contactSearchBar.textContent = text;
}
}
function toggleCreateSwarmView() {
if (!inNewSwarm) {
viewCoordinator.present("NewSwarmPage")
const newSwarmPage = viewCoordinator.getView("NewSwarmPage")
viewCoordinator.present("NewSwarmPage");
const newSwarmPage = viewCoordinator.getView("NewSwarmPage");
newSwarmPage.removeMember.connect((convId, member) => {
removeMember(convId, member)
})
removeMember(convId, member);
});
newSwarmPage.createSwarmClicked.connect((title, description, avatar) => {
var uris = []
for (var idx in newSwarmPage.members) {
var uri = newSwarmPage.members[idx].uri
if (uris.indexOf(uri) === -1) {
uris.push(uri)
var uris = [];
for (var idx in newSwarmPage.members) {
var uri = newSwarmPage.members[idx].uri;
if (uris.indexOf(uri) === -1) {
uris.push(uri);
}
}
}
let convuid = ConversationsAdapter.createSwarm(title, description, avatar, uris)
viewCoordinator.dismiss("NewSwarmPage")
LRCInstance.selectConversation(convuid)
})
let convuid = ConversationsAdapter.createSwarm(title, description, avatar, uris);
viewCoordinator.dismiss("NewSwarmPage");
LRCInstance.selectConversation(convuid);
});
} else {
viewCoordinator.dismiss("NewSwarmPage")
viewCoordinator.dismiss("NewSwarmPage");
}
}
function clearContactSearchBar() {
contactSearchBar.clearText()
contactSearchBar.clearText();
}
function selectTab(tabIndex) {
sidePanelTabBar.selectTab(tabIndex)
sidePanelTabBar.selectTab(tabIndex);
}
property bool inNewSwarm: viewCoordinator.currentViewName === "NewSwarmPage"
@ -104,65 +102,64 @@ SidePanelBase {
property var highlightedMembers: []
onHighlightedMembersChanged: {
if (inNewSwarm) {
const newSwarmPage = viewCoordinator.getView("NewSwarmPage")
newSwarmPage.members = highlightedMembers
const newSwarmPage = viewCoordinator.getView("NewSwarmPage");
newSwarmPage.members = highlightedMembers;
}
}
function refreshHighlighted(convId, highlightedStatus) {
var newH = root.highlighted
var newHm = root.highlightedMembers
var newH = root.highlighted;
var newHm = root.highlightedMembers;
if (highlightedStatus) {
var item = ConversationsAdapter.getConvInfoMap(convId)
var added = false
var item = ConversationsAdapter.getConvInfoMap(convId);
var added = false;
for (var idx in item.uris) {
var uri = item.uris[idx]
if (!Array.from(newHm).find(r => r.uri === uri) &&
uri !== CurrentAccount.uri) {
newHm.push({"uri": uri, "convId": convId})
added = true
var uri = item.uris[idx];
if (!Array.from(newHm).find(r => r.uri === uri) && uri !== CurrentAccount.uri) {
newHm.push({
"uri": uri,
"convId": convId
});
added = true;
}
}
if (!added)
return false
return false;
} else {
newH = Array.from(newH).filter(r => r !== convId)
newHm = Array.from(newHm).filter(r => r.convId !== convId)
newH = Array.from(newH).filter(r => r !== convId);
newHm = Array.from(newHm).filter(r => r.convId !== convId);
}
newH.push(convId)
root.highlighted = newH
root.highlightedMembers = newHm
ConversationsAdapter.ignoreFiltering(root.highlighted)
return true
newH.push(convId);
root.highlighted = newH;
root.highlightedMembers = newHm;
ConversationsAdapter.ignoreFiltering(root.highlighted);
return true;
}
function clearHighlighted() {
root.highlighted = []
root.highlightedMembers = []
root.highlighted = [];
root.highlightedMembers = [];
}
function removeMember(convId, member) {
var refreshHighlighted = true
var newHm = []
var refreshHighlighted = true;
var newHm = [];
for (var hm in root.highlightedMembers) {
var m = root.highlightedMembers[hm]
var m = root.highlightedMembers[hm];
if (m.convId === convId && m.uri === member) {
continue;
} else if (m.convId === convId) {
refreshHighlighted = false
refreshHighlighted = false;
}
newHm.push(m)
newHm.push(m);
}
root.highlightedMembers = newHm
root.highlightedMembers = newHm;
if (refreshHighlighted) {
// Remove highlighted status if necessary
for (var d in swarmCurrentConversationList.contentItem.children) {
var delegate = swarmCurrentConversationList.contentItem.children[d]
var delegate = swarmCurrentConversationList.contentItem.children[d];
if (delegate.convId === convId)
delegate.highlighted = false
delegate.highlighted = false;
}
}
}
@ -176,11 +173,16 @@ SidePanelBase {
color: JamiTheme.backgroundColor
}
header: AccountComboBox {}
header: AccountComboBox {
}
Item {
anchors.fill: parent
onVisibleChanged: {
donation.donationVisible = Qt.binding(() => JamiQmlUtils.isDonationBannerVisible());
}
RowLayout {
id: titleBar
@ -240,7 +242,7 @@ SidePanelBase {
sequence: "Ctrl+F"
context: Qt.ApplicationShortcut
onActivated: {
contactSearchBar.forceActiveFocus()
contactSearchBar.forceActiveFocus();
}
}
@ -250,20 +252,18 @@ SidePanelBase {
Layout.fillHeight: true
Layout.fillWidth: true
onSearchBarTextChanged: function(text){
onSearchBarTextChanged: function (text) {
// not calling positionViewAtBeginning will cause
// sort animation visual bugs
conversationListView.positionViewAtBeginning()
ConversationsAdapter.ignoreFiltering(root.highlighted)
ConversationsAdapter.setFilter(text)
conversationListView.positionViewAtBeginning();
ConversationsAdapter.ignoreFiltering(root.highlighted);
ConversationsAdapter.setFilter(text);
}
onReturnPressedWhileSearching: {
var listView = searchResultsListView.count ?
searchResultsListView :
conversationListView
var listView = searchResultsListView.count ? searchResultsListView : conversationListView;
if (listView.count)
listView.model.select(0)
listView.model.select(0);
}
}
@ -291,8 +291,7 @@ SidePanelBase {
SidePanelTabBar {
id: sidePanelTabBar
visible: ConversationsAdapter.pendingRequestCount &&
!contactSearchBar.textContent && smartListLayout.visible
visible: ConversationsAdapter.pendingRequestCount && !contactSearchBar.textContent && smartListLayout.visible
anchors.top: startBar.bottom
anchors.topMargin: visible ? 10 : 0
width: page.width
@ -311,7 +310,6 @@ SidePanelBase {
height: visible ? 42 : 0
color: JamiTheme.backgroundColor
Text {
id: searchStatusText
@ -326,13 +324,22 @@ SidePanelBase {
}
}
DonationBanner {
id: donation
anchors.horizontalCenter: parent.horizontalCenter
anchors.leftMargin: 15
anchors.rightMargin: 15
anchors.top: sidePanelTabBar.bottom
anchors.topMargin: 10
visible: donation.donationVisible
}
ColumnLayout {
id: smartListLayout
width: parent.width
anchors.top: searchStatusRect.bottom
anchors.topMargin: (sidePanelTabBar.visible ||
searchStatusRect.visible) ? 0 : 12
anchors.top: donation.donationVisible ? donation.bottom : sidePanelTabBar.bottom
anchors.topMargin: (sidePanelTabBar.visible || searchStatusRect.visible) ? 0 : 12
anchors.bottom: parent.bottom
spacing: 4
@ -350,14 +357,14 @@ SidePanelBase {
Layout.fillWidth: true
Layout.preferredHeight: visible ? contentHeight : 0
Layout.maximumHeight: {
var otherContentHeight = conversationListView.contentHeight + 16
var otherContentHeight = conversationListView.contentHeight + 16;
if (conversationListView.visible)
if (otherContentHeight < parent.height / 2)
return parent.height - otherContentHeight
return parent.height - otherContentHeight;
else
return parent.height / 2
return parent.height / 2;
else
return parent.height
return parent.height;
}
model: SearchResultsListModel
@ -385,9 +392,8 @@ SidePanelBase {
visible: inNewSwarm
width: parent.width
anchors.top: searchStatusRect.bottom
anchors.topMargin: (sidePanelTabBar.visible ||
searchStatusRect.visible) ? 0 : 12
anchors.top: donation.donationVisible ? donation.bottom : sidePanelTabBar.bottom
anchors.topMargin: (sidePanelTabBar.visible || searchStatusRect.visible) ? 0 : 12
anchors.bottom: parent.bottom
spacing: 4
@ -404,8 +410,8 @@ SidePanelBase {
onVisibleChanged: {
if (!swarmCurrentConversationList.visible) {
highlighted = false
root.clearHighlighted()
highlighted = false;
root.clearHighlighted();
}
}
@ -414,26 +420,26 @@ SidePanelBase {
// destroyed from the memory. So, re-add the highlighted
// status if necessary
if (Array.from(root.highlighted).find(r => r === UID)) {
highlighted = true
highlighted = true;
}
}
onHighlightedChanged: function onHighlightedChanged() {
if (highlighted && Array.from(root.highlighted).find(r => r === UID)) {
// Due to scrolling destruction/reconstruction
return
return;
}
var currentHighlighted = root.highlighted
var currentHighlighted = root.highlighted;
if (!root.refreshHighlighted(UID, highlighted)) {
highlighted = false
return
highlighted = false;
return;
}
if (highlighted) {
root.highlighted.push(UID)
root.highlighted.push(UID);
} else {
root.highlighted = Array.from(root.highlighted).filter(r => r !== UID)
root.highlighted = Array.from(root.highlighted).filter(r => r !== UID);
}
root.clearContactSearchBar()
root.clearContactSearchBar();
}
}
currentIndex: model.currentFilteredRow
@ -448,7 +454,9 @@ SidePanelBase {
interval: 750
running: isSharingPosition || isReceivingPosition
repeat: true
onTriggered: {showIconArrow = !showIconArrow}
onTriggered: {
showIconArrow = !showIconArrow;
}
}
}
}