diff --git a/CMakeLists.txt b/CMakeLists.txt index f98bc2a601e512decbbe93904e6bb9b33388bb42..4160a8f41c2c40eda0402f02458e144b42517453 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -178,6 +178,7 @@ set(COMMON_SOURCES ${APP_SRC_DIR}/utils.cpp ${APP_SRC_DIR}/mainapplication.cpp ${APP_SRC_DIR}/messagesadapter.cpp + ${APP_SRC_DIR}/positionobject.cpp ${APP_SRC_DIR}/positionmanager.cpp ${APP_SRC_DIR}/accountadapter.cpp ${APP_SRC_DIR}/calladapter.cpp @@ -233,6 +234,7 @@ set(COMMON_HEADERS ${APP_SRC_DIR}/mainapplication.h ${APP_SRC_DIR}/qrimageprovider.h ${APP_SRC_DIR}/messagesadapter.h + ${APP_SRC_DIR}/positionobject.h ${APP_SRC_DIR}/positionmanager.h ${APP_SRC_DIR}/accountadapter.h ${APP_SRC_DIR}/calladapter.h diff --git a/src/app/commoncomponents/Avatar.qml b/src/app/commoncomponents/Avatar.qml index f9399aa39ccc20d014b198bf8985d40291fb3f79..4090a6562d75e8430ca8fa978bb3065d584bf933 100644 --- a/src/app/commoncomponents/Avatar.qml +++ b/src/app/commoncomponents/Avatar.qml @@ -19,6 +19,8 @@ import QtQuick import QtQuick.Controls +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects import net.jami.Adapters 1.1 import net.jami.Constants 1.1 @@ -45,6 +47,8 @@ Item { property alias presenceStatus: presenceIndicator.status property bool showPresenceIndicator: true + property bool showSharePositionIndicator: false + property bool showSharedPositionIndicator: false property alias fillMode: image.fillMode onImageIdChanged: image.updateSource() @@ -116,4 +120,48 @@ Item { visible: showPresenceIndicator } + RowLayout { + id: positionIndicatorLayout + anchors.left: root.left + anchors.leftMargin: -1 + anchors.bottom: root.bottom + anchors.bottomMargin: -1 + spacing: 0 + property real sizeI: root.width * JamiTheme.avatarPresenceRatio + Rectangle { + id: sharePositionIndicator + visible: showSharePositionIndicator + Layout.preferredWidth: parent.sizeI + Layout.preferredHeight: parent.sizeI + color: JamiTheme.backgroundColor + radius: parent.sizeI * 0.5 + Image { + anchors.fill: parent + ColorOverlay { + anchors.fill: parent + source: parent + color: JamiTheme.sharePositionIndicatorColor + } + source: JamiResources.my_location_svg + } + } + Rectangle { + id: sharedPositionIndicator + visible: showSharedPositionIndicator + Layout.preferredWidth: parent.sizeI + Layout.preferredHeight: parent.sizeI + color: JamiTheme.backgroundColor + radius: parent.sizeI * 0.5 + Image { + anchors.fill: parent + ColorOverlay { + anchors.fill: parent + source: parent + color: JamiTheme.sharedPositionIndicatorColor + } + source: JamiResources.my_location_svg + } + } + } + } diff --git a/src/app/constant/JamiStrings.qml b/src/app/constant/JamiStrings.qml index ebc1248cf4f7cc78d892bd60eaaf3ff69ab46822..f24e472d06c8de74948f24ccec7b692fe2493ff1 100644 --- a/src/app/constant/JamiStrings.qml +++ b/src/app/constant/JamiStrings.qml @@ -299,15 +299,25 @@ Item { property string raiseHand: qsTr("Raise hand") property string layoutSettings: qsTr("Layout settings") - // Share location + // Share location/position property string shareLocation: qsTr("Share location") - property string stopSharingLocation: qsTr("Stop sharing location") + property string stopSharingLocation: qsTr("Stop sharing") property string shortSharing: qsTr("10 minutes") property string longSharing: qsTr("One hour") property string minutesLeft: qsTr("%1 minutes left") property string minuteLeft: qsTr("%1 minute left") - property string locationServicesError: qsTr("Jami needs to access to your location.\nIn Device Settings, please turn on Location Services.\nOther participants' location can still be received.") - property string locationServicesClosedError: qsTr("Please check your Internet connection.") + 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 stopSharingPopupBody: qsTr("Location is shared in several conversations"); + property string minimizeMapTooltip: qsTr("Minimize"); + property string maximizeMapTooltip: qsTr("Maximize"); + property string reduceMapTooltip: qsTr("Reduce"); + property string extendMapTooltip: qsTr("Extend"); + property string dragMapTooltip: qsTr("Drag"); + property string centerMapTooltip: qsTr("Center"); + property string closeMapTooltip: qsTr("Close"); // Chatview header property string hideChat: qsTr("Hide chat") diff --git a/src/app/constant/JamiTheme.qml b/src/app/constant/JamiTheme.qml index 613e81c04b91e4d3735948ae62107d635d10bda1..fb0c94944a55cfe0c90c1238bae9d090787a5475 100644 --- a/src/app/constant/JamiTheme.qml +++ b/src/app/constant/JamiTheme.qml @@ -213,6 +213,8 @@ Item { //mapPosition property color mapButtonsOverlayColor: darkTheme ? "#000000" : "#f0f0f0" property color mapButtonColor: darkTheme ? "#f0f0f0" : "#000000" + property color sharePositionIndicatorColor: red_ + property color sharedPositionIndicatorColor: urgentOrange_ // Files To Send Container property color removeFileButtonColor: Qt.rgba(96, 95, 97, 0.5) @@ -318,7 +320,7 @@ Item { property real preferredDialogWidth: 400 property real preferredDialogHeight: 300 property real minimumPreviewWidth: 120 - property real minimumMapWidth: 230 + property real minimumMapWidth: 250 property real pluginHandlersPopupViewHeight: 200 property real pluginHandlersPopupViewDelegateHeight: 50 property real secondaryDialogDimension: 500 diff --git a/src/app/mainview/components/ConversationAvatar.qml b/src/app/mainview/components/ConversationAvatar.qml index 8b20f775a34a0aac4317895591ec96820f73adeb..28d5f831269fc38b15c37d9364f2ef44dfefa0c9 100644 --- a/src/app/mainview/components/ConversationAvatar.qml +++ b/src/app/mainview/components/ConversationAvatar.qml @@ -28,6 +28,8 @@ Item { property alias imageId: avatar.imageId property alias showPresenceIndicator: avatar.showPresenceIndicator + property alias showSharePositionIndicator: avatar.showSharePositionIndicator + property alias showSharedPositionIndicator: avatar.showSharedPositionIndicator property alias animationMode: animation.mode SpinningAnimation { diff --git a/src/app/mainview/components/SmartListItemDelegate.qml b/src/app/mainview/components/SmartListItemDelegate.qml index 6b6f9e32c891d3222db835ed45fd8a1f403dd553..5d56c4db9d6fff10c789c352ffda9eb0d17943cf 100644 --- a/src/app/mainview/components/SmartListItemDelegate.qml +++ b/src/app/mainview/components/SmartListItemDelegate.qml @@ -68,10 +68,22 @@ ItemDelegate { imageId: UID showPresenceIndicator: Presence !== undefined ? Presence : false + showSharePositionIndicator: PositionManager.isPositionSharedToConv(UID) + showSharedPositionIndicator: PositionManager.isConvSharingPosition(UID) Layout.preferredWidth: JamiTheme.smartListAvatarSize Layout.preferredHeight: JamiTheme.smartListAvatarSize + Connections { + target: PositionManager + function onPositionShareConvIdsChanged () { + avatar.showSharePositionIndicator = PositionManager.isPositionSharedToConv(UID) + } + function onSharingUrisChanged () { + avatar.showSharedPositionIndicator = PositionManager.isConvSharingPosition(UID) + } + } + Rectangle { id: overlayHighlighted visible: highlighted && !interactive diff --git a/src/app/positioning.cpp b/src/app/positioning.cpp index e1f8ebc3512acaa37df340ae0d7967285b67299e..27d79953ea73ba5957932bcfbe10967173ebedb9 100644 --- a/src/app/positioning.cpp +++ b/src/app/positioning.cpp @@ -28,7 +28,7 @@ Positioning::Positioning(QString uri, QObject* parent) source_ = QGeoPositionInfoSource::createDefaultSource(this); QTimer* timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &Positioning::requestPosition); - timer->start(2000); + timer->start(5000); connect(source_, &QGeoPositionInfoSource::errorOccurred, this, &Positioning::slotError); connect(source_, &QGeoPositionInfoSource::positionUpdated, this, &Positioning::positionUpdated); // if location services are activated, positioning will be activated automatically @@ -38,11 +38,6 @@ Positioning::Positioning(QString uri, QObject* parent) &Positioning::locationServicesActivated); } -Positioning::~Positioning() -{ - sendStopSharingMsg(); -} - void Positioning::start() { @@ -60,16 +55,6 @@ Positioning::stop() isPositioning = false; } -void -Positioning::sendStopSharingMsg() -{ - QJsonObject jsonObj; - jsonObj.insert("type", QJsonValue("Stop")); - QJsonDocument doc(jsonObj); - QString strJson(doc.toJson(QJsonDocument::Compact)); - Q_EMIT newPosition(uri_, strJson, -1, ""); -} - QString Positioning::convertToJson(const QGeoPositionInfo& info) { diff --git a/src/app/positioning.h b/src/app/positioning.h index ae59b2edad669b44c683ada2f46fecbb73fd479c..d75e98d567feb2981af8a582dae1d1994a7152f2 100644 --- a/src/app/positioning.h +++ b/src/app/positioning.h @@ -28,8 +28,6 @@ class Positioning : public QObject public: Positioning(QString uri, QObject* parent = 0); - ~Positioning(); - /** * start to retreive the current position */ @@ -42,7 +40,6 @@ public: * send a stop signal to other peers to tell them * you stoped sharing yout position */ - void sendStopSharingMsg(); QString convertToJson(const QGeoPositionInfo& info); void setUri(QString uri); diff --git a/src/app/positionmanager.cpp b/src/app/positionmanager.cpp index 119b68be1e2829f85f5e84a8bd5adfa298cd3947..b21d4fa4df9036bdd5390eab00a7dbec015e92b1 100644 --- a/src/app/positionmanager.cpp +++ b/src/app/positionmanager.cpp @@ -19,8 +19,10 @@ PositionManager::PositionManager(LRCInstance* instance, QObject* parent) }); connect(timerStopSharing_, &QTimer::timeout, [=] { stopSharingPosition(); }); connect(lrcInstance_, &LRCInstance::selectedConvUidChanged, [this]() { + Q_EMIT positionShareConvIdsChanged(); set_mapAutoOpening(true); }); + set_isMapActive(false); } void @@ -28,8 +30,14 @@ PositionManager::safeInit() { connect(lrcInstance_, &LRCInstance::currentAccountIdChanged, [this]() { connectConversationModel(); + set_sharingUris({}); + objectListSharingUris_.clear(); + set_positionShareConvIds({}); localPositioning_->setUri(lrcInstance_->getCurrentAccountInfo().profileInfo.uri); }); + set_sharingUris({}); + objectListSharingUris_.clear(); + set_positionShareConvIds({}); localPositioning_.reset(new Positioning(lrcInstance_->getCurrentAccountInfo().profileInfo.uri)); connectConversationModel(); } @@ -49,8 +57,7 @@ PositionManager::connectConversationModel() void PositionManager::startPositioning() { - sharingUris_.clear(); - + currentConvSharingUris_.clear(); localPositioning_->start(); connect(localPositioning_.get(), &Positioning::newPosition, @@ -70,8 +77,54 @@ PositionManager::stopPositioning() localPositioning_->stop(); } +QString +PositionManager::getSelectedConvId() +{ + return lrcInstance_->get_selectedConvUid(); +} + +bool +PositionManager::isConvSharingPosition(const QString& convUri) +{ + const auto& convParticipants = lrcInstance_->getConversationFromConvUid(convUri) + .participantsUris(); + Q_FOREACH (const auto& id, convParticipants) { + if (id != lrcInstance_->getCurrentAccountInfo().profileInfo.uri) { + if (sharingUris_.contains(id)) { + return true; + } + } + } + return false; +} + +void +PositionManager::loadPreviousLocations() +{ + 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(), strJson, -1, ""); + } +} + +bool +PositionManager::isPositionSharedToConv(const QString& convUri) +{ + if (positionShareConvIds_.length()) { + auto iter = std::find(positionShareConvIds_.begin(), positionShareConvIds_.end(), convUri); + return (iter != positionShareConvIds_.end()); + } + return false; +} + void -PositionManager::onOwnPositionReceived(const QString& peerId, const QString& body) +PositionManager::sendPosition(const QString& peerId, const QString& body) { try { Q_FOREACH (const auto& id, positionShareConvIds_) { @@ -89,13 +142,26 @@ PositionManager::onOwnPositionReceived(const QString& peerId, const QString& bod } } +void +PositionManager::onWatchdogTimeout() +{ + QObject* obj = sender(); + auto it = std::find_if(objectListSharingUris_.cbegin(), + objectListSharingUris_.cend(), + [obj](const auto& it) { return it == obj; }); + if (it != objectListSharingUris_.cend()) { + QString stopMsg("{\"type\":\"Stop\"}"); + onPositionReceived(it.key(), stopMsg, -1, ""); + } +} + void PositionManager::sharePosition(int maximumTime) { connect(localPositioning_.get(), &Positioning::newPosition, this, - &PositionManager::onOwnPositionReceived, + &PositionManager::sendPosition, Qt::UniqueConnection); try { @@ -109,11 +175,29 @@ PositionManager::sharePosition(int maximumTime) } void -PositionManager::stopSharingPosition() +PositionManager::stopSharingPosition(const QString convId) { - localPositioning_->sendStopSharingMsg(); - stopPositionTimers(); - set_positionShareConvIds({}); + QString stopMsg; + stopMsg = "{\"type\":\"Stop\"}"; + if (convId == "") { + sendPosition(lrcInstance_->getCurrentAccountInfo().profileInfo.uri, stopMsg); + stopPositionTimers(); + set_positionShareConvIds({}); + } else { + const auto& convInfo = lrcInstance_->getConversationFromConvUid(convId); + Q_FOREACH (const QString& uri, convInfo.participantsUris()) { + if (lrcInstance_->getCurrentAccountInfo().profileInfo.uri != uri) { + lrcInstance_->getCurrentAccountInfo().contactModel->sendDhtMessage(uri, + stopMsg, + APPLICATION_GEO); + } + } + auto iter = std::find(positionShareConvIds_.begin(), positionShareConvIds_.end(), convId); + if (iter != positionShareConvIds_.end()) { + positionShareConvIds_.remove(std::distance(positionShareConvIds_.begin(), iter)); + } + Q_EMIT positionShareConvIdsChanged(); + } } void @@ -197,10 +281,9 @@ PositionManager::onPositionReceived(const QString& peerId, ->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()); - if (!isPeerIdInConv) - return; QVariantMap newPosition = parseJsonPosition(body, peerId); auto getShareInfo = [&](bool update) -> QVariantMap { @@ -217,36 +300,79 @@ PositionManager::onPositionReceived(const QString& peerId, if (!endSharing) { // open map on position reception - if (!isMapActive_ && mapAutoOpening_ + if (!isMapActive_ && mapAutoOpening_ && isPeerIdInConv && peerId != lrcInstance_->getCurrentAccountInfo().profileInfo.uri) { set_isMapActive(true); } } - auto iter = std::find(sharingUris_.begin(), sharingUris_.end(), peerId); - if (iter == sharingUris_.end()) { + auto iter = std::find(currentConvSharingUris_.begin(), currentConvSharingUris_.end(), peerId); + if (iter == currentConvSharingUris_.end()) { // New share if (!endSharing) { sharingUris_.insert(peerId); - Q_EMIT positionShareAdded(getShareInfo(false)); - } + Q_EMIT sharingUrisChanged(); + // list to save more information on position + watchdog + auto it = objectListSharingUris_.find(peerId); + if (it == objectListSharingUris_.end()) { + auto obj = new PositionObject(newPosition["lat"], newPosition["long"], this); + + objectListSharingUris_.insert(peerId, obj); + connect(obj, + &PositionObject::timeout, + this, + &PositionManager::onWatchdogTimeout, + Qt::DirectConnection); + } + + if (isPeerIdInConv) { + currentConvSharingUris_.insert(peerId); + Q_EMIT positionShareAdded(getShareInfo(false)); + } + // stop sharing position + } else { + sharingUris_.remove(peerId); + Q_EMIT sharingUrisChanged(); + auto it = objectListSharingUris_.find(peerId); + if (it != objectListSharingUris_.end()) { + it.value()->deleteLater(); + objectListSharingUris_.erase(it); + } + } } else { // Update/remove existing if (endSharing) { - // Remove (avoid self) - if (peerId != lrcInstance_->getCurrentAccountInfo().profileInfo.uri) { - sharingUris_.remove(peerId); + // Remove + + sharingUris_.remove(peerId); + Q_EMIT sharingUrisChanged(); + auto it = objectListSharingUris_.find(peerId); + if (it != objectListSharingUris_.end()) { + it.value()->deleteLater(); + objectListSharingUris_.erase(it); + } + if (isPeerIdInConv) { + currentConvSharingUris_.remove(peerId); Q_EMIT positionShareRemoved(peerId); - // close the map if you're not sharing and the only remaining position is yours - if (!positionShareConvIds_.length() && sharingUris_.size() == 1 - && sharingUris_.contains( - lrcInstance_->getCurrentAccountInfo().profileInfo.uri)) { + // close the map if you're not sharing and you don't receive position anymore + if (!positionShareConvIds_.length() + && ((sharingUris_.size() == 1 + && sharingUris_.contains( + lrcInstance_->getCurrentAccountInfo().profileInfo.uri)) + || sharingUris_.size() == 0)) { set_isMapActive(false); } } } else { // Update - Q_EMIT positionShareUpdated(getShareInfo(true)); + if (isPeerIdInConv) + Q_EMIT positionShareUpdated(getShareInfo(true)); + // reset watchdog + + auto it = objectListSharingUris_.find(peerId); + if (it != objectListSharingUris_.end()) { + it.value()->resetWatchdog(); + } } } } diff --git a/src/app/positionmanager.h b/src/app/positionmanager.h index 74148a317520b122dbcaadfb32f0e78c1e867e61..0111a4d9da92ca603502a068ff60439c663f3a78 100644 --- a/src/app/positionmanager.h +++ b/src/app/positionmanager.h @@ -21,6 +21,7 @@ #include "lrcinstance.h" #include "qmladapterbase.h" #include "positioning.h" +#include "positionobject.h" #include <QObject> #include <QString> @@ -31,6 +32,7 @@ class PositionManager : public QmlAdapterBase QML_RO_PROPERTY(bool, isMapActive) QML_RO_PROPERTY(int, timeSharingRemaining) QML_PROPERTY(QList<QString>, positionShareConvIds) + QML_PROPERTY(QSet<QString>, sharingUris) QML_PROPERTY(bool, mapAutoOpening) public: explicit PositionManager(LRCInstance* instance, QObject* parent = nullptr); @@ -54,9 +56,16 @@ protected: Q_INVOKABLE void connectConversationModel(); Q_INVOKABLE void setMapActive(bool state); Q_INVOKABLE void sharePosition(int maximumTime); + Q_INVOKABLE void stopSharingPosition(const QString convId = ""); + Q_INVOKABLE void startPositioning(); Q_INVOKABLE void stopPositioning(); - Q_INVOKABLE void stopSharingPosition(); + + Q_INVOKABLE QString getSelectedConvId(); + Q_INVOKABLE bool isPositionSharedToConv(const QString& convUri); + Q_INVOKABLE bool isConvSharingPosition(const QString& convUri); + + Q_INVOKABLE void loadPreviousLocations(); private Q_SLOTS: void onPositionErrorReceived(const QString error); @@ -64,11 +73,13 @@ private Q_SLOTS: const QString& body, const uint64_t& timestamp, const QString& daemonId); - void onOwnPositionReceived(const QString& peerId, const QString& body); + void sendPosition(const QString& peerId, const QString& body); + void onWatchdogTimeout(); private: std::unique_ptr<Positioning> localPositioning_; QTimer* timerTimeLeftSharing_ = nullptr; QTimer* timerStopSharing_ = nullptr; - QSet<QString> sharingUris_; + QSet<QString> currentConvSharingUris_; + QMap<QString, PositionObject*> objectListSharingUris_; }; diff --git a/src/app/positionobject.cpp b/src/app/positionobject.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e7a970526e01b8fbc6ef19aef6b3abd05d1b6176 --- /dev/null +++ b/src/app/positionobject.cpp @@ -0,0 +1,30 @@ +#include "positionobject.h" + +PositionObject::PositionObject(QVariant latitude, QVariant longitude, QObject* parent) + : QObject(parent) + , resetTime(20000) + , longitude_(longitude) + , latitude_(latitude) + +{ + watchdog_ = new QTimer(this); + watchdog_->start(resetTime); + connect(watchdog_, &QTimer::timeout, this, &PositionObject::timeout); +} + +void +PositionObject::resetWatchdog() +{ + watchdog_->start(resetTime); +} + +QVariant +PositionObject::getLongitude() +{ + return longitude_; +} +QVariant +PositionObject::getLatitude() +{ + return latitude_; +} diff --git a/src/app/positionobject.h b/src/app/positionobject.h new file mode 100644 index 0000000000000000000000000000000000000000..f2edc2ca8bb822de4435a42851b3497a5107f7f3 --- /dev/null +++ b/src/app/positionobject.h @@ -0,0 +1,28 @@ +#pragma once + +#include "qvariant.h" +#include <QObject> +#include <QString> +#include <QTimer> +#include <QDebug> + +class PositionObject : public QObject +{ + Q_OBJECT + +public: + PositionObject(QVariant latitude, QVariant longitude, QObject* parent = nullptr); + + Q_SIGNAL void timeout(); + + void resetWatchdog(); + + QVariant getLongitude(); + QVariant getLatitude(); + +private: + QVariant latitude_; + QVariant longitude_; + int resetTime; + QTimer* watchdog_; +}; diff --git a/src/app/webengine/map/MapPosition.qml b/src/app/webengine/map/MapPosition.qml index e8f7f28cd5e94f294e8cac1bc0af051876e27883..1da46a4e27cc92cb2b6dc3308b9c4f1262153f00 100644 --- a/src/app/webengine/map/MapPosition.qml +++ b/src/app/webengine/map/MapPosition.qml @@ -106,10 +106,6 @@ Rectangle { } } } - Component.onDestruction: { - PositionManager.stopSharingPosition(); - PositionManager.stopPositioning(); - } Component.onCompleted: { loadHtml(UtilsAdapter.qStringFromFile(mapHtml), mapHtml) @@ -122,6 +118,8 @@ Rectangle { webView.isLoaded = true runJavaScript("setMapView([" + 0 + ","+ 0 + "], " + 1 + " );" ); PositionManager.startPositioning() + //load locations that were received before this conversation was opened + PositionManager.loadPreviousLocations(); } } } @@ -152,7 +150,6 @@ Rectangle { onClicked: { buttonsChoseSharing.shortSharing = true } - } MaterialButton { @@ -195,69 +192,108 @@ Rectangle { } } - MaterialButton { - id: sharePositionButton - - preferredWidth: text.contentWidth - textLeftPadding: JamiTheme.buttontextPadding - textRightPadding: JamiTheme.buttontextPadding - primary: true - text: webView.isSharing ? JamiStrings.stopSharingLocation : JamiStrings.shareLocation - color: isError - ? JamiTheme.buttonTintedGreyInactive - : webView.isSharing ? JamiTheme.buttonTintedRed : JamiTheme.buttonTintedBlue - hoveredColor: isError - ? JamiTheme.buttonTintedGreyInactive - : webView.isSharing ? JamiTheme.buttonTintedRedHovered : JamiTheme.buttonTintedBlueHovered - pressedColor: isError - ? JamiTheme.buttonTintedGreyInactive - : webView.isSharing ? JamiTheme.buttonTintedRedPressed: JamiTheme.buttonTintedBluePressed + RowLayout { + id: sharePositionLayout Layout.alignment: Qt.AlignHCenter - property bool isHovered: false - property string positioningError: "default" - property bool isError: positioningError.length + MaterialButton { + id: sharePositionButton - function errorString(posError) { - if (posError === "locationServicesError") - return JamiStrings.locationServicesError - return JamiStrings.locationServicesClosedError - } + 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 (webView.isSharing) { - PositionManager.stopSharingPosition(); - } else { - if (buttonsChoseSharing.shortSharing) + onClicked: { + if (!isError) { + if( buttonsChoseSharing.shortSharing) PositionManager.sharePosition(10 * 60 * 1000); else PositionManager.sharePosition(60 * 60 * 1000); + visible = false } } - } - onHoveredChanged: { - isHovered = !isHovered - } + onHoveredChanged: { + isHovered = !isHovered + } - MaterialToolTip { - visible: sharePositionButton.isHovered - && sharePositionButton.isError && (sharePositionButton.positioningError !== "default") - x: 0 - y: 0 - text: sharePositionButton.errorString(sharePositionButton.positioningError) + MaterialToolTip { + visible: sharePositionButton.isHovered + && sharePositionButton.isError && (sharePositionButton.positioningError !== "default") + x: 0 + y: 0 + text: sharePositionButton.errorString(sharePositionButton.positioningError) + } + Connections { + target: PositionManager + function onPositioningError (err) { + sharePositionButton.positioningError = err; + } + } } + MaterialButton { + id: stopSharingPositionButton - Connections { - target: PositionManager - function onPositioningError (err) { - sharePositionButton.positioningError = err; + 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.positionShareConvIds.length >= 2) { + stopSharingPositionPopup.open() + } else { + PositionManager.stopSharingPosition(); + sharePositionButton.visible = true + } + } } } } } + StopSharingPositionPopup { + id: stopSharingPositionPopup + + property alias shareButtonVisibility: sharePositionButton.visible + } + Rectangle { id: buttonOverlay @@ -277,6 +313,7 @@ Rectangle { PushButton { id: btnCenter + toolTipText: JamiStrings.centerMapTooltip imageColor: JamiTheme.mapButtonColor normalColor: JamiTheme.transparentColor source: JamiResources.share_location_svg @@ -288,6 +325,7 @@ Rectangle { PushButton { id: btnMove + toolTipText: JamiStrings.dragMapTooltip imageColor: JamiTheme.mapButtonColor normalColor: JamiTheme.transparentColor source: JamiResources.move_svg @@ -303,8 +341,11 @@ Rectangle { } PushButton { - id: btnminimise + id: btnminimize + toolTipText: isMinimised + ? JamiStrings.extendMapTooltip + : JamiStrings.minimizeMapTooltip imageColor: JamiTheme.mapButtonColor normalColor: JamiTheme.transparentColor source: isMinimised @@ -319,6 +360,9 @@ Rectangle { 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 @@ -335,11 +379,13 @@ Rectangle { PushButton { id: btnClose + toolTipText: JamiStrings.closeMapTooltip imageColor: JamiTheme.mapButtonColor normalColor: JamiTheme.transparentColor source: JamiResources.round_close_24dp_svg onClicked: { + PositionManager.stopPositioning(); PositionManager.setMapActive(false); PositionManager.mapAutoOpening = false; diff --git a/src/app/webengine/map/StopSharingPositionPopup.qml b/src/app/webengine/map/StopSharingPositionPopup.qml new file mode 100644 index 0000000000000000000000000000000000000000..b59f08866401c6ea8fa2abbbca0e9bff13f4a26c --- /dev/null +++ b/src/app/webengine/map/StopSharingPositionPopup.qml @@ -0,0 +1,173 @@ +/* + * 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 QtQuick.Controls + +import net.jami.Models 1.1 +import net.jami.Adapters 1.1 +import net.jami.Constants 1.1 +import Qt5Compat.GraphicalEffects + + +import "../../commoncomponents" + +Popup { + id: root + + width: popupContent.width + height: popupContent.height + + parent: Overlay.overlay + + // center in parent + x: Math.round((parent.width - width) / 2) + y: Math.round((parent.height - height) / 2) + + signal joinClicked + + modal:true + padding: 0 + + visible: false + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + Rectangle { + id: container + + anchors.fill: parent + radius: JamiTheme.modalPopupRadius + color: JamiTheme.secondaryBackgroundColor + + ColumnLayout { + id: popupContent + + Layout.alignment: Qt.AlignCenter + + PushButton { + id: btnClose + + Layout.alignment: Qt.AlignRight + width: 30 + height: 30 + imageContainerWidth: 30 + imageContainerHeight : 30 + Layout.margins: 8 + radius : 5 + imageColor: "grey" + normalColor: JamiTheme.transparentColor + source: JamiResources.round_close_24dp_svg + onClicked: { root.visible = false } + } + + Text { + Layout.leftMargin: 20 + Layout.rightMargin: 20 + Layout.fillWidth: true + Layout.alignment: Qt.AlignCenter + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font.pixelSize: JamiTheme.popuptextSize + font.weight: Font.Medium + wrapMode: Text.WordWrap + color: JamiTheme.textColor + text: JamiStrings.stopSharingPopupBody + } + + RowLayout{ + Layout.margins: JamiTheme.popupButtonsMargin + Layout.alignment: Qt.AlignCenter + + MaterialButton { + preferredWidth: text.contentWidth + textLeftPadding: JamiTheme.buttontextPadding + textRightPadding: JamiTheme.buttontextPadding + + color: JamiTheme.buttonTintedBlue + hoveredColor: JamiTheme.buttonTintedBlueHovered + pressedColor: JamiTheme.buttonTintedBluePressed + text: JamiStrings.stopConvSharing + + onClicked: { + PositionManager.stopSharingPosition(PositionManager.getSelectedConvId()) + shareButtonVisibility = true + root.close() + } + } + + MaterialButton { + preferredWidth: text.contentWidth + textLeftPadding: JamiTheme.buttontextPadding + textRightPadding: JamiTheme.buttontextPadding + + color: JamiTheme.buttonTintedRed + hoveredColor: JamiTheme.buttonTintedRedHovered + pressedColor: JamiTheme.buttonTintedRedPressed + + text: JamiStrings.stopAllSharings + + onClicked: { + PositionManager.stopSharingPosition() + shareButtonVisibility = true + root.close() + } + } + } + + } + } + + background: Rectangle { + color: JamiTheme.transparentColor + } + + Overlay.modal: Rectangle { + color: JamiTheme.transparentColor + // Color animation for overlay when pop up is shown. + ColorAnimation on color { + to: JamiTheme.popupOverlayColor + duration: 500 + } + } + + DropShadow { + z: -1 + width: root.width + height: root.height + horizontalOffset: 3.0 + verticalOffset: 3.0 + radius: container.radius * 4 + color: JamiTheme.shadowColor + source: container + transparentBorder: true + } + + enter: Transition { + NumberAnimation { + properties: "opacity"; from: 0.0; to: 1.0 + duration: JamiTheme.shortFadeDuration + } + } + + exit: Transition { + NumberAnimation { + properties: "opacity"; from: 1.0; to: 0.0 + duration: JamiTheme.shortFadeDuration + } + } +}