diff --git a/resources/icons/unpin.svg b/resources/icons/unpin.svg
new file mode 100644
index 00000000..9f812821
--- /dev/null
+++ b/resources/icons/unpin.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/app/appsettingsmanager.h b/src/app/appsettingsmanager.h
index d69a3ace..30a0573a 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 c8fe3633..51023718 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 a45d43cf..1c19ac45 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,18 +51,28 @@ Rectangle {
addMemberPanel.visible = 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});
+
+ if (sprite === null) {
+ // Error Handling
+ console.log("Error creating object");
+ }
+ }
+ }
+ Connections {
+ target: PositionManager
+
+ function onOpenNewMap() {
+ instanceMapObject()
+ }
+ }
+
color: JamiTheme.chatviewBgColor
property string currentConvId: CurrentConversation.id
- onCurrentConvIdChanged: PositionManager.setMapActive(false);
-
- Loader {
- id: mapLoader
-
- active: PositionManager.isMapActive
- z: 10
- source: WITH_WEBENGINE ? "qrc:/webengine/map/MapPosition.qml" : ""
- }
HostPopup {
id: hostPopup
diff --git a/src/app/mainview/components/ChatViewFooter.qml b/src/app/mainview/components/ChatViewFooter.qml
index a2f16197..2dca139e 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 714f6fd9..30c14c81 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 11f173b2..4888a829 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 d8a8bbf0..be011415 100644
--- a/src/app/positioning.cpp
+++ b/src/app/positioning.cpp
@@ -21,14 +21,12 @@
#include
#include
-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 9b3423c9..e2bc1172 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 513393f0..c4c23007 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
#include
#include
@@ -9,31 +10,40 @@
#include
#include
-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 {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 {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 {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 {lrcInstance_->get_currentAccountId(),
- convId});
- if (iter != positionShareConvIds_.end()) {
- positionShareConvIds_.remove(std::distance(positionShareConvIds_.begin(), iter));
- }
- set_positionShareConvIdsCount(positionShareConvIds_.size());
}
}
void
-PositionManager::setMapActive(bool state)
+PositionManager::unPinMap(QString key)
{
- set_isMapActive(state);
- Q_EMIT isMapActiveChanged();
+ 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);
+ }
+ }
+}
+
+void
+PositionManager::setMapInactive(const QString key)
+{
+ 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
@@ -304,6 +412,151 @@ PositionManager::showNotification(const QString& accountId,
#endif
}
+void
+PositionManager::onNewConversation()
+{
+ 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++;
+ }
+ }
+}
+
+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());
+}
+
+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;
+ }
+ }
+}
+
+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);
+ }
+ }
+ }
+}
+
+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 {
+ qWarning() << "error in PositionManager::updatePositionInMemory(), it.value() is null";
+ }
+ } else {
+ qWarning()
+ << "Error: A position intented to be updated while not in objectListSharingUris_ ";
+ }
+
+ // 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,
@@ -311,106 +564,34 @@ PositionManager::onPositionReceived(const QString& accountId,
const uint64_t& timestamp,
const QString& daemonId)
{
- // 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());
+ // handlers variables
- QVariantMap newPosition = parseJsonPosition(body, peerId);
- auto getShareInfo = [&](bool update) -> QVariantMap {
- QVariantMap shareInfo;
- shareInfo["author"] = peerId;
- if (!update) {
- shareInfo["avatar"] = getAvatar(accountId, peerId);
- }
- shareInfo["long"] = newPosition["long"];
- shareInfo["lat"] = newPosition["lat"];
- return shareInfo;
- };
- auto endSharing = newPosition["type"] == "Stop";
+ // parse the position from json
+ QVariantMap positionReceived = parseJsonPosition(accountId, peerId, body);
- auto key = QPair {accountId, peerId};
+ // is it a message that notify an end of position sharing
+ auto endSharing = positionReceived["type"] == "Stop";
- if (!endSharing) {
- // open map on position reception
- if (!isMapActive_ && mapAutoOpening_ && isPeerIdInConv
- && peerId != lrcInstance_->getCurrentAccountInfo().profileInfo.uri) {
- set_isMapActive(true);
- }
+ // 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);
}
- 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 the position already exists
+ if (findPeerIdinAllPeers != objectListSharingUris_.end()) {
+ if (endSharing)
+ removePositionFromMemory(key, positionReceived);
+ else
+ updatePositionInMemory(key, positionReceived);
- 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);
- }
- }
- // stop sharing position
- } else {
- auto it = objectListSharingUris_.find(key);
- if (it != objectListSharingUris_.end()) {
- it.value()->deleteLater();
- objectListSharingUris_.erase(it);
- set_sharingUrisCount(objectListSharingUris_.size());
- }
- }
} 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 {
- "", lrcInstance_->getCurrentAccountInfo().profileInfo.uri}))
- || sharingUrisCount_ == 0)) {
- set_isMapActive(false);
- }
- }
- } else {
- // Update
- if (isPeerIdInConv)
- Q_EMIT positionShareUpdated(getShareInfo(true));
- // reset watchdog
-
- auto it = objectListSharingUris_.find(key);
- if (it != objectListSharingUris_.end()) {
- it.value()->resetWatchdog();
- }
- }
+ // 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 fc8c53a3..e75af1eb 100644
--- a/src/app/positionmanager.h
+++ b/src/app/positionmanager.h
@@ -24,19 +24,21 @@
#include "positionobject.h"
#include "systemtray.h"
+#include
#include
#include
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 localPositioning_;
- QTimer* timerTimeLeftSharing_ = nullptr;
- QTimer* timerStopSharing_ = nullptr;
- QSet> currentConvSharingUris_;
- QMap, PositionObject*> objectListSharingUris_;
- QList> positionShareConvIds_;
+ QMap mapTimerCountDown_;
+ QTimer* countdownTimer_ = nullptr;
+ // map of all shared position by peers
+ QMap objectListSharingUris_;
+ // list of all the peers the user is sharing position to
+ QList positionShareConvIds_;
+ QMutex mapStatusMutex_;
+ AppSettingsManager* settingsManager_;
};
diff --git a/src/app/positionobject.cpp b/src/app/positionobject.cpp
index e7a97052..a9e8b045 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 f2edc2ca..94e60f07 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 2359c801..d42c8cc5 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 82f7a93a..e0c50a2e 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 00000000..a21009b2
--- /dev/null
+++ b/src/app/settingsview/components/LocationSharingSettings.qml
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ * Author: Nicolas Vengeon
+ *
+ * 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 .
+ */
+
+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 17d9f723..6f5ea5b0 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 .
*/
+
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
+Item {
+ id: root
- x: xPos
- y: yPos
- width: isFullScreen ? root.width : windowSize
- height: isMinimised
- ? buttonOverlay.height + buttonsChoseSharing.height + 30
- : isFullScreen ? root.height - yPos : windowSize
+ 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
- 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
+ function closeMapPosition() {
+ root.destroy()
+ }
- WebEngineView {
- id: webView
+ Connections {
+ target: PositionManager
- 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
- }
-
- var scriptOlJs = {
- sourceUrl: Qt.resolvedUrl(olJs),
- injectionPoint: WebEngineScript.DocumentReady,
- worldId: WebEngineScript.MainWorld
- }
-
- 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 onPositionShareUpdated(shareInfo) {
- if(webView.isLoaded) {
- var curLong = shareInfo.long
- var curLat = shareInfo.lat
- webView.runJavaScript("updatePosition([" + curLong + "," + curLat + "], '" + shareInfo.author + "' )" );
- }
- }
-
- 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
-
- 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
- }
- }
-
- 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;
- }
- }
-
- 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)
-
- Layout.alignment: Qt.AlignHCenter
-
- property int remainingTimeMs: Math.ceil(PositionManager.timeSharingRemaining / 1000 / 60)
- }
- }
+ width: mapObject.width
+ height: mapObject.height
}
- RowLayout {
- id: sharePositionLayout
- Layout.alignment: Qt.AlignHCenter
+ onClosing: {
+ if (isUnpin) {
+ PositionManager.setMapInactive(attachedAccountId)
+ }
+ }
+ }
- MaterialButton {
- id: sharePositionButton
+ Item {
+ id: parentPin
- 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
+ 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
- onClicked: {
- if (!isError) {
- if( buttonsChoseSharing.shortSharing)
- PositionManager.sharePosition(10 * 60 * 1000);
- else
- PositionManager.sharePosition(60 * 60 * 1000);
- visible = false
+ WebEngineView {
+ id: webView
+
+ layer.enabled: !isFullScreen
+ layer.effect: OpacityMask {
+ maskSource:
+ Rectangle {
+ width: webView.width
+ height: webView.height
+ radius: 10
}
}
- onHoveredChanged: {
- isHovered = !isHovered
- }
+ width: parent.width
+ height: parent.height
- MaterialToolTip {
- visible: sharePositionButton.isHovered
- && sharePositionButton.isError && (sharePositionButton.positioningError !== "default")
- x: 0
- y: 0
- text: sharePositionButton.errorString(sharePositionButton.positioningError)
+ 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;
+
+ function loadScripts () {
+ var scriptMapJs = {
+ sourceUrl: Qt.resolvedUrl(mapJs),
+ injectionPoint: WebEngineScript.DocumentReady,
+ worldId: WebEngineScript.MainWorld
+ }
+
+ var scriptOlJs = {
+ sourceUrl: Qt.resolvedUrl(olJs),
+ injectionPoint: WebEngineScript.DocumentReady,
+ worldId: WebEngineScript.MainWorld
+ }
+
+ 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()" );
+ }
+ }
+ }
+
+ 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 + "' )" );
+ }
+ }
+ }
+
+ function onPositionShareRemoved(author, accountId) {
+ if(webView.isLoaded) {
+ if (accountId === attachedAccountId) {
+ webView.runJavaScript("removePosition( '" + author + "' )" );
+ 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()" );
+ Component.onCompleted: {
+ loadHtml(UtilsAdapter.qStringFromFile(mapHtml), mapHtml)
+ loadScripts()
}
- }
- 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
- }
- }
-
- 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;
- }
- }
-
- 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 00000000..8383addc
--- /dev/null
+++ b/src/app/webengine/map/MapPositionOverlay.qml
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ * Author: Nicolas Vengeon
+ *
+ * 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 .
+ */
+
+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 00000000..95e20a3a
--- /dev/null
+++ b/src/app/webengine/map/MapPositionSharingControl.qml
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ * Author: Nicolas Vengeon
+ *
+ * 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 .
+ */
+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 b59f0886..e1fe2c40 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 679a269b..7d53f293 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 b84c47ed..75016c78 100644
--- a/src/libclient/typedefs.h
+++ b/src/libclient/typedefs.h
@@ -30,6 +30,7 @@
typedef QMap MapStringString;
typedef QMap MapStringInt;
typedef QMap MapStringDouble;
+typedef QMap, bool> MapPairStrStrBool;
typedef QVector VectorInt;
typedef QVector VectorUInt;
typedef QVector VectorULongLong;
@@ -41,6 +42,8 @@ typedef QMap> MapStringMapStringStringList;
typedef QMap MapStringStringList;
typedef QVector VectorVectorByte;
typedef uint64_t DataTransferId;
+// accountId, conversationId
+typedef QPair PositionKey;
constexpr static const char* TRUE_STR = "true";
constexpr static const char* TEXT_PLAIN = "text/plain";