From d06902e3b7b6ba9f7eec2931c4dce879d4babdd5 Mon Sep 17 00:00:00 2001
From: Nicolas Vengeon <nicolas.vengeon@savoirfairelinux.com>
Date: Mon, 5 Dec 2022 15:44:04 -0500
Subject: [PATCH] Feature: unpin location sharing map

- Refactoring
- Unpined map
- handle multiple maps (one map per account)

Change-Id: I2b0abf284ccfe27b986f03915c5942d721403211
Gitlab: #901
---
 resources/icons/unpin.svg                     |   1 +
 src/app/appsettingsmanager.h                  |   4 +-
 src/app/constant/JamiStrings.qml              |  12 +-
 src/app/mainview/components/ChatView.qml      |  30 +-
 .../mainview/components/ChatViewFooter.qml    |   2 +-
 src/app/mainview/components/MessageBar.qml    |  44 +-
 .../components/SmartListItemDelegate.qml      |   8 +-
 src/app/positioning.cpp                       |  19 +-
 src/app/positioning.h                         |  11 +-
 src/app/positionmanager.cpp                   | 541 ++++++++++++------
 src/app/positionmanager.h                     |  66 ++-
 src/app/positionobject.cpp                    |   9 +-
 src/app/positionobject.h                      |   2 +
 src/app/qmlregister.cpp                       |   2 +-
 .../components/GeneralSettingsPage.qml        |  10 +
 .../components/LocationSharingSettings.qml    | 150 +++++
 src/app/webengine/map/MapPosition.qml         | 478 +++++-----------
 src/app/webengine/map/MapPositionOverlay.qml  | 125 ++++
 .../map/MapPositionSharingControl.qml         | 203 +++++++
 .../map/StopSharingPositionPopup.qml          |  10 +-
 src/app/webengine/map/map.js                  |  30 +-
 src/libclient/typedefs.h                      |   3 +
 22 files changed, 1161 insertions(+), 599 deletions(-)
 create mode 100644 resources/icons/unpin.svg
 create mode 100644 src/app/settingsview/components/LocationSharingSettings.qml
 create mode 100644 src/app/webengine/map/MapPositionOverlay.qml
 create mode 100644 src/app/webengine/map/MapPositionSharingControl.qml

diff --git a/resources/icons/unpin.svg b/resources/icons/unpin.svg
new file mode 100644
index 000000000..9f8128218
--- /dev/null
+++ b/resources/icons/unpin.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M9 42q-1.2 0-2.1-.9Q6 40.2 6 39V9q0-1.2.9-2.1Q7.8 6 9 6h13.95v3H9v30h30V25.05h3V39q0 1.2-.9 2.1-.9.9-2.1.9Zm10.1-10.95L17 28.9 36.9 9H25.95V6H42v16.05h-3v-10.9Z"/></svg>
\ No newline at end of file
diff --git a/src/app/appsettingsmanager.h b/src/app/appsettingsmanager.h
index d69a3acee..30a0573ac 100644
--- a/src/app/appsettingsmanager.h
+++ b/src/app/appsettingsmanager.h
@@ -54,7 +54,9 @@ extern const QString defaultDownloadPath;
     X(WindowGeometry, QRectF(qQNaN(), qQNaN(), 0., 0.)) \
     X(WindowState, QWindow::AutomaticVisibility) \
     X(EnableExperimentalSwarm, false) \
-    X(LANG, "SYSTEM")
+    X(LANG, "SYSTEM") \
+    X(PositionShareDuration, 15) \
+    X(PositionShareLimit, true)
 
 /*
  * A class to expose settings keys in both c++ and QML.
diff --git a/src/app/constant/JamiStrings.qml b/src/app/constant/JamiStrings.qml
index c8fe3633f..510237180 100644
--- a/src/app/constant/JamiStrings.qml
+++ b/src/app/constant/JamiStrings.qml
@@ -309,8 +309,12 @@ Item {
     property string locationServicesError: qsTr("Your precise location could not be determined.\nIn Device Settings, please turn on \"Location Services\".\nOther participants' location can still be received.")
     property string locationServicesClosedError: qsTr("Your precise location could not be determined. Please check your Internet connection.")
     property string stopAllSharings: qsTr("Turn off location sharing");
-    property string stopConvSharing: qsTr("Stop location sharing in this conversation");
+    property string shortStopAllSharings: qsTr("Turn off sharing");
+    property string stopConvSharing: qsTr("Stop location sharing in this conversation (%1)");
     property string stopSharingPopupBody: qsTr("Location is shared in several conversations");
+    property string unpinStopSharingTooltip: qsTr("Pin map to be able to share location or to turn off location in specific conversations");
+    property string stopSharingSeveralConversationTooltip: qsTr("Location is shared in several conversations, click to choose how to turn off location sharing")
+    property string shareLocationToolTip: qsTr("Share location to participants of this conversation (%1)");
     property string minimizeMapTooltip: qsTr("Minimize");
     property string maximizeMapTooltip: qsTr("Maximize");
     property string reduceMapTooltip: qsTr("Reduce");
@@ -318,6 +322,11 @@ Item {
     property string dragMapTooltip: qsTr("Drag");
     property string centerMapTooltip: qsTr("Center");
     property string closeMapTooltip: qsTr("Close");
+    property string unpin: qsTr("Unpin");
+    property string pinWindow: qsTr("Pin");
+    property string positionShareDuration: qsTr("Position share duration");
+    property string positionShareLimit: qsTr("Limit the duration of location sharing");
+    property string locationSharingLabel: qsTr("Location sharing");
 
     // Chatview header
     property string hideChat: qsTr("Hide chat")
@@ -694,6 +703,7 @@ Item {
     // SmartList
     property string clearText: qsTr("Clear Text")
     property string conversations: qsTr("Conversations")
+    property string conversation: qsTr("Conversation")
     property string searchResults: qsTr("Search Results")
 
     // SmartList context menu
diff --git a/src/app/mainview/components/ChatView.qml b/src/app/mainview/components/ChatView.qml
index a45d43cf6..1c19ac459 100644
--- a/src/app/mainview/components/ChatView.qml
+++ b/src/app/mainview/components/ChatView.qml
@@ -34,7 +34,7 @@ Rectangle {
     id: root
 
     property bool allMessagesLoaded
-
+    property var mapPositions: PositionManager.mapStatus
     signal needToHideConversationInCall
     signal messagesCleared
     signal messagesLoaded
@@ -51,19 +51,29 @@ Rectangle {
         addMemberPanel.visible = false
     }
 
-    color: JamiTheme.chatviewBgColor
-
-    property string currentConvId: CurrentConversation.id
-    onCurrentConvIdChanged: PositionManager.setMapActive(false);
+    function instanceMapObject() {
+        if (WITH_WEBENGINE) {
+            var component = Qt.createComponent("qrc:/webengine/map/MapPosition.qml");
+            var sprite = component.createObject(root, {maxWidth: root.width, maxHeight: root.height});
 
-    Loader {
-        id: mapLoader
+            if (sprite === null) {
+                // Error Handling
+                console.log("Error creating object");
+            }
+        }
+    }
+    Connections {
+        target: PositionManager
 
-        active: PositionManager.isMapActive
-        z: 10
-        source: WITH_WEBENGINE ? "qrc:/webengine/map/MapPosition.qml" : ""
+        function onOpenNewMap() {
+            instanceMapObject()
+        }
     }
 
+    color: JamiTheme.chatviewBgColor
+
+    property string currentConvId: CurrentConversation.id
+
     HostPopup {
         id: hostPopup
     }
diff --git a/src/app/mainview/components/ChatViewFooter.qml b/src/app/mainview/components/ChatViewFooter.qml
index a2f161971..2dca139e0 100644
--- a/src/app/mainview/components/ChatViewFooter.qml
+++ b/src/app/mainview/components/ChatViewFooter.qml
@@ -190,7 +190,7 @@ Rectangle {
             }
 
             onShowMapClicked: {
-                PositionManager.setMapActive(true);
+                PositionManager.setMapActive(CurrentAccount.id)
             }
 
             onSendFileButtonClicked: jamiFileDialog.open()
diff --git a/src/app/mainview/components/MessageBar.qml b/src/app/mainview/components/MessageBar.qml
index 714f6fd94..30c14c817 100644
--- a/src/app/mainview/components/MessageBar.qml
+++ b/src/app/mainview/components/MessageBar.qml
@@ -65,12 +65,33 @@ ColumnLayout {
         spacing: JamiTheme.chatViewFooterRowSpacing
 
         PushButton {
-            id: sendFileButton
+            id: showMapButton
 
             Layout.alignment: Qt.AlignVCenter
             Layout.leftMargin: marginSize
             Layout.preferredWidth: JamiTheme.chatViewFooterButtonSize
             Layout.preferredHeight: JamiTheme.chatViewFooterButtonSize
+            visible: WITH_WEBENGINE && !CurrentConversation.isSip
+
+            radius: JamiTheme.chatViewFooterButtonRadius
+            preferredSize: JamiTheme.chatViewFooterButtonIconSize
+
+            toolTipText: JamiStrings.shareLocation
+
+            source: JamiResources.share_location_svg
+
+            normalColor: JamiTheme.primaryBackgroundColor
+            imageColor: JamiTheme.messageWebViewFooterButtonImageColor
+
+            onClicked: root.showMapClicked()
+        }
+
+        PushButton {
+            id: sendFileButton
+
+            Layout.alignment: Qt.AlignVCenter
+            Layout.preferredWidth: JamiTheme.chatViewFooterButtonSize
+            Layout.preferredHeight: JamiTheme.chatViewFooterButtonSize
 
             radius: JamiTheme.chatViewFooterButtonRadius
             preferredSize: JamiTheme.chatViewFooterButtonIconSize - 6
@@ -130,27 +151,6 @@ ColumnLayout {
             Component.onCompleted: JamiQmlUtils.videoRecordMessageButtonObj = videoRecordMessageButton
         }
 
-        PushButton {
-            id: showMapButton
-
-            Layout.alignment: Qt.AlignVCenter
-            Layout.preferredWidth: JamiTheme.chatViewFooterButtonSize
-            Layout.preferredHeight: JamiTheme.chatViewFooterButtonSize
-            visible: WITH_WEBENGINE && !CurrentConversation.isSip
-
-            radius: JamiTheme.chatViewFooterButtonRadius
-            preferredSize: JamiTheme.chatViewFooterButtonIconSize
-
-            toolTipText: JamiStrings.shareLocation
-
-            source: JamiResources.share_location_svg
-
-            normalColor: JamiTheme.primaryBackgroundColor
-            imageColor: JamiTheme.messageWebViewFooterButtonImageColor
-
-            onClicked: root.showMapClicked()
-        }
-
         MessageBarTextArea {
             id: textArea
 
diff --git a/src/app/mainview/components/SmartListItemDelegate.qml b/src/app/mainview/components/SmartListItemDelegate.qml
index 11f173b2f..4888a8291 100644
--- a/src/app/mainview/components/SmartListItemDelegate.qml
+++ b/src/app/mainview/components/SmartListItemDelegate.qml
@@ -68,8 +68,8 @@ ItemDelegate {
 
             imageId: UID
             showPresenceIndicator: Presence !== undefined ? Presence : false
-            showSharePositionIndicator: PositionManager.isPositionSharedToConv(UID)
-            showSharedPositionIndicator: PositionManager.isConvSharingPosition(UID)
+            showSharePositionIndicator: PositionManager.isPositionSharedToConv(accountId, UID)
+            showSharedPositionIndicator: PositionManager.isConvSharingPosition(accountId, UID)
 
             Layout.preferredWidth: JamiTheme.smartListAvatarSize
             Layout.preferredHeight: JamiTheme.smartListAvatarSize
@@ -77,10 +77,10 @@ ItemDelegate {
             Connections {
                 target: PositionManager
                 function onPositionShareConvIdsCountChanged () {
-                    avatar.showSharePositionIndicator = PositionManager.isPositionSharedToConv(UID)
+                    avatar.showSharePositionIndicator = PositionManager.isPositionSharedToConv(accountId, UID)
                 }
                 function onSharingUrisCountChanged () {
-                    avatar.showSharedPositionIndicator = PositionManager.isConvSharingPosition(UID)
+                    avatar.showSharedPositionIndicator = PositionManager.isConvSharingPosition(accountId, UID)
                 }
             }
 
diff --git a/src/app/positioning.cpp b/src/app/positioning.cpp
index d8a8bbf07..be0114152 100644
--- a/src/app/positioning.cpp
+++ b/src/app/positioning.cpp
@@ -21,14 +21,12 @@
 #include <QJsonObject>
 #include <QJsonDocument>
 
-Positioning::Positioning(QString uri, QObject* parent)
+Positioning::Positioning(QObject* parent)
     : QObject(parent)
-    , uri_(uri)
 {
     source_ = QGeoPositionInfoSource::createDefaultSource(this);
-    QTimer* timer = new QTimer(this);
-    connect(timer, &QTimer::timeout, this, &Positioning::requestPosition);
-    timer->start(5000);
+    timer_ = new QTimer(this);
+    connect(timer_, &QTimer::timeout, this, &Positioning::requestPosition);
     connect(source_, &QGeoPositionInfoSource::errorOccurred, this, &Positioning::slotError);
     connect(source_, &QGeoPositionInfoSource::positionUpdated, this, &Positioning::positionUpdated);
     // if location services are activated, positioning will be activated automatically
@@ -41,6 +39,8 @@ Positioning::Positioning(QString uri, QObject* parent)
 void
 Positioning::start()
 {
+    requestPosition();
+    timer_->start(10000);
     if (source_ && !isPositioning) {
         source_->startUpdates();
         isPositioning = true;
@@ -53,6 +53,7 @@ Positioning::stop()
     if (source_ && isPositioning)
         source_->stopUpdates();
     isPositioning = false;
+    timer_->stop();
 }
 
 QString
@@ -70,17 +71,11 @@ Positioning::convertToJson(const QGeoPositionInfo& info)
     return strJson;
 }
 
-void
-Positioning::setUri(QString uri)
-{
-    uri_ = uri;
-}
-
 void
 Positioning::positionUpdated(const QGeoPositionInfo& info)
 {
     Q_EMIT positioningError("");
-    Q_EMIT newPosition("", uri_, convertToJson(info), -1, "");
+    Q_EMIT newPosition(convertToJson(info));
 }
 
 void
diff --git a/src/app/positioning.h b/src/app/positioning.h
index 9b3423c97..e2bc11720 100644
--- a/src/app/positioning.h
+++ b/src/app/positioning.h
@@ -27,7 +27,7 @@ class Positioning : public QObject
     Q_OBJECT
 
 public:
-    Positioning(QString uri, QObject* parent = 0);
+    Positioning(QObject* parent = 0);
     /**
      * start to retreive the current position
      */
