diff --git a/resources/icons/Icon_Donate.svg b/resources/icons/Icon_Donate.svg
new file mode 100644
index 0000000000000000000000000000000000000000..913daefdb94e8318a669aca684d2ff4df54617cc
--- /dev/null
+++ b/resources/icons/Icon_Donate.svg
@@ -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>
diff --git a/src/app/appsettingsmanager.h b/src/app/appsettingsmanager.h
index 8b29766ecedbe5d2da4f495576d48cce5bad5b40..b28deddc04efe09df90e1da31a184cc66a8c75e9 100644
--- a/src/app/appsettingsmanager.h
+++ b/src/app/appsettingsmanager.h
@@ -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
diff --git a/src/app/constant/JamiQmlUtils.qml b/src/app/constant/JamiQmlUtils.qml
index 4eaa02f8775f70540c12bdbd97e935cf74766ec6..4b5aa62bd9571308f8f8d44596ea1993fe93b758 100644
--- a/src/app/constant/JamiQmlUtils.qml
+++ b/src/app/constant/JamiQmlUtils.qml
@@ -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)));
+    }
 }
diff --git a/src/app/constant/JamiStrings.qml b/src/app/constant/JamiStrings.qml
index 98f339d55a8372b7d2952518cdba3d55e7a61a6f..817ce6425f218fc2921ced6edcfc27565c8c0e94 100644
--- a/src/app/constant/JamiStrings.qml
+++ b/src/app/constant/JamiStrings.qml
@@ -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")
 }
diff --git a/src/app/constant/JamiTheme.qml b/src/app/constant/JamiTheme.qml
index 7a42dd6f59051bfaa0c0fd8f9b2db2e3d0c4f56f..b66d734549357f2f611100cc2da7c052a83ce702 100644
--- a/src/app/constant/JamiTheme.qml
+++ b/src/app/constant/JamiTheme.qml
@@ -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;
     }
diff --git a/src/app/mainview/components/DonationBanner.qml b/src/app/mainview/components/DonationBanner.qml
new file mode 100644
index 0000000000000000000000000000000000000000..b7538878f917900f9e928809ea5ed8efbd9150ad
--- /dev/null
+++ b/src/app/mainview/components/DonationBanner.qml
@@ -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
+            }
+        }
+    }
+}
diff --git a/src/app/mainview/components/SidePanel.qml b/src/app/mainview/components/SidePanel.qml
index 170ade693d2e7e3167a0307375a5149131f4e44a..5ed1b5921122623c0076b1ec0b56210b48566798 100644
--- a/src/app/mainview/components/SidePanel.qml
+++ b/src/app/mainview/components/SidePanel.qml
@@ -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;
+                        }
                     }
                 }
             }