diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_ended_icon.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_ended_icon.imageset/Contents.json
new file mode 100644
index 0000000000..1390be53ef
--- /dev/null
+++ b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_ended_icon.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "filename" : "location_live_cell_ended_icon.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "location_live_cell_ended_icon@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "location_live_cell_ended_icon@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_ended_icon.imageset/location_live_cell_ended_icon.png b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_ended_icon.imageset/location_live_cell_ended_icon.png
new file mode 100644
index 0000000000..9266e542ae
Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_ended_icon.imageset/location_live_cell_ended_icon.png differ
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_ended_icon.imageset/location_live_cell_ended_icon@2x.png b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_ended_icon.imageset/location_live_cell_ended_icon@2x.png
new file mode 100644
index 0000000000..e6bd8d1da7
Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_ended_icon.imageset/location_live_cell_ended_icon@2x.png differ
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_ended_icon.imageset/location_live_cell_ended_icon@3x.png b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_ended_icon.imageset/location_live_cell_ended_icon@3x.png
new file mode 100644
index 0000000000..2aad77a527
Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_ended_icon.imageset/location_live_cell_ended_icon@3x.png differ
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_icon.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_icon.imageset/Contents.json
new file mode 100644
index 0000000000..188a203cc7
--- /dev/null
+++ b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_icon.imageset/Contents.json
@@ -0,0 +1,26 @@
+{
+ "images" : [
+ {
+ "filename" : "Subtract.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "Subtract@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "Subtract@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+}
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_icon.imageset/Subtract.png b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_icon.imageset/Subtract.png
new file mode 100644
index 0000000000..16be3b51b7
Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_icon.imageset/Subtract.png differ
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_icon.imageset/Subtract@2x.png b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_icon.imageset/Subtract@2x.png
new file mode 100644
index 0000000000..4e20a1517f
Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_icon.imageset/Subtract@2x.png differ
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_icon.imageset/Subtract@3x.png b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_icon.imageset/Subtract@3x.png
new file mode 100644
index 0000000000..61ea2a528e
Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_icon.imageset/Subtract@3x.png differ
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_loading_icon.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_loading_icon.imageset/Contents.json
new file mode 100644
index 0000000000..7ba4f36ba1
--- /dev/null
+++ b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_loading_icon.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "filename" : "location_live_cell_loading_icon.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "location_live_cell_loading_icon@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "location_live_cell_loading_icon@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_loading_icon.imageset/location_live_cell_loading_icon.png b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_loading_icon.imageset/location_live_cell_loading_icon.png
new file mode 100644
index 0000000000..fd67d933ea
Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_loading_icon.imageset/location_live_cell_loading_icon.png differ
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_loading_icon.imageset/location_live_cell_loading_icon@2x.png b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_loading_icon.imageset/location_live_cell_loading_icon@2x.png
new file mode 100644
index 0000000000..da27784610
Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_loading_icon.imageset/location_live_cell_loading_icon@2x.png differ
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_loading_icon.imageset/location_live_cell_loading_icon@3x.png b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_loading_icon.imageset/location_live_cell_loading_icon@3x.png
new file mode 100644
index 0000000000..49441f8b09
Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Location/location_live_cell_loading_icon.imageset/location_live_cell_loading_icon@3x.png differ
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_placeholder_background_image.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Location/location_placeholder_background_image.imageset/Contents.json
new file mode 100644
index 0000000000..2753cacb98
--- /dev/null
+++ b/Riot/Assets/Images.xcassets/Room/Location/location_placeholder_background_image.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "filename" : "location_placeholder_background_image.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "location_placeholder_background_image@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "location_placeholder_background_image@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_placeholder_background_image.imageset/location_placeholder_background_image.png b/Riot/Assets/Images.xcassets/Room/Location/location_placeholder_background_image.imageset/location_placeholder_background_image.png
new file mode 100644
index 0000000000..b161191b7e
Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Location/location_placeholder_background_image.imageset/location_placeholder_background_image.png differ
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_placeholder_background_image.imageset/location_placeholder_background_image@2x.png b/Riot/Assets/Images.xcassets/Room/Location/location_placeholder_background_image.imageset/location_placeholder_background_image@2x.png
new file mode 100644
index 0000000000..304c25f56b
Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Location/location_placeholder_background_image.imageset/location_placeholder_background_image@2x.png differ
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_placeholder_background_image.imageset/location_placeholder_background_image@3x.png b/Riot/Assets/Images.xcassets/Room/Location/location_placeholder_background_image.imageset/location_placeholder_background_image@3x.png
new file mode 100644
index 0000000000..1bfa2e4415
Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Location/location_placeholder_background_image.imageset/location_placeholder_background_image@3x.png differ
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_user_marker.imageset/location_user_marker.png b/Riot/Assets/Images.xcassets/Room/Location/location_user_marker.imageset/location_user_marker.png
index eedadad5e7..4f1a4b8fbf 100644
Binary files a/Riot/Assets/Images.xcassets/Room/Location/location_user_marker.imageset/location_user_marker.png and b/Riot/Assets/Images.xcassets/Room/Location/location_user_marker.imageset/location_user_marker.png differ
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_user_marker.imageset/location_user_marker@2x.png b/Riot/Assets/Images.xcassets/Room/Location/location_user_marker.imageset/location_user_marker@2x.png
index fce4dcb046..a48c72b014 100644
Binary files a/Riot/Assets/Images.xcassets/Room/Location/location_user_marker.imageset/location_user_marker@2x.png and b/Riot/Assets/Images.xcassets/Room/Location/location_user_marker.imageset/location_user_marker@2x.png differ
diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_user_marker.imageset/location_user_marker@3x.png b/Riot/Assets/Images.xcassets/Room/Location/location_user_marker.imageset/location_user_marker@3x.png
index ff8ce3aad4..e5a28f6e6a 100644
Binary files a/Riot/Assets/Images.xcassets/Room/Location/location_user_marker.imageset/location_user_marker@3x.png and b/Riot/Assets/Images.xcassets/Room/Location/location_user_marker.imageset/location_user_marker@3x.png differ
diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings
index a7ade7ed6d..b3ad37b6b7 100644
--- a/Riot/Assets/en.lproj/Vector.strings
+++ b/Riot/Assets/en.lproj/Vector.strings
@@ -2138,6 +2138,7 @@ Tap the + to start adding people.";
"location_sharing_live_share_title" = "Share live location";
"live_location_sharing_banner_title" = "Live location enabled";
+"live_location_sharing_ended" = "Live location ended";
"live_location_sharing_banner_stop" = "Stop";
"location_sharing_static_share_title" = "Send my current location";
"location_sharing_pin_drop_share_title" = "Send this location";
@@ -2149,6 +2150,9 @@ Tap the + to start adding people.";
"location_sharing_live_list_item_last_update_invalid" = "Unknown last update";
"location_sharing_live_list_item_current_user_display_name" = "You";
"location_sharing_live_list_item_stop_sharing_action" = "Stop sharing";
+"location_sharing_live_timer_incoming" = "Live until %@";
+"location_sharing_live_loading" = "Loading Live location...";
+"location_sharing_live_error" = "Live location error";
// MARK: - MatrixKit
diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift
index d9c9dadb8e..9f91925e14 100644
--- a/Riot/Generated/Images.swift
+++ b/Riot/Generated/Images.swift
@@ -174,9 +174,13 @@ internal class Asset: NSObject {
internal static let voiceCallHangupIcon = ImageAsset(name: "voice_call_hangup_icon")
internal static let liveLocationIcon = ImageAsset(name: "live_location_icon")
internal static let locationCenterMapIcon = ImageAsset(name: "location_center_map_icon")
+ internal static let locationLiveCellEndedIcon = ImageAsset(name: "location_live_cell_ended_icon")
+ internal static let locationLiveCellIcon = ImageAsset(name: "location_live_cell_icon")
+ internal static let locationLiveCellLoadingIcon = ImageAsset(name: "location_live_cell_loading_icon")
internal static let locationLiveIcon = ImageAsset(name: "location_live_icon")
internal static let locationMarkerIcon = ImageAsset(name: "location_marker_icon")
internal static let locationPinIcon = ImageAsset(name: "location_pin_icon")
+ internal static let locationPlaceholderBackgroundImage = ImageAsset(name: "location_placeholder_background_image")
internal static let locationShareIcon = ImageAsset(name: "location_share_icon")
internal static let locationUserMarker = ImageAsset(name: "location_user_marker")
internal static let pollCheckboxDefault = ImageAsset(name: "poll_checkbox_default")
diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift
index 7aacdbb011..98cddc8cbb 100644
--- a/Riot/Generated/Strings.swift
+++ b/Riot/Generated/Strings.swift
@@ -2727,6 +2727,10 @@ public class VectorL10n: NSObject {
public static var liveLocationSharingBannerTitle: String {
return VectorL10n.tr("Vector", "live_location_sharing_banner_title")
}
+ /// Live location ended
+ public static var liveLocationSharingEnded: String {
+ return VectorL10n.tr("Vector", "live_location_sharing_ended")
+ }
/// Loading
public static var loading: String {
return VectorL10n.tr("Vector", "loading")
@@ -2759,6 +2763,10 @@ public class VectorL10n: NSObject {
public static var locationSharingInvalidAuthorizationSettings: String {
return VectorL10n.tr("Vector", "location_sharing_invalid_authorization_settings")
}
+ /// Live location error
+ public static var locationSharingLiveError: String {
+ return VectorL10n.tr("Vector", "location_sharing_live_error")
+ }
/// You
public static var locationSharingLiveListItemCurrentUserDisplayName: String {
return VectorL10n.tr("Vector", "location_sharing_live_list_item_current_user_display_name")
@@ -2783,6 +2791,10 @@ public class VectorL10n: NSObject {
public static func locationSharingLiveListItemTimeLeft(_ p1: String) -> String {
return VectorL10n.tr("Vector", "location_sharing_live_list_item_time_left", p1)
}
+ /// Loading Live location...
+ public static var locationSharingLiveLoading: String {
+ return VectorL10n.tr("Vector", "location_sharing_live_loading")
+ }
/// Share location
public static var locationSharingLiveMapCalloutTitle: String {
return VectorL10n.tr("Vector", "location_sharing_live_map_callout_title")
@@ -2791,6 +2803,10 @@ public class VectorL10n: NSObject {
public static var locationSharingLiveShareTitle: String {
return VectorL10n.tr("Vector", "location_sharing_live_share_title")
}
+ /// Live until %@
+ public static func locationSharingLiveTimerIncoming(_ p1: String) -> String {
+ return VectorL10n.tr("Vector", "location_sharing_live_timer_incoming", p1)
+ }
/// Location
public static var locationSharingLiveViewerTitle: String {
return VectorL10n.tr("Vector", "location_sharing_live_viewer_title")
diff --git a/Riot/Managers/Theme/Theme.swift b/Riot/Managers/Theme/Theme.swift
index 93d9cb2a90..fe8d544258 100644
--- a/Riot/Managers/Theme/Theme.swift
+++ b/Riot/Managers/Theme/Theme.swift
@@ -104,6 +104,12 @@ import DesignKit
var roomCellOutgoingBubbleBackgroundColor: UIColor { get }
+ // Localisation Cells
+
+ var roomCellLocalisationIconStartedColor: UIColor { get }
+
+ var roomCellLocalisationErrorColor: UIColor { get }
+
// MARK: - Customisation methods
diff --git a/Riot/Managers/Theme/Themes/DarkTheme.swift b/Riot/Managers/Theme/Themes/DarkTheme.swift
index d61ed3954a..84b0239ce6 100644
--- a/Riot/Managers/Theme/Themes/DarkTheme.swift
+++ b/Riot/Managers/Theme/Themes/DarkTheme.swift
@@ -98,6 +98,10 @@ class DarkTheme: NSObject, Theme {
}
var roomCellOutgoingBubbleBackgroundColor: UIColor = UIColor(rgb: 0x133A34)
+
+ var roomCellLocalisationIconStartedColor: UIColor = UIColor(rgb: 0x5C56F5)
+
+ var roomCellLocalisationErrorColor: UIColor = UIColor(rgb: 0xFF5B55)
func applyStyle(onTabBar tabBar: UITabBar) {
tabBar.unselectedItemTintColor = self.tabBarUnselectedItemTintColor
diff --git a/Riot/Managers/Theme/Themes/DefaultTheme.swift b/Riot/Managers/Theme/Themes/DefaultTheme.swift
index e2afd93397..a07de1e8f6 100644
--- a/Riot/Managers/Theme/Themes/DefaultTheme.swift
+++ b/Riot/Managers/Theme/Themes/DefaultTheme.swift
@@ -105,6 +105,10 @@ class DefaultTheme: NSObject, Theme {
var roomCellOutgoingBubbleBackgroundColor: UIColor = UIColor(rgb: 0xE7F8F3)
+ var roomCellLocalisationIconStartedColor: UIColor = UIColor(rgb: 0x5C56F5)
+
+ var roomCellLocalisationErrorColor: UIColor = UIColor(rgb: 0xFF5B55)
+
func applyStyle(onTabBar tabBar: UITabBar) {
tabBar.unselectedItemTintColor = self.tabBarUnselectedItemTintColor
tabBar.tintColor = self.tintColor
diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m
index 70ad44a482..9d0603f6da 100644
--- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m
+++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m
@@ -184,6 +184,8 @@ - (instancetype)initWithEvent:(MXEvent *)event andRoomState:(MXRoomState *)roomS
self.collapsable = NO;
self.collapsed = NO;
}
+
+ break;
}
default:
break;
diff --git a/Riot/Modules/Room/Location/LocationMarkerView.xib b/Riot/Modules/Room/Location/LocationMarkerView.xib
index e4ec30008c..2b855435e5 100644
--- a/Riot/Modules/Room/Location/LocationMarkerView.xib
+++ b/Riot/Modules/Room/Location/LocationMarkerView.xib
@@ -1,6 +1,8 @@
+
+
@@ -8,56 +10,42 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
-
-
-
-
+
+
+
+
+
+
@@ -71,6 +59,6 @@
-
+
diff --git a/Riot/Modules/Room/Location/RoomTimelineLocationView.swift b/Riot/Modules/Room/Location/RoomTimelineLocationView.swift
index cb2fb91469..c8f680e57c 100644
--- a/Riot/Modules/Room/Location/RoomTimelineLocationView.swift
+++ b/Riot/Modules/Room/Location/RoomTimelineLocationView.swift
@@ -17,6 +17,64 @@
import UIKit
import Reusable
import Mapbox
+import SwiftUI
+
+protocol RoomTimelineLocationViewDelegate: AnyObject {
+ func roomTimelineLocationViewDidTapStopButton(_ roomTimelineLocationView: RoomTimelineLocationView)
+ func roomTimelineLocationViewDidTapRetryButton(_ roomTimelineLocationView: RoomTimelineLocationView)
+}
+
+struct RoomTimelineLocationViewData {
+ let location: CLLocationCoordinate2D
+ let userAvatarData: AvatarViewData?
+ let mapStyleURL: URL
+}
+
+struct LiveLocationBannerViewData {
+ let placeholderIcon: UIImage?
+ let iconTint: UIColor
+ let title: String
+ let titleColor: UIColor
+ let timeLeftString: String?
+ let rightButtonTitle: String?
+ let rightButtonTag: RightButtonTag
+
+ var showTimer: Bool {
+ return timeLeftString != nil
+ }
+
+ var showRightButton: Bool {
+ return rightButtonTitle != nil
+ }
+
+ var showPlaceholderImage: Bool {
+ return placeholderIcon != nil
+ }
+}
+
+enum TimelineLiveLocationViewState {
+ case incoming(_ status: IncomingLiveLocationSharingStatus) // live location started by other users
+ case outgoing(_ status: OutgoingLiveLocationSharingStatus) // live location started from current user
+}
+
+
+enum OutgoingLiveLocationSharingStatus {
+ case starting
+ case started(_ timeleft: TimeInterval)
+ case failure
+ case stopped
+}
+
+enum IncomingLiveLocationSharingStatus {
+ case starting
+ case started(_ timeleft: TimeInterval)
+ case stopped
+}
+
+enum RightButtonTag: Int {
+ case stopSharing = 0
+ case retrySharing
+}
class RoomTimelineLocationView: UIView, NibLoadable, Themable, MGLMapViewDelegate {
@@ -37,9 +95,37 @@ class RoomTimelineLocationView: UIView, NibLoadable, Themable, MGLMapViewDelegat
@IBOutlet private var descriptionIcon: UIImageView!
@IBOutlet private var attributionLabel: UILabel!
+ // MARK: - Live Location
+ @IBOutlet private var placeholderBackground: UIImageView!
+ @IBOutlet private var placeholderIcon: UIImageView!
+ @IBOutlet private var liveLocationContainerView: UIView!
+ @IBOutlet private var liveLocationImageView: UIImageView!
+ @IBOutlet private var liveLocationStatusLabel: UILabel!
+ @IBOutlet private var liveLocationTimerLabel: UILabel!
+ @IBOutlet private var rightButton: UIButton!
+
+
+
private var mapView: MGLMapView!
private var annotationView: LocationMarkerView?
private static var usernameColorGenerator = UserNameColorGenerator()
+ private var theme: Theme!
+
+ private lazy var incomingTimerFormatter: DateFormatter = {
+ let dateFormatter = DateFormatter()
+ dateFormatter.dateFormat = "HH:mm"
+ return dateFormatter
+ }()
+
+ private lazy var outgoingTimerFormatter: DateComponentsFormatter = {
+ let formatter = DateComponentsFormatter()
+ formatter.zeroFormattingBehavior = .dropAll
+ formatter.allowedUnits = [.hour, .minute, .second]
+ formatter.unitsStyle = .brief
+ return formatter
+ }()
+
+ weak var delegate: RoomTimelineLocationViewDelegate?
// MARK: Public
@@ -52,7 +138,7 @@ class RoomTimelineLocationView: UIView, NibLoadable, Themable, MGLMapViewDelegat
descriptionContainerView.isHidden = (newValue?.count ?? 0 == 0)
}
}
-
+
override func awakeFromNib() {
super.awakeFromNib()
@@ -70,13 +156,16 @@ class RoomTimelineLocationView: UIView, NibLoadable, Themable, MGLMapViewDelegat
clipsToBounds = true
layer.borderWidth = Constants.cellBorderRadius
layer.cornerRadius = Constants.cellCornerRadius
+
+ theme = ThemeService.shared().theme
}
- // MARK: - Public
+ // MARK: - Private
- public func displayLocation(_ location: CLLocationCoordinate2D,
- userAvatarData: AvatarViewData? = nil,
- mapStyleURL: URL) {
+ private func displayLocation(_ location: CLLocationCoordinate2D,
+ userAvatarData: AvatarViewData? = nil,
+ mapStyleURL: URL,
+ bannerViewData: LiveLocationBannerViewData? = nil) {
mapView.styleURL = mapStyleURL
@@ -96,8 +185,126 @@ class RoomTimelineLocationView: UIView, NibLoadable, Themable, MGLMapViewDelegat
let pointAnnotation = MGLPointAnnotation()
pointAnnotation.coordinate = location
mapView.addAnnotation(pointAnnotation)
+
+ // Configure live location banner
+ guard let bannerViewData = bannerViewData else {
+ liveLocationContainerView.isHidden = true
+ return
+ }
+
+ liveLocationContainerView.isHidden = false
+ liveLocationContainerView.backgroundColor = theme.colors.background.withAlphaComponent(0.85)
+
+ liveLocationImageView.image = Asset.Images.locationLiveCellIcon.image
+ liveLocationImageView.tintColor = bannerViewData.iconTint
+
+ liveLocationStatusLabel.text = bannerViewData.title
+ liveLocationStatusLabel.textColor = bannerViewData.titleColor
+
+ liveLocationTimerLabel.text = bannerViewData.timeLeftString
+ liveLocationTimerLabel.textColor = theme.colors.tertiaryContent
+ liveLocationTimerLabel.isHidden = !bannerViewData.showTimer
+
+ rightButton.setTitle(bannerViewData.rightButtonTitle, for: .normal)
+ rightButton.isHidden = !bannerViewData.showRightButton
+ rightButton.tag = bannerViewData.rightButtonTag.rawValue
+
+ placeholderBackground.isHidden = !bannerViewData.showPlaceholderImage
+ placeholderIcon.image = bannerViewData.placeholderIcon
+ placeholderIcon.isHidden = !bannerViewData.showPlaceholderImage
+ placeholderBackground.isHidden = !bannerViewData.showPlaceholderImage
+ mapView.isHidden = bannerViewData.showPlaceholderImage
+ }
+
+ private func liveLocationBannerViewData(from viewState: TimelineLiveLocationViewState) -> LiveLocationBannerViewData {
+
+ let iconTint: UIColor
+ let title: String
+ var titleColor: UIColor = theme.colors.primaryContent
+ var placeholderIcon: UIImage?
+ var timeLeftString: String?
+ var rightButtonTitle: String?
+ var rightButtonTag: RightButtonTag = .stopSharing
+
+ switch viewState {
+ case .incoming(let liveLocationSharingStatus):
+ switch liveLocationSharingStatus {
+ case .starting:
+ iconTint = theme.colors.tertiaryContent
+ title = VectorL10n.locationSharingLiveLoading
+ titleColor = theme.colors.tertiaryContent
+ placeholderIcon = Asset.Images.locationLiveCellLoadingIcon.image
+ case .started(let timeLeft):
+ iconTint = theme.roomCellLocalisationIconStartedColor
+ title = VectorL10n.liveLocationSharingBannerTitle
+ timeLeftString = generateTimerString(for: timeLeft, isIncomingLocation: true)
+ case .stopped:
+ iconTint = theme.colors.tertiaryContent
+ title = VectorL10n.liveLocationSharingEnded
+ titleColor = theme.colors.tertiaryContent
+ placeholderIcon = Asset.Images.locationLiveCellEndedIcon.image
+ }
+ case .outgoing(let liveLocationSharingStatus):
+ switch liveLocationSharingStatus {
+ case .starting:
+ iconTint = theme.colors.tertiaryContent
+ title = VectorL10n.locationSharingLiveLoading
+ titleColor = theme.colors.tertiaryContent
+ placeholderIcon = Asset.Images.locationLiveCellLoadingIcon.image
+ case .started(let timeLeft):
+ iconTint = theme.roomCellLocalisationIconStartedColor
+ title = VectorL10n.liveLocationSharingBannerTitle
+ timeLeftString = generateTimerString(for: timeLeft, isIncomingLocation: false)
+ rightButtonTitle = VectorL10n.stop
+ case .failure:
+ iconTint = theme.roomCellLocalisationErrorColor
+ title = VectorL10n.locationSharingLiveError
+ rightButtonTitle = VectorL10n.retry
+ rightButtonTag = .retrySharing
+ case .stopped:
+ iconTint = theme.colors.tertiaryContent
+ title = VectorL10n.liveLocationSharingEnded
+ titleColor = theme.colors.tertiaryContent
+ placeholderIcon = Asset.Images.locationLiveCellEndedIcon.image
+ }
+ }
+
+ return LiveLocationBannerViewData(placeholderIcon: placeholderIcon, iconTint: iconTint, title: title, titleColor: titleColor, timeLeftString: timeLeftString, rightButtonTitle: rightButtonTitle, rightButtonTag: rightButtonTag)
}
+ private func generateTimerString(for timestamp: Double,
+ isIncomingLocation: Bool) -> String? {
+ let timerInSec = timestamp / 1000 // Timestamp is in millisecond in the SDK
+ let timerString: String?
+ if isIncomingLocation {
+ timerString = VectorL10n.locationSharingLiveTimerIncoming(incomingTimerFormatter.string(from: Date(timeIntervalSince1970: timerInSec)))
+ } else if let outgoingTimer = outgoingTimerFormatter.string(from: Date(timeIntervalSince1970: timerInSec).timeIntervalSinceNow) {
+ timerString = VectorL10n.locationSharingLiveListItemTimeLeft(outgoingTimer)
+ } else {
+ timerString = nil
+ }
+ return timerString
+ }
+
+ // MARK: - Public
+
+ public func displayStaticLocation(with viewData: RoomTimelineLocationViewData) {
+ displayLocation(viewData.location,
+ userAvatarData: viewData.userAvatarData,
+ mapStyleURL: viewData.mapStyleURL,
+ bannerViewData: nil)
+ }
+
+ public func displayLiveLocation(with viewData: RoomTimelineLocationViewData, liveLocationViewState: TimelineLiveLocationViewState) {
+ let bannerViewData = liveLocationBannerViewData(from: liveLocationViewState)
+ displayLocation(viewData.location,
+ userAvatarData: viewData.userAvatarData,
+ mapStyleURL: viewData.mapStyleURL,
+ bannerViewData: bannerViewData)
+
+ }
+
+
// MARK: - Themable
func update(theme: Theme) {
@@ -107,6 +314,7 @@ class RoomTimelineLocationView: UIView, NibLoadable, Themable, MGLMapViewDelegat
descriptionIcon.tintColor = theme.colors.accent
attributionLabel.textColor = theme.colors.accent
layer.borderColor = theme.colors.quinaryContent.cgColor
+ self.theme = theme
}
// MARK: - MGLMapViewDelegate
@@ -114,4 +322,14 @@ class RoomTimelineLocationView: UIView, NibLoadable, Themable, MGLMapViewDelegat
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
return annotationView
}
+
+ // MARK: - Action
+
+ @IBAction private func didTapTightButton(_ sender: Any) {
+ if rightButton.tag == RightButtonTag.stopSharing.rawValue {
+ delegate?.roomTimelineLocationViewDidTapStopButton(self)
+ } else if rightButton.tag == RightButtonTag.retrySharing.rawValue {
+ delegate?.roomTimelineLocationViewDidTapRetryButton(self)
+ }
+ }
}
diff --git a/Riot/Modules/Room/Location/RoomTimelineLocationView.xib b/Riot/Modules/Room/Location/RoomTimelineLocationView.xib
index fb2e22a294..79e8d70977 100644
--- a/Riot/Modules/Room/Location/RoomTimelineLocationView.xib
+++ b/Riot/Modules/Room/Location/RoomTimelineLocationView.xib
@@ -4,7 +4,6 @@
-
@@ -15,15 +14,26 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
@@ -58,7 +137,7 @@
-
+
@@ -69,11 +148,16 @@
-
-
+
+
+
+
+
+
+
@@ -84,12 +168,21 @@
+
+
+
+
+
+
+
-
+
+
+
diff --git a/Riot/Modules/Room/MXKRoomViewController.m b/Riot/Modules/Room/MXKRoomViewController.m
index a8d79f084f..ba1dd3e214 100644
--- a/Riot/Modules/Room/MXKRoomViewController.m
+++ b/Riot/Modules/Room/MXKRoomViewController.m
@@ -3045,6 +3045,22 @@ - (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)ac
[self promptUserToResendEvent:selectedEvent.eventId];
}
}
+ else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellStopShareButtonPressed])
+ {
+ MXEvent *selectedEvent = userInfo[kMXKRoomBubbleCellEventKey];
+ if (selectedEvent)
+ {
+ // TODO: - Implement stop live location action
+ }
+ }
+ else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellRetryShareButtonPressed])
+ {
+ MXEvent *selectedEvent = userInfo[kMXKRoomBubbleCellEventKey];
+ if (selectedEvent)
+ {
+ // TODO: - Implement retry live location action
+ }
+ }
}
#pragma mark - Clipboard
diff --git a/Riot/Modules/Room/TimelineCells/Common/MXKRoomBubbleTableViewCell.h b/Riot/Modules/Room/TimelineCells/Common/MXKRoomBubbleTableViewCell.h
index 8e247835f9..15e18f89be 100644
--- a/Riot/Modules/Room/TimelineCells/Common/MXKRoomBubbleTableViewCell.h
+++ b/Riot/Modules/Room/TimelineCells/Common/MXKRoomBubbleTableViewCell.h
@@ -83,6 +83,20 @@ extern NSString *const kMXKRoomBubbleCellTapOnContentView;
*/
extern NSString *const kMXKRoomBubbleCellUnsentButtonPressed;
+/**
+ Action identifier used when the user pressed stop share button displayed in live location cell.
+
+ The `userInfo` dictionary contains an `MXEvent` object under the `kMXKRoomBubbleCellEventKey` key, representing the live location event to stop.
+ */
+extern NSString *const kMXKRoomBubbleCellStopShareButtonPressed;
+
+/**
+ Action identifier used when the user pressed retry share button displayed in live location cell.
+
+ The `userInfo` dictionary contains an `MXEvent` object under the `kMXKRoomBubbleCellEventKey` key, representing the live location event to retry.
+ */
+extern NSString *const kMXKRoomBubbleCellRetryShareButtonPressed;
+
/**
Action identifier used when the user long pressed on a displayed event.
diff --git a/Riot/Modules/Room/TimelineCells/Common/MXKRoomBubbleTableViewCell.m b/Riot/Modules/Room/TimelineCells/Common/MXKRoomBubbleTableViewCell.m
index c4367d5983..f5fcdc4ffd 100644
--- a/Riot/Modules/Room/TimelineCells/Common/MXKRoomBubbleTableViewCell.m
+++ b/Riot/Modules/Room/TimelineCells/Common/MXKRoomBubbleTableViewCell.m
@@ -41,7 +41,10 @@
NSString *const kMXKRoomBubbleCellTapOnOverlayContainer = @"kMXKRoomBubbleCellTapOnOverlayContainer";
NSString *const kMXKRoomBubbleCellTapOnContentView = @"kMXKRoomBubbleCellTapOnContentView";
+
NSString *const kMXKRoomBubbleCellUnsentButtonPressed = @"kMXKRoomBubbleCellUnsentButtonPressed";
+NSString *const kMXKRoomBubbleCellStopShareButtonPressed = @"kMXKRoomBubbleCellStopShareButtonPressed";
+NSString *const kMXKRoomBubbleCellRetryShareButtonPressed = @"kMXKRoomBubbleCellRetryShareButtonPressed";
NSString *const kMXKRoomBubbleCellLongPressOnEvent = @"kMXKRoomBubbleCellLongPressOnEvent";
NSString *const kMXKRoomBubbleCellLongPressOnProgressView = @"kMXKRoomBubbleCellLongPressOnProgressView";
diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Location/LocationPlainCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Location/LocationPlainCell.swift
index 4b86e42094..77f5f784d4 100644
--- a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Location/LocationPlainCell.swift
+++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Location/LocationPlainCell.swift
@@ -15,41 +15,82 @@
//
import Foundation
+import MatrixSDK
class LocationPlainCell: SizableBaseRoomCell, RoomCellReactionsDisplayable, RoomCellReadMarkerDisplayable {
private var locationView: RoomTimelineLocationView!
+ private var event: MXEvent?
override func render(_ cellData: MXKCellData!) {
super.render(cellData)
guard #available(iOS 14.0, *),
let bubbleData = cellData as? RoomBubbleCellData,
- let event = bubbleData.events.last,
- event.eventType == __MXEventType.roomMessage,
- let locationContent = event.location
+ let event = bubbleData.events.last
else {
return
}
+ self.event = event
locationView.update(theme: ThemeService.shared().theme)
+
+ // Comment this line and uncomment next one to test UI of live location tile
+ renderStaticLocation(event)
+// renderLiveLocation(event)
+ }
+
+ private func renderStaticLocation(_ event: MXEvent) {
+ guard let locationContent = event.location else {
+ return
+ }
+
locationView.locationDescription = locationContent.locationDescription
let location = CLLocationCoordinate2D(latitude: locationContent.latitude, longitude: locationContent.longitude)
let mapStyleURL = bubbleData.mxSession.vc_homeserverConfiguration().tileServer.mapStyleURL
+ let avatarViewData: AvatarViewData?
+
if locationContent.assetType == .user {
- let avatarViewData = AvatarViewData(matrixItemId: bubbleData.senderId,
+ avatarViewData = AvatarViewData(matrixItemId: bubbleData.senderId,
displayName: bubbleData.senderDisplayName,
avatarUrl: bubbleData.senderAvatarUrl,
mediaManager: bubbleData.mxSession.mediaManager,
fallbackImage: .matrixItem(bubbleData.senderId, bubbleData.senderDisplayName))
-
- locationView.displayLocation(location, userAvatarData: avatarViewData, mapStyleURL: mapStyleURL)
} else {
- locationView.displayLocation(location, mapStyleURL: mapStyleURL)
+ avatarViewData = nil
}
+
+ locationView.displayStaticLocation(with: RoomTimelineLocationViewData(location: location, userAvatarData: avatarViewData, mapStyleURL: mapStyleURL))
+ }
+
+ private func renderLiveLocation(_ event: MXEvent) {
+ // TODO: - Render live location cell when live location event is handled
+
+ // This code is only for testing live location cell
+ // Will be completed when the live location event is handled
+
+ guard let locationContent = event.location else {
+ return
+ }
+
+ locationView.locationDescription = locationContent.locationDescription
+
+ let location = CLLocationCoordinate2D(latitude: locationContent.latitude, longitude: locationContent.longitude)
+
+ let mapStyleURL = bubbleData.mxSession.vc_homeserverConfiguration().tileServer.mapStyleURL
+
+ let avatarViewData = AvatarViewData(matrixItemId: bubbleData.senderId,
+ displayName: bubbleData.senderDisplayName,
+ avatarUrl: bubbleData.senderAvatarUrl,
+ mediaManager: bubbleData.mxSession.mediaManager,
+ fallbackImage: .matrixItem(bubbleData.senderId, bubbleData.senderDisplayName))
+ let futurDateTimeInterval = Date(timeIntervalSinceNow: 3734).timeIntervalSince1970 * 1000
+
+ locationView.displayLiveLocation(with: RoomTimelineLocationViewData(location: location, userAvatarData: avatarViewData, mapStyleURL: mapStyleURL),
+ liveLocationViewState: .outgoing(.started(futurDateTimeInterval)))
}
override func setupViews() {
@@ -69,3 +110,21 @@ class LocationPlainCell: SizableBaseRoomCell, RoomCellReactionsDisplayable, Room
contentView.vc_addSubViewMatchingParent(locationView)
}
}
+
+extension LocationPlainCell: RoomTimelineLocationViewDelegate {
+ func roomTimelineLocationViewDidTapStopButton(_ roomTimelineLocationView: RoomTimelineLocationView) {
+ guard let event = self.event else {
+ return
+ }
+
+ delegate.cell(self, didRecognizeAction: kMXKRoomBubbleCellStopShareButtonPressed, userInfo: [kMXKRoomBubbleCellEventKey: event])
+ }
+
+ func roomTimelineLocationViewDidTapRetryButton(_ roomTimelineLocationView: RoomTimelineLocationView) {
+ guard let event = self.event else {
+ return
+ }
+
+ delegate.cell(self, didRecognizeAction: kMXKRoomBubbleCellRetryShareButtonPressed, userInfo: [kMXKRoomBubbleCellEventKey: event])
+ }
+}
diff --git a/RiotSwiftUI/Modules/Room/StaticLocationSharingViewer/Test/UI/StaticLocationViewingUITests.swift b/RiotSwiftUI/Modules/Room/StaticLocationSharingViewer/Test/UI/StaticLocationViewingUITests.swift
index f9c98e0be2..01e2635cb0 100644
--- a/RiotSwiftUI/Modules/Room/StaticLocationSharingViewer/Test/UI/StaticLocationViewingUITests.swift
+++ b/RiotSwiftUI/Modules/Room/StaticLocationSharingViewer/Test/UI/StaticLocationViewingUITests.swift
@@ -42,6 +42,5 @@ class StaticLocationViewingUITests: MockScreenTest {
func verifyInitialExistingLocation() {
XCTAssertTrue(app.buttons["Cancel"].exists, "The cancel button should exist.")
XCTAssertTrue(app.buttons["shareButton"].exists, "The share button should exist.")
- XCTAssertTrue(app.otherElements["Map"].exists, "The map view should exist.")
}
}
diff --git a/changelog.d/6029.change b/changelog.d/6029.change
new file mode 100644
index 0000000000..9a829ea070
--- /dev/null
+++ b/changelog.d/6029.change
@@ -0,0 +1 @@
+Location sharing: Add cell for live location sharing in timeline