@@ -42,8 +42,6 @@ public:
      */
     QString convertToJson(const QGeoPositionInfo& info);
 
-    void setUri(QString uri);
-
 private Q_SLOTS:
     void slotError(QGeoPositionInfoSource::Error error);
     void positionUpdated(const QGeoPositionInfo& info);
@@ -57,15 +55,12 @@ private Q_SLOTS:
     void locationServicesActivated();
 
 Q_SIGNALS:
-    void newPosition(const QString& unused_AccountId,
-                     const QString& peerId,
-                     const QString& body,
-                     const uint64_t& timestamp,
-                     const QString& daemonId);
+    void newPosition(const QString& body);
     void positioningError(const QString error);
 
 private:
     QString uri_;
     QGeoPositionInfoSource* source_ = nullptr;
     bool isPositioning = false;
+    QTimer* timer_;
 };
diff --git a/src/app/positionmanager.cpp b/src/app/positionmanager.cpp
index 513393f06..c4c230079 100644
--- a/src/app/positionmanager.cpp
+++ b/src/app/positionmanager.cpp
@@ -1,7 +1,8 @@
 #include "positionmanager.h"
 
-#include "qtutils.h"
+#include "appsettingsmanager.h"
 
+#include "qtutils.h"
 #include <QApplication>
 #include <QBuffer>
 #include <QList>
@@ -9,31 +10,40 @@
 #include <QJsonDocument>
 #include <QImageReader>
 
-PositionManager::PositionManager(SystemTray* systemTray, LRCInstance* instance, QObject* parent)
+PositionManager::PositionManager(AppSettingsManager* settingsManager,
+                                 SystemTray* systemTray,
+                                 LRCInstance* instance,
+                                 QObject* parent)
     : QmlAdapterBase(instance, parent)
     , systemTray_(systemTray)
+    , settingsManager_(settingsManager)
 {
-    timerTimeLeftSharing_ = new QTimer(this);
-    timerStopSharing_ = new QTimer(this);
-    connect(timerTimeLeftSharing_, &QTimer::timeout, [=] {
-        set_timeSharingRemaining(timerStopSharing_->remainingTime());
-    });
-    connect(timerStopSharing_, &QTimer::timeout, [=] { stopSharingPosition(); });
-    connect(lrcInstance_, &LRCInstance::selectedConvUidChanged, [this]() {
-        set_mapAutoOpening(true);
-    });
-    connect(lrcInstance_, &LRCInstance::currentAccountIdChanged, [this]() {
-        if (!localPositioning_) // Not yet initialized
-            return;
-        localPositioning_->setUri(lrcInstance_->getCurrentAccountInfo().profileInfo.uri);
-    });
-    set_isMapActive(false);
+    countdownTimer_ = new QTimer(this);
+    connect(countdownTimer_, &QTimer::timeout, this, &PositionManager::countdownUpdate);
+    connect(lrcInstance_,
+            &LRCInstance::selectedConvUidChanged,
+            this,
+            &PositionManager::onNewConversation,
+            Qt::UniqueConnection);
+    connect(lrcInstance_,
+            &LRCInstance::currentAccountIdChanged,
+            this,
+            &PositionManager::onNewAccount,
+            Qt::UniqueConnection);
+    connect(
+        this,
+        &PositionManager::localPositionReceived,
+        this,
+        [this](const QString& accountId, const QString& peerId, const QString& body) {
+            onPositionReceived(accountId, peerId, body, -1, "");
+        },
+        Qt::QueuedConnection);
 }
 
 void
 PositionManager::safeInit()
 {
-    localPositioning_.reset(new Positioning(lrcInstance_->getCurrentAccountInfo().profileInfo.uri));
+    localPositioning_.reset(new Positioning());
     connectAccountModel();
 }
 
@@ -50,41 +60,36 @@ PositionManager::connectAccountModel()
 void
 PositionManager::startPositioning()
 {
-    currentConvSharingUris_.clear();
-    localPositioning_->start();
-    connect(localPositioning_.get(),
-            &Positioning::newPosition,
-            this,
-            &PositionManager::onPositionReceived,
-            Qt::UniqueConnection);
+    if (localPositioning_)
+        localPositioning_->start();
+
     connect(localPositioning_.get(),
             &Positioning::positioningError,
             this,
             &PositionManager::onPositionErrorReceived,
             Qt::UniqueConnection);
+    connect(
+        localPositioning_.get(),
+        &Positioning::newPosition,
+        this,
+        [this](const QString& body) { sendPosition(body, true); },
+        Qt::UniqueConnection);
 }
-
 void
 PositionManager::stopPositioning()
 {
-    localPositioning_->stop();
-}
-
-QString
-PositionManager::getSelectedConvId()
-{
-    return lrcInstance_->get_selectedConvUid();
+    if (localPositioning_)
+        localPositioning_->stop();
 }
 
 bool
-PositionManager::isConvSharingPosition(const QString& convUri)
+PositionManager::isConvSharingPosition(const QString& accountId, const QString& convUri)
 {
     const auto& convParticipants = lrcInstance_->getConversationFromConvUid(convUri)
                                        .participantsUris();
     Q_FOREACH (const auto& id, convParticipants) {
         if (id != lrcInstance_->getCurrentAccountInfo().profileInfo.uri) {
-            if (objectListSharingUris_.contains(
-                    QPair<QString, QString> {lrcInstance_->get_currentAccountId(), id})) {
+            if (objectListSharingUris_.contains(PositionKey {accountId, id})) {
                 return true;
             }
         }
@@ -93,36 +98,53 @@ PositionManager::isConvSharingPosition(const QString& convUri)
 }
 
 void
-PositionManager::loadPreviousLocations()
+PositionManager::loadPreviousLocations(QString& accountId)
 {
     QVariantMap shareInfo;
     for (auto it = objectListSharingUris_.begin(); it != objectListSharingUris_.end(); it++) {
-        QJsonObject jsonObj;
-        jsonObj.insert("type", QJsonValue("Position"));
-        jsonObj.insert("lat", it.value()->getLatitude().toString());
-        jsonObj.insert("long", it.value()->getLongitude().toString());
-        QJsonDocument doc(jsonObj);
-        QString strJson(doc.toJson(QJsonDocument::Compact));
-        onPositionReceived(it.key().first, it.key().second, strJson, -1, "");
+        if (it.key().first == accountId) {
+            QJsonObject jsonObj;
+            jsonObj.insert("type", QJsonValue("Position"));
+            jsonObj.insert("lat", it.value()->getLatitude().toString());
+            jsonObj.insert("long", it.value()->getLongitude().toString());
+            QJsonDocument doc(jsonObj);
+            QString strJson(doc.toJson(QJsonDocument::Compact));
+            // parse the position from json
+            QVariantMap positionReceived = parseJsonPosition(it.key().first,
+                                                             it.key().second,
+                                                             strJson);
+            addPositionToMap(it.key(), positionReceived);
+        }
     }
 }
 
+QString
+PositionManager::getmapTitle(QString& accountId, QString convId)
+{
+    if (!convId.isEmpty() && !accountId.isEmpty()) {
+        return lrcInstance_->getAccountInfo(accountId).conversationModel->title(convId);
+    }
+    if (!accountId.isEmpty())
+        return lrcInstance_->getAccountInfo(accountId).registeredName;
+    return {};
+}
+
 bool
-PositionManager::isPositionSharedToConv(const QString& convUid)
+PositionManager::isPositionSharedToConv(const QString& accountId, const QString& convUid)
 {
     if (positionShareConvIds_.length()) {
         auto iter = std::find(positionShareConvIds_.begin(),
                               positionShareConvIds_.end(),
-                              QPair<QString, QString> {lrcInstance_->get_currentAccountId(),
-                                                       convUid});
+                              PositionKey {accountId, convUid});
         return (iter != positionShareConvIds_.end());
     }
     return false;
 }
 
 void
-PositionManager::sendPosition(const QString& body)
+PositionManager::sendPosition(const QString& body, bool triggersLocalPosition)
 {
+    // send position to positionShareConvIds_ participants
     try {
         Q_FOREACH (const auto& key, positionShareConvIds_) {
             const auto& convInfo = lrcInstance_->getConversationFromConvUid(key.second, key.first);
@@ -137,6 +159,15 @@ PositionManager::sendPosition(const QString& body)
     } catch (const std::exception& e) {
         qDebug() << Q_FUNC_INFO << e.what();
     }
+    if (triggersLocalPosition) {
+        // send own position to every account with an opened map
+        QMutexLocker lk(&mapStatusMutex_);
+        for (auto it = mapStatus_.begin(); it != mapStatus_.end(); it++) {
+            Q_EMIT localPositionReceived(it.key(),
+                                         lrcInstance_->getAccountInfo(it.key()).profileInfo.uri,
+                                         body);
+        }
+    }
 }
 
 void
@@ -149,26 +180,17 @@ PositionManager::onWatchdogTimeout()
     if (it != objectListSharingUris_.cend()) {
         QString stopMsg("{\"type\":\"Stop\"}");
         onPositionReceived(it.key().first, it.key().second, stopMsg, -1, "");
+        makeVisibleSharingButton(it.key().first);
     }
 }
 
 void
-PositionManager::sharePosition(int maximumTime)
+PositionManager::sharePosition(int maximumTime, QString accountId, QString convId)
 {
-    connect(
-        localPositioning_.get(),
-        &Positioning::newPosition,
-        this,
-        [&](const QString&, const QString&, const QString& body, const uint64_t&, const QString&) {
-            sendPosition(body);
-        },
-        Qt::UniqueConnection);
-
     try {
-        startPositionTimers(maximumTime);
-        const auto convUid = lrcInstance_->get_selectedConvUid();
-        positionShareConvIds_.append(
-            QPair<QString, QString> {lrcInstance_->get_currentAccountId(), convUid});
+        if (settingsManager_->getValue(Settings::Key::PositionShareLimit) == true)
+            startPositionTimers(maximumTime);
+        positionShareConvIds_.append(PositionKey {accountId, convId});
         set_positionShareConvIdsCount(positionShareConvIds_.size());
     } catch (...) {
         qDebug() << "Exception during sharePosition:";
@@ -176,17 +198,46 @@ PositionManager::sharePosition(int maximumTime)
 }
 
 void
-PositionManager::stopSharingPosition(const QString convId)
+PositionManager::stopSharingPosition(QString accountId, const QString convId)
 {
     QString stopMsg;
     stopMsg = "{\"type\":\"Stop\"}";
-    if (convId == "") {
-        sendPosition(stopMsg);
+    if (accountId == "") {
+        sendPosition(stopMsg, false);
         stopPositionTimers();
         positionShareConvIds_.clear();
-        set_positionShareConvIdsCount(positionShareConvIds_.size());
     } else {
-        const auto& convInfo = lrcInstance_->getConversationFromConvUid(convId);
+        if (convId == "") {
+            stopPositionTimers(accountId);
+            for (auto it = positionShareConvIds_.begin(); it != positionShareConvIds_.end();) {
+                if (it->first == accountId) {
+                    sendStopMessage(accountId, it->second);
+                    it = positionShareConvIds_.erase(it);
+                } else
+                    ++it;
+            }
+        } else {
+            sendStopMessage(accountId, convId);
+            auto iter = std::find(positionShareConvIds_.begin(),
+                                  positionShareConvIds_.end(),
+                                  PositionKey {accountId, convId});
+            if (iter != positionShareConvIds_.end()) {
+                positionShareConvIds_.remove(std::distance(positionShareConvIds_.begin(), iter));
+            }
+        }
+    }
+    if (!positionShareConvIds_.size())
+        countdownTimer_->stop();
+    set_positionShareConvIdsCount(positionShareConvIds_.size());
+}
+
+void
+PositionManager::sendStopMessage(QString accountId, const QString convId)
+{
+    QString stopMsg;
+    stopMsg = "{\"type\":\"Stop\"}";
+    if (accountId != "" && convId != "") {
+        const auto& convInfo = lrcInstance_->getConversationFromConvUid(convId, accountId);
         Q_FOREACH (const QString& uri, convInfo.participantsUris()) {
             if (lrcInstance_->getCurrentAccountInfo().profileInfo.uri != uri) {
                 lrcInstance_->getCurrentAccountInfo().contactModel->sendDhtMessage(uri,
@@ -194,22 +245,68 @@ PositionManager::stopSharingPosition(const QString convId)
                                                                                    APPLICATION_GEO);
             }
         }
-        auto iter = std::find(positionShareConvIds_.begin(),
-                              positionShareConvIds_.end(),
-                              QPair<QString, QString> {lrcInstance_->get_currentAccountId(),
-                                                       convId});
-        if (iter != positionShareConvIds_.end()) {
-            positionShareConvIds_.remove(std::distance(positionShareConvIds_.begin(), iter));
+    }
+}
+
+void
+PositionManager::unPinMap(QString key)
+{
+    QMutexLocker lk(&mapStatusMutex_);
+    if (mapStatus_.find(key) != mapStatus_.end()) {
+        mapStatus_[key] = true;
+        Q_EMIT mapStatusChanged();
+        Q_EMIT unPinMapSignal(key);
+    } else {
+        qWarning() << "Error: Can't unpin a map that doesn't exist";
+    }
+}
+
+void
+PositionManager::pinMap(QString key)
+{
+    QMutexLocker lk(&mapStatusMutex_);
+    if (mapStatus_.find(key) != mapStatus_.end()) {
+        // map can be pined only if it's in the right account
+        if (key == lrcInstance_->get_currentAccountId()) {
+            mapStatus_[key] = false;
+            lk.unlock();
+            Q_EMIT mapStatusChanged();
+            Q_EMIT pinMapSignal(key);
+        } else {
+            lk.unlock();
+            setMapInactive(key);
         }
-        set_positionShareConvIdsCount(positionShareConvIds_.size());
     }
 }
 
 void
-PositionManager::setMapActive(bool state)
+PositionManager::setMapInactive(const QString key)
 {
-    set_isMapActive(state);
-    Q_EMIT isMapActiveChanged();
+    QMutexLocker lk(&mapStatusMutex_);
+    if (mapStatus_.find(key) != mapStatus_.end()) {
+        mapStatus_.remove(key);
+        Q_EMIT mapStatusChanged();
+        Q_EMIT closeMap(key);
+        if (!mapStatus_.size()) {
+            stopPositioning();
+        }
+    } else {
+        qWarning() << "Error: Can't set inactive a map that doesn't exists";
+    }
+}
+
+void
+PositionManager::setMapActive(QString key)
+{
+    if (mapStatus_.find(key) == mapStatus_.end()) {
+        mapStatus_.insert(key, false);
+        Q_EMIT mapStatusChanged();
+        // creation on QML
+        Q_EMIT openNewMap();
+
+    } else {
+        pinMap(key);
+    }
 }
 
 QString
@@ -233,7 +330,9 @@ PositionManager::getAvatar(const QString& accountId, const QString& uri)
 }
 
 QVariantMap
-PositionManager::parseJsonPosition(const QString& body, const QString& peerId)
+PositionManager::parseJsonPosition(const QString& accountId,
+                                   const QString& peerId,
+                                   const QString& body)
 {
     QJsonDocument temp = QJsonDocument::fromJson(body.toUtf8());
     QJsonObject jsonObject = temp.object();
@@ -250,6 +349,7 @@ PositionManager::parseJsonPosition(const QString& body, const QString& peerId)
             pos["time"] = i.value().toVariant();
 
         pos["author"] = peerId;
+        pos["account"] = accountId;
     }
     return pos;
 }
@@ -257,17 +357,25 @@ PositionManager::parseJsonPosition(const QString& body, const QString& peerId)
 void
 PositionManager::startPositionTimers(int timeSharing)
 {
-    set_timeSharingRemaining(timeSharing);
-    timerTimeLeftSharing_->start(1000);
-    timerStopSharing_->start(timeSharing);
+    mapTimerCountDown_[lrcInstance_->get_currentAccountId()] = timeSharing;
+    countdownUpdate();
+    countdownTimer_->start(1000);
 }
 
 void
-PositionManager::stopPositionTimers()
+PositionManager::stopPositionTimers(QString accountId)
 {
-    set_timeSharingRemaining(0);
-    timerTimeLeftSharing_->stop();
-    timerStopSharing_->stop();
+    // reset all timers
+    if (accountId == nullptr) {
+        mapTimerCountDown_.clear();
+    } else {
+        auto it = mapTimerCountDown_.find(accountId);
+        if (it != mapTimerCountDown_.end()) {
+            mapTimerCountDown_.erase(it);
+        }
+        if (!mapTimerCountDown_.size())
+            countdownTimer_->stop();
+    }
 }
 
 void
@@ -305,112 +413,185 @@ PositionManager::showNotification(const QString& accountId,
 }
 
 void
-PositionManager::onPositionReceived(const QString& accountId,
-                                    const QString& peerId,
-                                    const QString& body,
-                                    const uint64_t& timestamp,
-                                    const QString& daemonId)
+PositionManager::onNewConversation()
 {
-    // only show shared positions from contacts in the current conversation
-    const auto& convParticipants = lrcInstance_
-                                       ->getConversationFromConvUid(
-                                           lrcInstance_->get_selectedConvUid())
-                                       .participantsUris();
-    // to know if the position received is from someone in the current conversation
-    bool isPeerIdInConv = (std::find(convParticipants.begin(), convParticipants.end(), peerId)
-                           != convParticipants.end());
-
-    QVariantMap newPosition = parseJsonPosition(body, peerId);
-    auto getShareInfo = [&](bool update) -> QVariantMap {
-        QVariantMap shareInfo;
-        shareInfo["author"] = peerId;
-        if (!update) {
-            shareInfo["avatar"] = getAvatar(accountId, peerId);
+    set_mapAutoOpening(true);
+}
+
+void
+PositionManager::onNewAccount()
+{
+    QMutexLocker lk(&mapStatusMutex_);
+    for (auto it = mapStatus_.begin(); it != mapStatus_.end();) {
+        if (it.value() == false) {
+            Q_EMIT closeMap(it.key());
+            it = mapStatus_.erase(it);
+            Q_EMIT mapStatusChanged();
+        } else {
+            it++;
         }
-        shareInfo["long"] = newPosition["long"];
-        shareInfo["lat"] = newPosition["lat"];
-        return shareInfo;
-    };
-    auto endSharing = newPosition["type"] == "Stop";
+    }
+}
 
-    auto key = QPair<QString, QString> {accountId, peerId};
+bool
+PositionManager::isNewMessageTriggersMap(bool endSharing,
+                                         const QString& uri,
+                                         const QString& accountId)
+{
+    QMutexLocker lk(&mapStatusMutex_);
+    return !endSharing && (accountId == lrcInstance_->get_currentAccountId()) && mapAutoOpening_
+           && (uri != lrcInstance_->getCurrentAccountInfo().profileInfo.uri)
+           && (mapStatus_.find(accountId) == mapStatus_.end());
+}
 
-    if (!endSharing) {
-        // open map on position reception
-        if (!isMapActive_ && mapAutoOpening_ && isPeerIdInConv
-            && peerId != lrcInstance_->getCurrentAccountInfo().profileInfo.uri) {
-            set_isMapActive(true);
+void
+PositionManager::countdownUpdate()
+{
+    // First removal of timers and shared position
+    auto end = std::find_if(mapTimerCountDown_.begin(),
+                            mapTimerCountDown_.end(),
+                            [](const auto& end) { return end == 0; });
+    if (end != mapTimerCountDown_.end()) {
+        Q_EMIT sendCountdownUpdate(end.key(), end.value());
+        stopSharingPosition(end.key());
+    }
+    // When removals are done, countdown can be updated
+    for (auto it = mapTimerCountDown_.begin(); it != mapTimerCountDown_.end(); it++) {
+        if (it.value() != 0) {
+            Q_EMIT sendCountdownUpdate(it.key(), it.value());
+            it.value() -= 1000;
         }
     }
-    auto iter = std::find(currentConvSharingUris_.begin(), currentConvSharingUris_.end(), key);
-    if (iter == currentConvSharingUris_.end()) {
-        // New share
-        if (!endSharing) {
-            // list to save more information on position + watchdog
-            auto it = objectListSharingUris_.find(key);
-            auto isNewSharing = it == objectListSharingUris_.end();
-            if (isNewSharing) {
-                auto obj = new PositionObject(newPosition["lat"], newPosition["long"], this);
-
-                objectListSharingUris_.insert(key, obj);
-                set_sharingUrisCount(objectListSharingUris_.size());
-                connect(obj,
-                        &PositionObject::timeout,
-                        this,
-                        &PositionManager::onWatchdogTimeout,
-                        Qt::DirectConnection);
-            }
+}
 
-            if (isPeerIdInConv) {
-                currentConvSharingUris_.insert(key);
-                Q_EMIT positionShareAdded(getShareInfo(false));
-            } else if (isNewSharing && accountId != "") {
-                auto& convInfo = lrcInstance_->getConversationFromPeerUri(peerId, accountId);
-                if (!convInfo.uid.isEmpty()) {
-                    showNotification(accountId, convInfo.uid, peerId);
-                }
+void
+PositionManager::addPositionToMap(PositionKey key, QVariantMap position)
+{
+    // avatar only sent one time to qml, when a new position is added
+    position["avatar"] = getAvatar(key.first, key.second);
+    Q_EMIT positionShareAdded(position);
+}
+
+void
+PositionManager::addPositionToMemory(PositionKey key, QVariantMap positionReceived)
+{
+    // add the position to the list
+    auto obj = new PositionObject(positionReceived["lat"], positionReceived["long"], this);
+    objectListSharingUris_.insert(key, obj);
+
+    // information for qml
+    set_sharingUrisCount(objectListSharingUris_.size());
+
+    // watchdog
+    connect(obj,
+            &PositionObject::timeout,
+            this,
+            &PositionManager::onWatchdogTimeout,
+            Qt::DirectConnection);
+
+    auto& accountId = key.first;
+    auto& uri = key.second;
+    // Add position to the current map if needed)
+    addPositionToMap(key, positionReceived);
+
+    // show notification
+    if (accountId != "") {
+        QMutexLocker lk(&mapStatusMutex_);
+        if (mapStatus_.find(accountId) == mapStatus_.end()) {
+            auto& convInfo = lrcInstance_->getConversationFromPeerUri(uri, accountId);
+            if (!convInfo.uid.isEmpty()) {
+                showNotification(accountId, convInfo.uid, uri);
             }
-            // stop sharing position
+        }
+    }
+}
+
+void
+PositionManager::updatePositionInMemory(PositionKey key, QVariantMap positionReceived)
+{
+    auto it = objectListSharingUris_.find(key);
+    if (it != objectListSharingUris_.end()) {
+        if (it.value()) {
+            // reset watchdog
+            it.value()->resetWatchdog();
+            // update position
+            it.value()->updatePosition(positionReceived["lat"], positionReceived["long"]);
         } else {
-            auto it = objectListSharingUris_.find(key);
-            if (it != objectListSharingUris_.end()) {
-                it.value()->deleteLater();
-                objectListSharingUris_.erase(it);
-                set_sharingUrisCount(objectListSharingUris_.size());
-            }
+            qWarning() << "error in PositionManager::updatePositionInMemory(), it.value() is null";
         }
     } else {
-        // Update/remove existing
-        if (endSharing) {
-            // Remove
-            auto it = objectListSharingUris_.find(key);
-            if (it != objectListSharingUris_.end()) {
-                it.value()->deleteLater();
-                objectListSharingUris_.erase(it);
-                set_sharingUrisCount(objectListSharingUris_.size());
-            }
-            if (isPeerIdInConv) {
-                currentConvSharingUris_.remove(key);
-                Q_EMIT positionShareRemoved(peerId);
-                // close the map if you're not sharing and you don't receive position anymore
-                if (!positionShareConvIds_.length()
-                    && ((sharingUrisCount_ == 1
-                         && objectListSharingUris_.contains(QPair<QString, QString> {
-                             "", lrcInstance_->getCurrentAccountInfo().profileInfo.uri}))
-                        || sharingUrisCount_ == 0)) {
-                    set_isMapActive(false);
-                }
-            }
-        } else {
-            // Update
-            if (isPeerIdInConv)
-                Q_EMIT positionShareUpdated(getShareInfo(true));
-            // reset watchdog
+        qWarning()
+            << "Error: A position intented to be updated while not in objectListSharingUris_ ";
+    }
 
-            auto it = objectListSharingUris_.find(key);
-            if (it != objectListSharingUris_.end()) {
-                it.value()->resetWatchdog();
-            }
-        }
+    // update position on the map (if needed)
+    Q_EMIT positionShareUpdated(positionReceived);
+}
+
+void
+PositionManager::removePositionFromMemory(PositionKey key, QVariantMap positionReceived)
+{
+    // Remove
+    auto it = objectListSharingUris_.find(key);
+    if (it != objectListSharingUris_.end()) {
+        // free memory
+        it.value()->deleteLater();
+        // delete value
+        objectListSharingUris_.erase(it);
+        // update list count for qml
+        set_sharingUrisCount(objectListSharingUris_.size());
+    } else {
+        qWarning()
+            << "Error: A position intented to be removed while not in objectListSharingUris_ ";
+        return;
+    }
+    // if needed, remove from map
+    Q_EMIT positionShareRemoved(key.second, positionReceived["account"].toString());
+    // close the map if you're not sharing and you don't receive position anymore
+    if (!positionShareConvIds_.length()
+        && ((sharingUrisCount_ == 1
+             && objectListSharingUris_.begin().key().second
+                    == lrcInstance_->getCurrentAccountInfo().profileInfo.uri)
+            || sharingUrisCount_ == 0)) {
+        setMapInactive(lrcInstance_->get_currentAccountId());
+    }
+}
+
+void
+PositionManager::onPositionReceived(const QString& accountId,
+                                    const QString& peerId,
+                                    const QString& body,
+                                    const uint64_t& timestamp,
+                                    const QString& daemonId)
+{
+    // handlers variables
+
+    // parse the position from json
+    QVariantMap positionReceived = parseJsonPosition(accountId, peerId, body);
+
+    // is it a message that notify an end of position sharing
+    auto endSharing = positionReceived["type"] == "Stop";
+
+    // key to identify the peer
+    auto key = PositionKey {accountId, peerId};
+
+    // check if the position exists in all shared positions, even if not visible to the screen
+    auto findPeerIdinAllPeers = objectListSharingUris_.find(key);
+
+    // open the map on position reception if needed
+    if (isNewMessageTriggersMap(endSharing, peerId, accountId)) {
+        setMapActive(accountId);
+    }
+
+    // if the position already exists
+    if (findPeerIdinAllPeers != objectListSharingUris_.end()) {
+        if (endSharing)
+            removePositionFromMemory(key, positionReceived);
+        else
+            updatePositionInMemory(key, positionReceived);
+
+    } else {
+        // It is the first time a position is received from this peer
+        addPositionToMemory(key, positionReceived);
     }
 }
diff --git a/src/app/positionmanager.h b/src/app/positionmanager.h
index fc8c53a3c..e75af1eb7 100644
--- a/src/app/positionmanager.h
+++ b/src/app/positionmanager.h
@@ -24,19 +24,21 @@
 #include "positionobject.h"
 #include "systemtray.h"
 
+#include <QMutex>
 #include <QObject>
 #include <QString>
 
 class PositionManager : public QmlAdapterBase
 {
     Q_OBJECT
-    QML_RO_PROPERTY(bool, isMapActive)
-    QML_RO_PROPERTY(int, timeSharingRemaining)
+    // map of elements : map key and isUnpin
+    QML_PROPERTY(QVariantMap, mapStatus)
+    QML_PROPERTY(bool, mapAutoOpening)
     QML_PROPERTY(int, positionShareConvIdsCount)
     QML_PROPERTY(int, sharingUrisCount)
-    QML_PROPERTY(bool, mapAutoOpening)
 public:
-    explicit PositionManager(SystemTray* systemTray,
+    explicit PositionManager(AppSettingsManager* settingsManager,
+                             SystemTray* systemTray,
                              LRCInstance* instance,
                              QObject* parent = nullptr);
     ~PositionManager() = default;
@@ -45,30 +47,49 @@ Q_SIGNALS:
     void positioningError(const QString error);
     void positionShareAdded(const QVariantMap& shareInfo);
     void positionShareUpdated(const QVariantMap& posInfo);
-    void positionShareRemoved(const QString& uri);
+    void positionShareRemoved(const QString& uri, const QString& accountId);
+    void openNewMap();
+    void closeMap(const QString& key);
+    void pinMapSignal(const QString& key);
+    void unPinMapSignal(const QString& key);
+    void localPositionReceived(const QString& accountId, const QString& peerId, const QString& body);
+    void makeVisibleSharingButton(const QString& accountId);
+    void sendCountdownUpdate(const QString& accountId, const int remainingTime);
 
 protected:
     void safeInit() override;
 
     QString getAvatar(const QString& accountId, const QString& peerId);
-    QVariantMap parseJsonPosition(const QString& body, const QString& peerId);
+    QVariantMap parseJsonPosition(const QString& accountId,
+                                  const QString& peerId,
+                                  const QString& body);
+    void addPositionToMap(PositionKey key, QVariantMap position);
+    void addPositionToMemory(PositionKey key, QVariantMap positionReceived);
+    void updatePositionInMemory(PositionKey key, QVariantMap positionReceived);
+    void removePositionFromMemory(PositionKey key, QVariantMap positionReceived);
     void positionWatchDog();
     void startPositionTimers(int timeSharing);
-    void stopPositionTimers();
+    void stopPositionTimers(QString accountId = {});
+    bool isNewMessageTriggersMap(bool endSharing, const QString& uri, const QString& accountId);
+    void countdownUpdate();
+    void sendStopMessage(QString accountId = "", const QString convId = "");
 
     Q_INVOKABLE void connectAccountModel();
-    Q_INVOKABLE void setMapActive(bool state);
-    Q_INVOKABLE void sharePosition(int maximumTime);
-    Q_INVOKABLE void stopSharingPosition(const QString convId = "");
+    Q_INVOKABLE void pinMap(QString key);
+    Q_INVOKABLE void unPinMap(QString key);
+    Q_INVOKABLE void setMapActive(QString key);
+    Q_INVOKABLE void setMapInactive(const QString key);
+    Q_INVOKABLE void sharePosition(int maximumTime, QString accountId, QString convId);
+    Q_INVOKABLE void stopSharingPosition(QString accountId = "", const QString convId = "");
 
     Q_INVOKABLE void startPositioning();
     Q_INVOKABLE void stopPositioning();
 
-    Q_INVOKABLE QString getSelectedConvId();
-    Q_INVOKABLE bool isPositionSharedToConv(const QString& convUri);
-    Q_INVOKABLE bool isConvSharingPosition(const QString& convUri);
+    Q_INVOKABLE bool isPositionSharedToConv(const QString& accountId, const QString& convUid);
+    Q_INVOKABLE bool isConvSharingPosition(const QString& accountId, const QString& convUri);
 
-    Q_INVOKABLE void loadPreviousLocations();
+    Q_INVOKABLE void loadPreviousLocations(QString& accountId);
+    Q_INVOKABLE QString getmapTitle(QString& accountId, QString convId = "");
 
 private Q_SLOTS:
     void onPositionErrorReceived(const QString error);
@@ -77,16 +98,21 @@ private Q_SLOTS:
                             const QString& body,
                             const uint64_t& timestamp,
                             const QString& daemonId);
-    void sendPosition(const QString& body);
+    void sendPosition(const QString& body, bool triggersLocalPosition = true);
     void onWatchdogTimeout();
     void showNotification(const QString& accountId, const QString& convId, const QString& from);
+    void onNewConversation();
+    void onNewAccount();
 
 private:
     SystemTray* systemTray_;
     std::unique_ptr<Positioning> localPositioning_;
-    QTimer* timerTimeLeftSharing_ = nullptr;
-    QTimer* timerStopSharing_ = nullptr;
-    QSet<QPair<QString, QString>> currentConvSharingUris_;
-    QMap<QPair<QString, QString>, PositionObject*> objectListSharingUris_;
-    QList<QPair<QString, QString>> positionShareConvIds_;
+    QMap<QString, int> mapTimerCountDown_;
+    QTimer* countdownTimer_ = nullptr;
+    // map of all shared position by peers
+    QMap<PositionKey, PositionObject*> objectListSharingUris_;
+    // list of all the peers the user is sharing position to
+    QList<PositionKey> positionShareConvIds_;
+    QMutex mapStatusMutex_;
+    AppSettingsManager* settingsManager_;
 };
diff --git a/src/app/positionobject.cpp b/src/app/positionobject.cpp
index e7a970526..a9e8b0453 100644
--- a/src/app/positionobject.cpp
+++ b/src/app/positionobject.cpp
@@ -2,7 +2,7 @@
 
 PositionObject::PositionObject(QVariant latitude, QVariant longitude, QObject* parent)
     : QObject(parent)
-    , resetTime(20000)
+    , resetTime(40000)
     , longitude_(longitude)
     , latitude_(latitude)
 
@@ -28,3 +28,10 @@ PositionObject::getLatitude()
 {
     return latitude_;
 }
+
+void
+PositionObject::updatePosition(QVariant latitude, QVariant longitude)
+{
+    longitude_ = longitude;
+    latitude_ = latitude;
+}
diff --git a/src/app/positionobject.h b/src/app/positionobject.h
index f2edc2ca8..94e60f076 100644
--- a/src/app/positionobject.h
+++ b/src/app/positionobject.h
@@ -20,6 +20,8 @@ public:
     QVariant getLongitude();
     QVariant getLatitude();
 
+    void updatePosition(QVariant latitude, QVariant longitude);
+
 private:
     QVariant latitude_;
     QVariant longitude_;
diff --git a/src/app/qmlregister.cpp b/src/app/qmlregister.cpp
index 2359c8014..d42c8cc5b 100644
--- a/src/app/qmlregister.cpp
+++ b/src/app/qmlregister.cpp
@@ -112,7 +112,7 @@ registerTypes(QQmlEngine* engine,
     // setup the adapters (their lifetimes are that of MainApplication)
     auto callAdapter = new CallAdapter(systemTray, lrcInstance, parent);
     auto messagesAdapter = new MessagesAdapter(settingsManager, previewEngine, lrcInstance, parent);
-    auto positionManager = new PositionManager(systemTray, lrcInstance, parent);
+    auto positionManager = new PositionManager(settingsManager, systemTray, lrcInstance, parent);
     auto conversationsAdapter = new ConversationsAdapter(systemTray, lrcInstance, parent);
     auto avAdapter = new AvAdapter(lrcInstance, parent);
     auto contactAdapter = new ContactAdapter(lrcInstance, parent);
diff --git a/src/app/settingsview/components/GeneralSettingsPage.qml b/src/app/settingsview/components/GeneralSettingsPage.qml
index 82f7a93a4..e0c50a2ed 100644
--- a/src/app/settingsview/components/GeneralSettingsPage.qml
+++ b/src/app/settingsview/components/GeneralSettingsPage.qml
@@ -63,6 +63,16 @@ Rectangle {
             itemWidth: preferredColumnWidth
         }
 
+        // location sharing setting panel
+        LocationSharingSettings {
+            Layout.fillWidth: true
+            Layout.topMargin: JamiTheme.preferredMarginSize
+            Layout.leftMargin: JamiTheme.preferredMarginSize
+            Layout.rightMargin: JamiTheme.preferredMarginSize
+
+            itemWidth: preferredColumnWidth
+        }
+
         // file transfer setting panel
         FileTransferSettings {
             id: fileTransferSettings
diff --git a/src/app/settingsview/components/LocationSharingSettings.qml b/src/app/settingsview/components/LocationSharingSettings.qml
new file mode 100644
index 000000000..a21009b2d
--- /dev/null
+++ b/src/app/settingsview/components/LocationSharingSettings.qml
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ * Author: Nicolas Vengeon <nicolas.vengeon@savoirfairelinux.com>
+ *
+ * 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.Models 1.1
+import net.jami.Adapters 1.1
+import net.jami.Enums 1.1
+import net.jami.Constants 1.1
+
+import "../../commoncomponents"
+
+ColumnLayout {
+    id: root
+
+    property int itemWidth
+
+    Label {
+        Layout.fillWidth: true
+
+        text: JamiStrings.locationSharingLabel
+        font.pointSize: JamiTheme.headerFontSize
+        font.kerning: true
+        color: JamiTheme.textColor
+
+        horizontalAlignment: Text.AlignLeft
+        verticalAlignment: Text.AlignVCenter
+    }
+
+    ToggleSwitch {
+        id: isTimeLimit
+
+        visible: WITH_WEBENGINE
+
+        Layout.fillWidth: true
+        Layout.leftMargin: JamiTheme.preferredMarginSize
+
+        checked: UtilsAdapter.getAppValue(Settings.PositionShareLimit)
+
+        labelText: JamiStrings.positionShareLimit
+        fontPointSize: JamiTheme.settingsFontSize
+
+        tooltipText: JamiStrings.positionShareLimit
+
+        onSwitchToggled: {
+            positionSharingLimitation = !UtilsAdapter.getAppValue(Settings.PositionShareLimit)
+            UtilsAdapter.setAppValue(Settings.PositionShareLimit,
+                                     positionSharingLimitation)
+
+        }
+        property bool positionSharingLimitation: UtilsAdapter.getAppValue(Settings.PositionShareLimit)
+    }
+
+    RowLayout {
+        id: timeSharingLocation
+
+        Layout.fillWidth: true
+        Layout.preferredHeight: JamiTheme.preferredFieldHeight
+        Layout.leftMargin: JamiTheme.preferredMarginSize
+        visible: isTimeLimit.positionSharingLimitation
+
+        function standartCountdown(minutes) {
+            var hour = Math.floor(minutes / 60)
+            var min = minutes % 60
+            if (hour) {
+                if (min)
+                    return qsTr("%1h%2min").arg(hour).arg(min)
+                else
+                    return qsTr("%1h").arg(hour)
+            }
+            return qsTr("%1min").arg(min)
+        }
+
+        Text {
+            Layout.fillWidth: true
+            Layout.rightMargin: JamiTheme.preferredMarginSize / 2
+
+            color: JamiTheme.textColor
+            text: JamiStrings.positionShareDuration
+            font.pointSize: JamiTheme.settingsFontSize
+            font.kerning: true
+            elide: Text.ElideRight
+            horizontalAlignment: Text.AlignLeft
+            verticalAlignment: Text.AlignVCenter
+        }
+
+        Text {
+            id: timeSharingLocationValueLabel
+
+            Layout.alignment: Qt.AlignRight
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            Layout.rightMargin: JamiTheme.preferredMarginSize / 2
+
+            color: JamiTheme.textColor
+            text: timeSharingLocation.standartCountdown(UtilsAdapter.getAppValue(Settings.PositionShareDuration))
+
+            font.pointSize: JamiTheme.settingsFontSize
+            font.kerning: true
+
+            horizontalAlignment: Text.AlignRight
+            verticalAlignment: Text.AlignVCenter
+        }
+
+        Slider {
+            id: timeSharingSlider
+
+            Layout.maximumWidth: itemWidth
+            Layout.alignment: Qt.AlignRight
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+
+            value: Math.log(UtilsAdapter.getAppValue(Settings.PositionShareDuration))
+
+            from: 0.5
+            to: Math.log(600)
+            stepSize: 0.05
+
+            onMoved: {
+                timeSharingLocationValueLabel.text = timeSharingLocation.standartCountdown(Math.floor(Math.exp(value)))
+                UtilsAdapter.setAppValue(Settings.PositionShareDuration, Math.floor(Math.exp(value)))
+            }
+
+            MaterialToolTip {
+                id: toolTip
+
+                text: JamiStrings.positionShareDuration
+                visible: parent.hovered
+                delay: Qt.styleHints.mousePressAndHoldInterval
+            }
+        }
+    }
+}
diff --git a/src/app/webengine/map/MapPosition.qml b/src/app/webengine/map/MapPosition.qml
index 17d9f7232..6f5ea5b05 100644
--- a/src/app/webengine/map/MapPosition.qml
+++ b/src/app/webengine/map/MapPosition.qml
@@ -15,6 +15,7 @@
  * 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
@@ -23,373 +24,212 @@ import QtWebEngine
 
 import net.jami.Models 1.1
 import net.jami.Adapters 1.1
+import net.jami.Enums 1.1
 import net.jami.Constants 1.1
 
 import "../../commoncomponents"
 
-Rectangle {
-    id: mapPopup
-
-    x: xPos
-    y: yPos
-    width: isFullScreen ? root.width : windowSize
-    height: isMinimised
-            ? buttonOverlay.height + buttonsChoseSharing.height + 30
-            : isFullScreen ? root.height - yPos : windowSize
-
-    property bool isFullScreen: false
-    property bool isMinimised: false
-    property real windowSize: windowPreferedSize > JamiTheme.minimumMapWidth
-                              ? windowPreferedSize
-                              : JamiTheme.minimumMapWidth
-    property real windowPreferedSize: root.width > root.height
-                                      ? root.height / 3
-                                      : root.width / 3
-    property real xPos: 0
-    property real yPos: JamiTheme.chatViewHeaderPreferredHeight
-
-    WebEngineView {
-        id: webView
-
-        width: parent.width
-        height: parent.height
-
-        property string mapHtml: ":/webengine/map/map.html"
-        property string olCss: ":/webengine/map/ol.css"
-        property string mapJs: "../../webengine/map/map.js"
-        property string olJs: "../../webengine/map/ol.js"
-        property bool isLoaded: false
-        property var positionList: PositionManager.positionList;
-        property var avatarPositionList: PositionManager.avatarPositionList;
-        property bool isSharing: (PositionManager.positionShareConvIdsCount !== 0 )
-
-        function loadScripts () {
-            var scriptMapJs = {
-                sourceUrl: Qt.resolvedUrl(mapJs),
-                injectionPoint: WebEngineScript.DocumentReady,
-                worldId: WebEngineScript.MainWorld
-            }
+Item {
+    id: root
 
-            var scriptOlJs = {
-                sourceUrl: Qt.resolvedUrl(olJs),
-                injectionPoint: WebEngineScript.DocumentReady,
-                worldId: WebEngineScript.MainWorld
-            }
+    property bool isUnpin: false
+    property real maxWidth
+    property real maxHeight
+    property string attachedAccountId
+    property string currentAccountId: CurrentAccount.id
+    property string currentConvId: CurrentConversation.id
+    property bool isSharing: (PositionManager.positionShareConvIdsCount !== 0)
+    property bool isSharingToCurrentConversation
 
-            userScripts.collection = [ scriptOlJs, scriptMapJs ]
-        }
-        Connections {
-            target: PositionManager
-
-            function onPositionShareAdded(shareInfo) {
-                if(webView.isLoaded) {
-                    var curLong = shareInfo.long
-                    var curLat = shareInfo.lat
-                    webView.runJavaScript("newPosition([" + curLong + "," + curLat  + "], '" + shareInfo.author + "', '" + shareInfo.avatar + "' )" );
-                    webView.runJavaScript("zoomTolayersExtent()" );
-                }
-
-            }
+    function closeMapPosition() {
+        root.destroy()
+    }
 
-            function onPositionShareUpdated(shareInfo) {
-                if(webView.isLoaded) {
-                    var curLong = shareInfo.long
-                    var curLat = shareInfo.lat
-                    webView.runJavaScript("updatePosition([" + curLong + "," + curLat  + "], '" + shareInfo.author + "' )" );
-                }
-            }
+    Connections {
+        target: PositionManager
 
-            function onPositionShareRemoved(author) {
-                if(webView.isLoaded) {
-                    webView.runJavaScript("removePosition( '" + author + "' )" );
-                    webView.runJavaScript("zoomTolayersExtent()" );
-                }
+        function onPinMapSignal(key) {
+            if (key === attachedAccountId) {
+                isUnpin = false
+                mapObject.state = "pin"
+                windowUnpin.close()
             }
         }
 
-        Component.onCompleted: {
-            loadHtml(UtilsAdapter.qStringFromFile(mapHtml), mapHtml)
-            loadScripts()
+        function onCloseMap(key) {
+            if (key === attachedAccountId )
+                closeMapPosition()
         }
 
-        onLoadingChanged: function (loadingInfo) {
-            if (loadingInfo.status === WebEngineView.LoadSucceededStatus) {
-                runJavaScript(UtilsAdapter.getStyleSheet("olcss",UtilsAdapter.qStringFromFile(olCss)))
-                webView.isLoaded = true
-                runJavaScript("setMapView([" + 0 + ","+ 0  + "], " + 1 + " );" );
-                PositionManager.startPositioning()
-                //load locations that were received before this conversation was opened
-                PositionManager.loadPreviousLocations();
+        function onUnPinMapSignal(key) {
+            if (key === attachedAccountId ) {
+                isUnpin = true
+                mapObject.state = "unpin"
+                windowUnpin.show()
             }
         }
     }
 
-    ColumnLayout {
-        id: buttonsChoseSharing
+    Window {
+        id: windowUnpin
 
-        anchors.horizontalCenter: mapPopup.horizontalCenter
-        anchors.margins: 10
-        anchors.bottom: mapPopup.bottom
+        width: parentPin.width
+        height: parentPin.height
+        visible: false
+        title: PositionManager.getmapTitle(attachedAccountId)
 
-        property bool shortSharing: true
+        Item {
+            id: parentUnPin
 
-        RowLayout {
-            Layout.alignment: Qt.AlignHCenter
-
-            MaterialButton {
-                id: shortSharingButton
+            width: mapObject.width
+            height: mapObject.height
+        }
 
-                preferredWidth: text.contentWidth
-                visible: !webView.isSharing
-                textLeftPadding: JamiTheme.buttontextPadding
-                textRightPadding: JamiTheme.buttontextPadding
-                primary: true
-                text: JamiStrings.shortSharing
-                color: buttonsChoseSharing.shortSharing ? JamiTheme.buttonTintedBluePressed : JamiTheme.buttonTintedBlue
-                fontSize: JamiTheme.timerButtonsFontSize
-                onClicked: {
-                    buttonsChoseSharing.shortSharing = true
-                }
+        onClosing: {
+            if (isUnpin) {
+                PositionManager.setMapInactive(attachedAccountId)
             }
+        }
+    }
 
-            MaterialButton {
-                id: longSharingButton
-
-                preferredWidth: text.contentWidth
-                visible: !webView.isSharing
-                textLeftPadding: JamiTheme.buttontextPadding
-                textRightPadding: JamiTheme.buttontextPadding
-                primary: true
-                text: JamiStrings.longSharing
-                color: !buttonsChoseSharing.shortSharing ? JamiTheme.buttonTintedBluePressed : JamiTheme.buttonTintedBlue
-                fontSize: JamiTheme.timerButtonsFontSize
-                onClicked: {
-                    buttonsChoseSharing.shortSharing = false;
+    Item {
+        id: parentPin
+
+        width: mapObject.width
+        height: mapObject.height
+
+        Rectangle {
+            id: mapObject
+
+            x: xPos
+            y: yPos
+            width: root.isUnpin
+                   ? windowUnpin.width
+                   : isFullScreen ? root.maxWidth : windowSize
+            height: root.isUnpin
+                    ? windowUnpin.height
+                    : isFullScreen ? root.maxHeight - yPos : windowSize
+
+            property bool isFullScreen: false
+            property real windowSize: windowPreferedSize > JamiTheme.minimumMapWidth
+                                      ? windowPreferedSize
+                                      : JamiTheme.minimumMapWidth
+            property real windowPreferedSize: root.maxWidth > root.maxHeight
+                                              ? root.maxHeight / 3
+                                              : root.maxWidth / 3
+            property real xPos: 0
+            property real yPos: root.isUnpin ? 0 : JamiTheme.chatViewHeaderPreferredHeight
+
+            states: [ State {
+                    name: "unpin"
+                    ParentChange { target: mapObject; parent: parentUnPin; x:0; y:0 }
+                },
+                State {
+                    name: "pin"
+                    ParentChange { target: mapObject; parent: parentPin; x:xPos; y:JamiTheme.chatViewHeaderPreferredHeight }
+                }
+            ]
+            property alias webView: webView
+
+            WebEngineView {
+                id: webView
+
+                layer.enabled: !isFullScreen
+                layer.effect: OpacityMask {
+                    maskSource:
+                        Rectangle {
+                            width: webView.width
+                            height: webView.height
+                            radius: 10
+                    }
                 }
-            }
-
-            Rectangle {
-
-                radius: 10
-                width: textTimer.width + 15
-                height: textTimer.height + 15
-                color: JamiTheme.mapButtonsOverlayColor
-                visible: webView.isSharing && PositionManager.timeSharingRemaining
-
-                Text {
-                    id: textTimer
 
-                    anchors.centerIn: parent
-                    color: JamiTheme.mapButtonColor
-                    text: remainingTimeMs <= 1
-                          ? JamiStrings.minuteLeft.arg(remainingTimeMs)
-                          : JamiStrings.minutesLeft.arg(remainingTimeMs)
+                width: parent.width
+                height: parent.height
 
-                    Layout.alignment: Qt.AlignHCenter
+                property string mapHtml: ":/webengine/map/map.html"
+                property string olCss: ":/webengine/map/ol.css"
+                property string mapJs: "../../webengine/map/map.js"
+                property string olJs: "../../webengine/map/ol.js"
+                property bool isLoaded: false
+                property var positionList: PositionManager.positionList;
+                property var avatarPositionList: PositionManager.avatarPositionList;
 
-                    property int remainingTimeMs: Math.ceil(PositionManager.timeSharingRemaining / 1000 / 60)
-                }
-            }
-        }
-
-        RowLayout {
-            id: sharePositionLayout
-            Layout.alignment: Qt.AlignHCenter
-
-            MaterialButton {
-                id: sharePositionButton
-
-                preferredWidth: text.contentWidth
-                textLeftPadding: JamiTheme.buttontextPadding
-                textRightPadding: JamiTheme.buttontextPadding
-                primary: true
-                visible: ! PositionManager.isPositionSharedToConv(PositionManager.getSelectedConvId())
-                text: JamiStrings.shareLocation
-                color: isError
-                       ? JamiTheme.buttonTintedGreyInactive
-                       : JamiTheme.buttonTintedBlue
-                hoveredColor: isError
-                              ? JamiTheme.buttonTintedGreyInactive
-                              : JamiTheme.buttonTintedBlueHovered
-                pressedColor: isError
-                              ? JamiTheme.buttonTintedGreyInactive
-                              : JamiTheme.buttonTintedBluePressed
-                Layout.alignment: Qt.AlignHCenter
-                property bool isHovered: false
-                property string positioningError: "default"
-                property bool isError: positioningError.length
-                function errorString(posError) {
-                    if (posError === "locationServicesError")
-                        return JamiStrings.locationServicesError
-                    return JamiStrings.locationServicesClosedError
-                }
-
-                onClicked: {
-                    if (!isError) {
-                        if( buttonsChoseSharing.shortSharing)
-                            PositionManager.sharePosition(10 * 60 * 1000);
-                        else
-                            PositionManager.sharePosition(60 * 60 * 1000);
-                        visible = false
+                function loadScripts () {
+                    var scriptMapJs = {
+                        sourceUrl: Qt.resolvedUrl(mapJs),
+                        injectionPoint: WebEngineScript.DocumentReady,
+                        worldId: WebEngineScript.MainWorld
                     }
-                }
 
-                onHoveredChanged: {
-                    isHovered = !isHovered
-                }
+                    var scriptOlJs = {
+                        sourceUrl: Qt.resolvedUrl(olJs),
+                        injectionPoint: WebEngineScript.DocumentReady,
+                        worldId: WebEngineScript.MainWorld
+                    }
 
-                MaterialToolTip {
-                    visible: sharePositionButton.isHovered
-                             && sharePositionButton.isError && (sharePositionButton.positioningError !== "default")
-                    x: 0
-                    y: 0
-                    text: sharePositionButton.errorString(sharePositionButton.positioningError)
+                    userScripts.collection = [ scriptOlJs, scriptMapJs ]
                 }
                 Connections {
                     target: PositionManager
-                    function onPositioningError (err) {
-                        sharePositionButton.positioningError = err;
-                    }
-                }
-            }
-            MaterialButton {
-                id: stopSharingPositionButton
-
-                preferredWidth: text.contentWidth
-                textLeftPadding: JamiTheme.buttontextPadding
-                textRightPadding: JamiTheme.buttontextPadding
-                primary: true
-                visible: webView.isSharing
-                text:   JamiStrings.stopSharingLocation
-                color: isError
-                       ? JamiTheme.buttonTintedGreyInactive
-                       : JamiTheme.buttonTintedRed
-                hoveredColor: isError
-                              ? JamiTheme.buttonTintedGreyInactive
-                              : JamiTheme.buttonTintedRedHovered
-                pressedColor: isError
-                              ? JamiTheme.buttonTintedGreyInactive
-                              :  JamiTheme.buttonTintedRedPressed
-                Layout.alignment: Qt.AlignHCenter
-                property bool isHovered: false
-                property string positioningError
-                property bool isError: positioningError.length
-                onClicked: {
-                    if (!isError) {
-                        if (PositionManager.positionShareConvIdsCount >= 2) {
-                            stopSharingPositionPopup.open()
-                        } else {
-                            PositionManager.stopSharingPosition();
-                            sharePositionButton.visible = true
+
+                    function onPositionShareAdded(shareInfo) {
+                        if(webView.isLoaded) {
+                            if (shareInfo.account === attachedAccountId) {
+                                var curLong = shareInfo.long
+                                var curLat = shareInfo.lat
+                                webView.runJavaScript("newPosition([" + curLong + "," + curLat  + "], '" + shareInfo.author + "', '" + shareInfo.avatar + "' )" );
+                                webView.runJavaScript("zoomTolayersExtent()" );
+                            }
                         }
                     }
-                }
-            }
-        }
-    }
-
-    StopSharingPositionPopup {
-        id: stopSharingPositionPopup
-
-        property alias shareButtonVisibility: sharePositionButton.visible
-    }
-
-    Rectangle {
-        id: buttonOverlay
 
-        anchors.right: webView.right
-        anchors.top: webView.top
-        anchors.margins: 10
-        radius: 10
-        width: lay.width + 10
-        height: lay.height + 10
-        color: JamiTheme.mapButtonsOverlayColor
-
-        RowLayout {
-            id: lay
-
-            anchors.centerIn: parent
-
-            PushButton {
-                id: btnCenter
-
-                toolTipText: JamiStrings.centerMapTooltip
-                imageColor: JamiTheme.mapButtonColor
-                normalColor: JamiTheme.transparentColor
-                source: JamiResources.share_location_svg
-                onClicked: {
-                    webView.runJavaScript("zoomTolayersExtent()" );
-                }
-            }
+                    function onPositionShareUpdated(shareInfo) {
+                        if(webView.isLoaded) {
+                            if (shareInfo.account === attachedAccountId) {
+                                var curLong = shareInfo.long
+                                var curLat = shareInfo.lat
+                                webView.runJavaScript("updatePosition([" + curLong + "," + curLat  + "], '" + shareInfo.author + "' )" );
+                            }
+                        }
+                    }
 
-            PushButton {
-                id: btnMove
-
-                toolTipText: JamiStrings.dragMapTooltip
-                imageColor: JamiTheme.mapButtonColor
-                normalColor: JamiTheme.transparentColor
-                source: JamiResources.move_svg
-
-                MouseArea {
-                    anchors.fill: parent
-                    drag.target: mapPopup
-                    drag.minimumX: 0
-                    drag.maximumX: root.width - mapPopup.width
-                    drag.minimumY: 0
-                    drag.maximumY: root.height - mapPopup.height
+                    function onPositionShareRemoved(author, accountId) {
+                        if(webView.isLoaded) {
+                            if (accountId === attachedAccountId) {
+                                webView.runJavaScript("removePosition( '" + author + "' )" );
+                                webView.runJavaScript("zoomTolayersExtent()" );
+                            }
+                        }
+                    }
                 }
-            }
 
-            PushButton {
-                id: btnminimize
-
-                toolTipText: isMinimised
-                            ? JamiStrings.extendMapTooltip
-                            : JamiStrings.minimizeMapTooltip
-                imageColor: JamiTheme.mapButtonColor
-                normalColor: JamiTheme.transparentColor
-                source: isMinimised
-                        ? JamiResources.close_fullscreen_24dp_svg
-                        : JamiResources.minimize_svg
-                onClicked: {
-                    isMinimised = !isMinimised
-                    isFullScreen = false;
+                Component.onCompleted: {
+                    loadHtml(UtilsAdapter.qStringFromFile(mapHtml), mapHtml)
+                    loadScripts()
                 }
-            }
 
-            PushButton {
-                id: btnmaximise
-
-                toolTipText: isFullScreen
-                            ? JamiStrings.reduceMapTooltip
-                            : JamiStrings.maximizeMapTooltip
-                imageColor: JamiTheme.mapButtonColor
-                normalColor: JamiTheme.transparentColor
-                source: isFullScreen? JamiResources.close_fullscreen_24dp_svg : JamiResources.open_in_full_24dp_svg
-                onClicked: {
-                    if (!isFullScreen && !isMinimised) {
-                        mapPopup.x = mapPopup.xPos
-                        mapPopup.y = mapPopup.yPos
+                onLoadingChanged: function (loadingInfo) {
+                    if (loadingInfo.status === WebEngineView.LoadSucceededStatus) {
+                        attachedAccountId = CurrentAccount.id
+                        runJavaScript(UtilsAdapter.getStyleSheet("olcss",UtilsAdapter.qStringFromFile(olCss)))
+                        webView.isLoaded = true
+                        runJavaScript("setMapView([" + 0 + ","+ 0  + "], " + 1 + " );" );
+                        PositionManager.startPositioning()
+                        //load locations that were received before this conversation was opened
+                        PositionManager.loadPreviousLocations(attachedAccountId);
                     }
-                    isFullScreen = !isFullScreen
-                    isMinimised = false;
                 }
             }
 
-            PushButton {
-                id: btnClose
+            MapPositionSharingControl {}
 
-                toolTipText: JamiStrings.closeMapTooltip
-                imageColor: JamiTheme.mapButtonColor
-                normalColor: JamiTheme.transparentColor
-                source: JamiResources.round_close_24dp_svg
+            MapPositionOverlay {}
 
-                onClicked: {
-                    PositionManager.stopPositioning();
-                    PositionManager.setMapActive(false);
-                    PositionManager.mapAutoOpening = false;
+            StopSharingPositionPopup {
+                id: stopSharingPositionPopup
 
-                }
+                property alias attachedAccountId: root.attachedAccountId
             }
         }
     }
diff --git a/src/app/webengine/map/MapPositionOverlay.qml b/src/app/webengine/map/MapPositionOverlay.qml
new file mode 100644
index 000000000..8383addc1
--- /dev/null
+++ b/src/app/webengine/map/MapPositionOverlay.qml
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ * Author: Nicolas Vengeon <nicolas.vengeon@savoirfairelinux.com>
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+import QtQuick
+import QtQuick.Layouts
+
+import net.jami.Constants 1.1
+import net.jami.Adapters 1.1
+
+import "../../commoncomponents"
+
+Rectangle {
+    id: root
+
+    anchors.right: webView.right
+    anchors.top: webView.top
+    anchors.margins: 10
+    radius: 10
+    width: lay.width + 10
+    height: lay.height + 10
+    color: JamiTheme.mapButtonsOverlayColor
+
+    RowLayout {
+        id: lay
+
+        anchors.centerIn: parent
+
+        PushButton {
+            id: btnUnpin
+
+            toolTipText: !isUnpin ? JamiStrings.unpin : JamiStrings.pinWindow
+            imageColor: JamiTheme.mapButtonColor
+            normalColor: JamiTheme.transparentColor
+            source: JamiResources.unpin_svg
+            onClicked: {
+                if (!isUnpin) {
+                    PositionManager.unPinMap(attachedAccountId)
+                } else {
+                    PositionManager.pinMap(attachedAccountId)
+                }
+            }
+        }
+
+        PushButton {
+            id: btnCenter
+
+            toolTipText: JamiStrings.centerMapTooltip
+            imageColor: JamiTheme.mapButtonColor
+            normalColor: JamiTheme.transparentColor
+            source: JamiResources.share_location_svg
+            onClicked: {
+                webView.runJavaScript("zoomTolayersExtent()" );
+            }
+        }
+
+        PushButton {
+            id: btnMove
+
+            toolTipText: JamiStrings.dragMapTooltip
+            imageColor: JamiTheme.mapButtonColor
+            normalColor: JamiTheme.transparentColor
+            source: JamiResources.move_svg
+            visible: !isUnpin
+
+            MouseArea {
+                anchors.fill: parent
+                drag.target: mapObject
+                drag.minimumX: 0
+                drag.maximumX: maxWidth - mapObject.maxWidth
+                drag.minimumY: 0
+                drag.maximumY: maxHeight - mapObject.maxHeight
+            }
+        }
+
+        PushButton {
+            id: btnMaximise
+
+            visible: !isUnpin
+            toolTipText: mapObject.isFullScreen
+                         ? JamiStrings.reduceMapTooltip
+                         : JamiStrings.maximizeMapTooltip
+            imageColor: JamiTheme.mapButtonColor
+            normalColor: JamiTheme.transparentColor
+            source: mapObject.isFullScreen? JamiResources.close_fullscreen_24dp_svg : JamiResources.open_in_full_24dp_svg
+            onClicked: {
+                if (!mapObject.isFullScreen) {
+                    mapObject.x = mapObject.xPos
+                    mapObject.y = mapObject.yPos
+                }
+
+                mapObject.isFullScreen = !mapObject.isFullScreen
+            }
+        }
+
+        PushButton {
+            id: btnClose
+
+            toolTipText: JamiStrings.closeMapTooltip
+            imageColor: JamiTheme.mapButtonColor
+            normalColor: JamiTheme.transparentColor
+            source: JamiResources.round_close_24dp_svg
+            visible: !isUnpin
+
+            onClicked: {
+                PositionManager.setMapInactive(attachedAccountId)
+                PositionManager.mapAutoOpening = false
+            }
+        }
+    }
+}
diff --git a/src/app/webengine/map/MapPositionSharingControl.qml b/src/app/webengine/map/MapPositionSharingControl.qml
new file mode 100644
index 000000000..95e20a3ae
--- /dev/null
+++ b/src/app/webengine/map/MapPositionSharingControl.qml
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ * Author: Nicolas Vengeon <nicolas.vengeon@savoirfairelinux.com>
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+import QtQuick
+import QtQuick.Layouts
+
+import net.jami.Constants 1.1
+import net.jami.Adapters 1.1
+import net.jami.Enums 1.1
+
+import "../../commoncomponents"
+
+ColumnLayout {
+    id: root
+
+    anchors.horizontalCenter: mapObject.horizontalCenter
+    anchors.margins: 10
+    anchors.bottom: mapObject.bottom
+
+    RowLayout {
+        Layout.alignment: Qt.AlignHCenter
+
+        Rectangle {
+            radius: 10
+            Layout.preferredWidth: textTimer.width + 15
+            Layout.preferredHeight: textTimer.height + 15
+            color: JamiTheme.mapButtonsOverlayColor
+            visible: textTimer.remainingTimeMs === 0
+                     ? false
+                     : isUnpin
+                       ? isSharing
+                       : isSharingToCurrentConversation
+
+            Text {
+                id: textTimer
+
+                anchors.centerIn: parent
+                color: JamiTheme.mapButtonColor
+                text: standartCountdown(Math.floor(remainingTimeMs / 1000))
+
+                function standartCountdown(seconds) {
+                    var minutes = Math.floor(seconds / 60);
+                    var hour = Math.floor(minutes / 60)
+                    minutes = minutes % 60
+                    var sec = seconds % 60
+                    if (hour) {
+                        if (minutes)
+                            return qsTr("%1h%2min").arg(hour).arg(minutes)
+                        else
+                            return qsTr("%1h").arg(hour)
+                    }
+                    if (minutes) {
+                        if (sec)
+                            return qsTr("%1m%2sec").arg(minutes).arg(sec)
+                        else
+                            return qsTr("%1m").arg(minutes)
+
+                    }
+                    return qsTr("%1sec").arg(sec)
+                }
+
+                property int remainingTimeMs: 0
+                Connections {
+                    target: PositionManager
+                    function onSendCountdownUpdate(accountId, remainingTime) {
+                        if (accountId === attachedAccountId) {
+                            textTimer.remainingTimeMs = remainingTime
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    RowLayout {
+        id: sharePositionLayout
+
+        Layout.alignment: Qt.AlignHCenter
+
+        MaterialButton {
+            id: sharePositionButton
+
+            preferredWidth: text.contentWidth
+            textLeftPadding: JamiTheme.buttontextPadding
+            textRightPadding: JamiTheme.buttontextPadding
+            primary: true
+            visible: !isSharingToCurrentConversation && !isUnpin
+            text: JamiStrings.shareLocation
+            color: isError
+                   ? JamiTheme.buttonTintedGreyInactive
+                   : JamiTheme.buttonTintedBlue
+            hoveredColor: isError
+                          ? JamiTheme.buttonTintedGreyInactive
+                          : JamiTheme.buttonTintedBlueHovered
+            pressedColor: isError
+                          ? JamiTheme.buttonTintedGreyInactive
+                          : JamiTheme.buttonTintedBluePressed
+            Layout.alignment: Qt.AlignHCenter
+            property bool isHovered: false
+            property string positioningError: "default"
+            property bool isError: positioningError.length
+            property int positionShareConvIdsCount: PositionManager.positionShareConvIdsCount
+            property string currentConvId: CurrentConversation.id
+            property bool isMapUnpin: isUnpin
+
+            function errorString(posError) {
+                if (posError === "locationServicesError")
+                    return JamiStrings.locationServicesError
+                return JamiStrings.locationServicesClosedError
+            }
+
+            onPositionShareConvIdsCountChanged: {
+                isSharingToCurrentConversation = PositionManager.isPositionSharedToConv(attachedAccountId, currentConvId)
+            }
+
+            onCurrentConvIdChanged: {
+                isSharingToCurrentConversation = PositionManager.isPositionSharedToConv(attachedAccountId, currentConvId)
+            }
+
+            onIsMapUnpinChanged: {
+                isSharingToCurrentConversation = PositionManager.isPositionSharedToConv(attachedAccountId, currentConvId)
+            }
+
+            onClicked: {
+                var sharingDuration = 60 * 1000 * UtilsAdapter.getAppValue(Settings.PositionShareDuration)
+                if (!isError && !isUnpin) {
+                    PositionManager.sharePosition(sharingDuration, attachedAccountId, currentConvId);
+                }
+                webView.runJavaScript("zoomTolayersExtent()" );
+            }
+
+            MaterialToolTip {
+                property bool isSharingPossible: !(sharePositionButton.isError && (sharePositionButton.positioningError !== "default"))
+
+                visible: sharePositionButton.hovered
+                text: isSharingPossible
+                      ? JamiStrings.shareLocationToolTip.arg(PositionManager.getmapTitle(attachedAccountId, currentConvId))
+                      : sharePositionButton.errorString(sharePositionButton.positioningError)
+            }
+            Connections {
+                target: PositionManager
+                function onPositioningError (err) {
+                    sharePositionButton.positioningError = err;
+                }
+            }
+        }
+
+        MaterialButton {
+            id: stopSharingPositionButton
+
+            preferredWidth: text.contentWidth
+            textLeftPadding: JamiTheme.buttontextPadding
+            textRightPadding: JamiTheme.buttontextPadding
+            primary: true
+            visible: isSharing
+            text: stopAllSharing
+                  ? JamiStrings.shortStopAllSharings
+                  : JamiStrings.stopSharingLocation
+            color: isError
+                   ? JamiTheme.buttonTintedGreyInactive
+                   : JamiTheme.buttonTintedRed
+            hoveredColor: isError
+                          ? JamiTheme.buttonTintedGreyInactive
+                          : JamiTheme.buttonTintedRedHovered
+            pressedColor: isError
+                          ? JamiTheme.buttonTintedGreyInactive
+                          :  JamiTheme.buttonTintedRedPressed
+            Layout.alignment: Qt.AlignHCenter
+            toolTipText: stopAllSharing
+                         ? isUnpin
+                           ? JamiStrings.unpinStopSharingTooltip
+                           : JamiStrings.stopAllSharings
+            : JamiStrings.stopSharingSeveralConversationTooltip
+            property bool isHovered: false
+            property string positioningError
+            property bool isError: positioningError.length
+            property bool stopAllSharing: !(PositionManager.positionShareConvIdsCount >= 2 && !isUnpin && isSharingToCurrentConversation)
+            onClicked: {
+                if (!isError) {
+                    if (stopAllSharing) {
+                        PositionManager.stopSharingPosition();
+                    } else {
+                        stopSharingPositionPopup.open()
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/app/webengine/map/StopSharingPositionPopup.qml b/src/app/webengine/map/StopSharingPositionPopup.qml
index b59f08866..e1fe2c40b 100644
--- a/src/app/webengine/map/StopSharingPositionPopup.qml
+++ b/src/app/webengine/map/StopSharingPositionPopup.qml
@@ -19,12 +19,11 @@
 import QtQuick
 import QtQuick.Layouts
 import QtQuick.Controls
+import Qt5Compat.GraphicalEffects
 
 import net.jami.Models 1.1
 import net.jami.Adapters 1.1
 import net.jami.Constants 1.1
-import Qt5Compat.GraphicalEffects
-
 
 import "../../commoncomponents"
 
@@ -101,11 +100,10 @@ Popup {
                     color: JamiTheme.buttonTintedBlue
                     hoveredColor: JamiTheme.buttonTintedBlueHovered
                     pressedColor: JamiTheme.buttonTintedBluePressed
-                    text: JamiStrings.stopConvSharing
+                    text: JamiStrings.stopConvSharing.arg(PositionManager.getmapTitle(attachedAccountId, CurrentConversation.id))
 
                     onClicked: {
-                        PositionManager.stopSharingPosition(PositionManager.getSelectedConvId())
-                        shareButtonVisibility = true
+                        PositionManager.stopSharingPosition(attachedAccountId, CurrentConversation.id)
                         root.close()
                     }
                 }
@@ -123,12 +121,10 @@ Popup {
 
                     onClicked: {
                         PositionManager.stopSharingPosition()
-                        shareButtonVisibility = true
                         root.close()
                     }
                 }
             }
-
         }
     }
 
diff --git a/src/app/webengine/map/map.js b/src/app/webengine/map/map.js
index 679a269b7..7d53f293f 100644
--- a/src/app/webengine/map/map.js
+++ b/src/app/webengine/map/map.js
@@ -61,7 +61,7 @@ var proj = new ol.proj.Projection({
     extent: extent
 })
 
-function setSource (coordos, authorI,imageI) {
+function setSource (coordos, avatar) {
     var coord = ol.proj.fromLonLat(coordos)
     var pointFeature = new ol.Feature({
        geometry: new ol.geom.Point(coord),
@@ -69,16 +69,16 @@ function setSource (coordos, authorI,imageI) {
     })
 
     var preStyle = new ol.style.Icon({
-                src: "data:image/png;base64," +  imageI})
+                src: "data:image/png;base64," +  avatar})
 
-    //resize the image to 40 px
+    //resize the image to 35 px
     var image = preStyle.getImage()
     if (!image.width) {
       image.addEventListener('load', function () {
-        preStyle.setScale([40 / image.width, 40 / image.height])
+        preStyle.setScale([35 / image.width, 35 / image.height])
       })
     } else {
-      preStyle.setScale([40 / image.width, 40 / image.height])
+      preStyle.setScale([35 / image.width, 35 / image.height])
     }
 
     var  iconStyle = new ol.style.Style({
@@ -93,18 +93,24 @@ function setSource (coordos, authorI,imageI) {
     return vectorSource
 }
 
-function newPosition (coordos, authorI, image) {
-    vectorSource = setSource(coordos, authorI, image)
+function newPosition (coordos, authorUri, avatar) {
+    var layerArray = map.getLayers().getArray();
+    for (var i = 0; i < layerArray.length; i++ ){
+        if(layerArray[i].layer_type === authorUri) {
+            return
+        }
+    }
+    vectorSource = setSource(coordos, avatar)
     var iconLayer = new ol.layer.Vector({source: vectorSource})
-    iconLayer.layer_type = authorI
+    iconLayer.layer_type = authorUri
     map.addLayer(iconLayer)
 }
 
-function updatePosition (coordos, authorI) {
+function updatePosition (coordos, authorUri) {
     var coord = ol.proj.fromLonLat(coordos);
     var layerArray = map.getLayers().getArray();
     for (var i = 0; i < layerArray.length; i++ ){
-        if(layerArray[i].layer_type === authorI) {
+        if(layerArray[i].layer_type === authorUri) {
             layerArray[i].getSource().getFeatures()[0].getGeometry().setCoordinates(coord)
             return
         }
@@ -123,10 +129,10 @@ function zoomTolayersExtent() {
                              padding: [80 ,80 ,80 ,80]})
 }
 
-function removePosition (authorI) {
+function removePosition (authorUri) {
     var layerArray = map.getLayers().getArray();
     for (var i = 0; i < layerArray.length; i++ ){
-        if(layerArray[i].layer_type === authorI) {
+        if(layerArray[i].layer_type === authorUri) {
             map.removeLayer(layerArray[i])
             return
         }
diff --git a/src/libclient/typedefs.h b/src/libclient/typedefs.h
index b84c47eda..75016c78e 100644
--- a/src/libclient/typedefs.h
+++ b/src/libclient/typedefs.h
@@ -30,6 +30,7 @@
 typedef QMap<QString, QString> MapStringString;
 typedef QMap<QString, int> MapStringInt;
 typedef QMap<QString, double> MapStringDouble;
+typedef QMap<QPair<QString, QString>, bool> MapPairStrStrBool;
 typedef QVector<int> VectorInt;
 typedef QVector<uint> VectorUInt;
 typedef QVector<qulonglong> VectorULongLong;
@@ -41,6 +42,8 @@ typedef QMap<QString, QMap<QString, QStringList>> MapStringMapStringStringList;
 typedef QMap<QString, QStringList> MapStringStringList;
 typedef QVector<QByteArray> VectorVectorByte;
 typedef uint64_t DataTransferId;
+// accountId, conversationId
+typedef QPair<QString, QString> PositionKey;
 
 constexpr static const char* TRUE_STR = "true";
 constexpr static const char* TEXT_PLAIN = "text/plain";
-- 
GitLab