mirror of
https://codeberg.org/comaps/comaps
synced 2025-12-19 13:03:36 +00:00
[ios] Review fixes
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
This commit is contained in:
committed by
Yannik Bloscheck
parent
d677112edd
commit
e2efbbe68c
@@ -67,7 +67,7 @@ class ChartLineView: UIView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setY(min: CGFloat, max: CGFloat, animationStyle: ChartAnimation = .none) {
|
func setY(min: CGFloat, max: CGFloat, animationStyle: ChartAnimation = .none) {
|
||||||
assert(min < max)
|
assert(min <= max)
|
||||||
minY = min
|
minY = min
|
||||||
maxY = max
|
maxY = max
|
||||||
updateGraph(animationStyle: animationStyle)
|
updateGraph(animationStyle: animationStyle)
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ static PlacePageDataSchedule convertOpeningHours(std::string_view rawOH)
|
|||||||
- (instancetype)initWithTrackInfo:(TrackInfo * _Nonnull)trackInfo {
|
- (instancetype)initWithTrackInfo:(TrackInfo * _Nonnull)trackInfo {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self) {
|
if (self) {
|
||||||
// TODO: (KK) Replace separator with a shared static constant.
|
|
||||||
NSString * kSeparator = @" • ";
|
NSString * kSeparator = @" • ";
|
||||||
_title = [@[trackInfo.duration, trackInfo.distance] componentsJoinedByString:kSeparator];
|
_title = [@[trackInfo.duration, trackInfo.distance] componentsJoinedByString:kSeparator];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ static ElevationDifficulty convertDifficulty(uint8_t difficulty) {
|
|||||||
andAltitude:point.m_point.GetAltitude()];
|
andAltitude:point.m_point.GetAltitude()];
|
||||||
[pointsArray addObject:elevationPoint];
|
[pointsArray addObject:elevationPoint];
|
||||||
}
|
}
|
||||||
return [pointsArray copy];
|
return pointsArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -41,10 +41,7 @@ final class TrackRecordingButtonViewController: MWMViewController {
|
|||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
// async is for smoother appearance
|
setState(self.state, completion: nil)
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + kDefaultAnimationDuration) {
|
|
||||||
self.setState(self.state, completion: nil)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public methods
|
// MARK: - Public methods
|
||||||
|
|||||||
@@ -892,7 +892,7 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
|||||||
TrackInfo * _Nonnull trackInfo,
|
TrackInfo * _Nonnull trackInfo,
|
||||||
ElevationProfileData * _Nonnull (^ _Nullable elevationData) ()) {
|
ElevationProfileData * _Nonnull (^ _Nullable elevationData) ()) {
|
||||||
__strong __typeof(weakSelf) self = weakSelf;
|
__strong __typeof(weakSelf) self = weakSelf;
|
||||||
|
if (!self) return;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case TrackRecordingStateInactive:
|
case TrackRecordingStateInactive:
|
||||||
[self stopObservingTrackRecordingUpdates];
|
[self stopObservingTrackRecordingUpdates];
|
||||||
|
|||||||
@@ -9,15 +9,18 @@ enum TrackRecordingAction {
|
|||||||
case stopAndSave(name: String)
|
case stopAndSave(name: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum TrackRecordingError: Error {
|
enum LocationError: Error {
|
||||||
case locationIsProhibited
|
case locationIsProhibited
|
||||||
case trackIsEmpty
|
|
||||||
case systemError(Error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum TrackRecordingActionResult {
|
enum StartTrackRecordingResult {
|
||||||
case success
|
case success
|
||||||
case error(TrackRecordingError)
|
case failure(Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum StopTrackRecordingResult {
|
||||||
|
case success
|
||||||
|
case trackIsEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
@@ -44,8 +47,6 @@ typealias TrackRecordingStateHandler = (TrackRecordingState, TrackInfo, Elevatio
|
|||||||
@objcMembers
|
@objcMembers
|
||||||
final class TrackRecordingManager: NSObject {
|
final class TrackRecordingManager: NSObject {
|
||||||
|
|
||||||
typealias CompletionHandler = (TrackRecordingActionResult) -> Void
|
|
||||||
|
|
||||||
fileprivate struct Observation {
|
fileprivate struct Observation {
|
||||||
weak var observer: AnyObject?
|
weak var observer: AnyObject?
|
||||||
var recordingStateDidChangeHandler: TrackRecordingStateHandler?
|
var recordingStateDidChangeHandler: TrackRecordingStateHandler?
|
||||||
@@ -111,22 +112,47 @@ final class TrackRecordingManager: NSObject {
|
|||||||
recordingState == .active
|
recordingState == .active
|
||||||
}
|
}
|
||||||
|
|
||||||
func processAction(_ action: TrackRecordingAction, completion: (CompletionHandler)? = nil) {
|
func start(completion: ((StartTrackRecordingResult) -> Void)? = nil) {
|
||||||
do {
|
do {
|
||||||
switch action {
|
switch recordingState {
|
||||||
case .start:
|
case .inactive:
|
||||||
try startRecording()
|
try checkIsLocationEnabled()
|
||||||
case .stopAndSave(let name):
|
subscribeOnTrackRecordingProgressUpdates()
|
||||||
stopRecording()
|
trackRecorder.startTrackRecording()
|
||||||
try checkIsTrackNotEmpty()
|
notifyObservers()
|
||||||
saveTrackRecording(name: name)
|
do {
|
||||||
|
try activityManager?.start(with: trackRecordingInfo)
|
||||||
|
} catch {
|
||||||
|
LOG(.warning, "Failed to start activity manager")
|
||||||
|
handleError(error)
|
||||||
|
}
|
||||||
|
case .active:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
completion?(.success)
|
completion?(.success)
|
||||||
} catch {
|
} catch {
|
||||||
handleError(error, completion: completion)
|
handleError(error)
|
||||||
|
completion?(.failure(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func stopAndSave(withName name: String = "", completion: ((StopTrackRecordingResult) -> Void)? = nil) {
|
||||||
|
unsubscribeFromTrackRecordingProgressUpdates()
|
||||||
|
trackRecorder.stopTrackRecording()
|
||||||
|
trackRecordingInfo = .empty()
|
||||||
|
activityManager?.stop()
|
||||||
|
notifyObservers()
|
||||||
|
|
||||||
|
guard !trackRecorder.isTrackRecordingEmpty() else {
|
||||||
|
Toast.show(withText: L("track_recording_toast_nothing_to_save"))
|
||||||
|
completion?(.trackIsEmpty)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
trackRecorder.saveTrackRecording(withName: name)
|
||||||
|
completion?(.success)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Private methods
|
// MARK: - Private methods
|
||||||
|
|
||||||
private func subscribeOnTheAppLifecycleEvents() {
|
private func subscribeOnTheAppLifecycleEvents() {
|
||||||
@@ -136,20 +162,12 @@ final class TrackRecordingManager: NSObject {
|
|||||||
object: nil)
|
object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func checkIsLocationEnabled() throws(TrackRecordingError) {
|
private func checkIsLocationEnabled() throws {
|
||||||
if locationService.isLocationProhibited() {
|
if locationService.isLocationProhibited() {
|
||||||
throw TrackRecordingError.locationIsProhibited
|
throw LocationError.locationIsProhibited
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func checkIsTrackNotEmpty() throws(TrackRecordingError) {
|
|
||||||
if trackRecorder.isTrackRecordingEmpty() {
|
|
||||||
throw TrackRecordingError.trackIsEmpty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Handle track recording process
|
|
||||||
|
|
||||||
private func subscribeOnTrackRecordingProgressUpdates() {
|
private func subscribeOnTrackRecordingProgressUpdates() {
|
||||||
trackRecorder.setTrackRecordingUpdateHandler { [weak self] info in
|
trackRecorder.setTrackRecordingUpdateHandler { [weak self] info in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
@@ -163,52 +181,15 @@ final class TrackRecordingManager: NSObject {
|
|||||||
trackRecorder.setTrackRecordingUpdateHandler(nil)
|
trackRecorder.setTrackRecordingUpdateHandler(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Handle Start/Stop event and Errors
|
private func handleError(_ error: Error) {
|
||||||
|
|
||||||
private func startRecording() throws(TrackRecordingError) {
|
|
||||||
switch recordingState {
|
|
||||||
case .inactive:
|
|
||||||
try checkIsLocationEnabled()
|
|
||||||
subscribeOnTrackRecordingProgressUpdates()
|
|
||||||
trackRecorder.startTrackRecording()
|
|
||||||
notifyObservers()
|
|
||||||
do {
|
|
||||||
try activityManager?.start(with: trackRecordingInfo)
|
|
||||||
} catch {
|
|
||||||
LOG(.warning, "Failed to start activity manager")
|
|
||||||
handleError(.systemError(error))
|
|
||||||
}
|
|
||||||
case .active:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func stopRecording() {
|
|
||||||
unsubscribeFromTrackRecordingProgressUpdates()
|
|
||||||
trackRecorder.stopTrackRecording()
|
|
||||||
trackRecordingInfo = .empty()
|
|
||||||
activityManager?.stop()
|
|
||||||
notifyObservers()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func saveTrackRecording(name: String) {
|
|
||||||
trackRecorder.saveTrackRecording(withName: name)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func handleError(_ error: TrackRecordingError, completion: (CompletionHandler)? = nil) {
|
|
||||||
switch error {
|
switch error {
|
||||||
case TrackRecordingError.locationIsProhibited:
|
case LocationError.locationIsProhibited:
|
||||||
// Show alert to enable location
|
// Show alert to enable location
|
||||||
locationService.checkLocationStatus()
|
locationService.checkLocationStatus()
|
||||||
case TrackRecordingError.trackIsEmpty:
|
default:
|
||||||
Toast.show(withText: L("track_recording_toast_nothing_to_save"))
|
|
||||||
case TrackRecordingError.systemError(let error):
|
|
||||||
LOG(.error, error.localizedDescription)
|
LOG(.error, error.localizedDescription)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
DispatchQueue.main.async {
|
|
||||||
completion?(.error(error))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ final class TrackRecordingManagerTests: XCTestCase {
|
|||||||
mockLocationService.locationIsProhibited = false
|
mockLocationService.locationIsProhibited = false
|
||||||
mockTrackRecorder.trackRecordingIsEnabled = false
|
mockTrackRecorder.trackRecordingIsEnabled = false
|
||||||
|
|
||||||
trackRecordingManager.processAction(.start)
|
trackRecordingManager.start()
|
||||||
|
|
||||||
XCTAssertTrue(mockTrackRecorder.startTrackRecordingCalled)
|
XCTAssertTrue(mockTrackRecorder.startTrackRecordingCalled)
|
||||||
XCTAssertTrue(mockActivityManager.startCalled)
|
XCTAssertTrue(mockActivityManager.startCalled)
|
||||||
@@ -60,20 +60,21 @@ final class TrackRecordingManagerTests: XCTestCase {
|
|||||||
|
|
||||||
func test_GivenStartRecording_WhenLocationDisabled_ThenShouldFail() {
|
func test_GivenStartRecording_WhenLocationDisabled_ThenShouldFail() {
|
||||||
mockLocationService.locationIsProhibited = true
|
mockLocationService.locationIsProhibited = true
|
||||||
|
let expectation = expectation(description: "Location is prohibited")
|
||||||
trackRecordingManager.processAction(.start) { result in
|
trackRecordingManager.start() { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
XCTFail("Should not succeed")
|
XCTFail("Should not succeed")
|
||||||
case .error(let error):
|
case .failure(let error):
|
||||||
switch error {
|
switch error {
|
||||||
case .locationIsProhibited:
|
case LocationError.locationIsProhibited:
|
||||||
XCTAssertTrue(true)
|
expectation.fulfill()
|
||||||
default:
|
default:
|
||||||
XCTFail("Unexpected error: \(error)")
|
XCTFail("Unexpected error: \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
wait(for: [expectation], timeout: 1.0)
|
||||||
XCTAssertFalse(self.mockTrackRecorder.startTrackRecordingCalled)
|
XCTAssertFalse(self.mockTrackRecorder.startTrackRecordingCalled)
|
||||||
XCTAssertTrue(trackRecordingManager.recordingState == .inactive)
|
XCTAssertTrue(trackRecordingManager.recordingState == .inactive)
|
||||||
}
|
}
|
||||||
@@ -82,12 +83,12 @@ final class TrackRecordingManagerTests: XCTestCase {
|
|||||||
mockTrackRecorder.trackRecordingIsEnabled = true
|
mockTrackRecorder.trackRecordingIsEnabled = true
|
||||||
mockTrackRecorder.trackRecordingIsEmpty = false
|
mockTrackRecorder.trackRecordingIsEmpty = false
|
||||||
|
|
||||||
trackRecordingManager.processAction(.stopAndSave(name: "Test Track")) { result in
|
trackRecordingManager.stopAndSave(withName: "Test Track") { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
XCTAssertTrue(true)
|
XCTAssertTrue(true)
|
||||||
case .error(let error):
|
case .trackIsEmpty:
|
||||||
XCTFail("Unexpected error: \(error)")
|
XCTFail("Track should not be empty")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
XCTAssertTrue(mockTrackRecorder.stopTrackRecordingCalled)
|
XCTAssertTrue(mockTrackRecorder.stopTrackRecordingCalled)
|
||||||
@@ -99,20 +100,16 @@ final class TrackRecordingManagerTests: XCTestCase {
|
|||||||
func test_GivenStopRecording_WhenTrackIsEmpty_ThenShouldFail() {
|
func test_GivenStopRecording_WhenTrackIsEmpty_ThenShouldFail() {
|
||||||
mockTrackRecorder.trackRecordingIsEnabled = true
|
mockTrackRecorder.trackRecordingIsEnabled = true
|
||||||
mockTrackRecorder.trackRecordingIsEmpty = true
|
mockTrackRecorder.trackRecordingIsEmpty = true
|
||||||
|
let expectation = expectation(description: "Track recording should be empty")
|
||||||
trackRecordingManager.processAction(.stopAndSave(name: "Test Track")) { result in
|
trackRecordingManager.stopAndSave(withName: "Test Track") { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
XCTFail("Should not succeed")
|
XCTFail("Should not succeed")
|
||||||
case .error(let error):
|
|
||||||
switch error {
|
|
||||||
case .trackIsEmpty:
|
case .trackIsEmpty:
|
||||||
XCTAssertTrue(true)
|
expectation.fulfill()
|
||||||
default:
|
|
||||||
XCTFail("Unexpected error: \(error)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
wait(for: [expectation], timeout: 1.0)
|
||||||
XCTAssertFalse(mockTrackRecorder.saveTrackRecordingCalled)
|
XCTAssertFalse(mockTrackRecorder.saveTrackRecordingCalled)
|
||||||
XCTAssertTrue(trackRecordingManager.recordingState == .inactive)
|
XCTAssertTrue(trackRecordingManager.recordingState == .inactive)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,13 +76,19 @@ extension BottomMenuInteractor: BottomMenuInteractorProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func toggleTrackRecording() {
|
func toggleTrackRecording() {
|
||||||
|
close()
|
||||||
switch trackRecorder.recordingState {
|
switch trackRecorder.recordingState {
|
||||||
case .active:
|
case .active:
|
||||||
break
|
break
|
||||||
case .inactive:
|
case .inactive:
|
||||||
trackRecorder.processAction(.start)
|
trackRecorder.start { result in
|
||||||
}
|
switch result {
|
||||||
close()
|
case .success:
|
||||||
MapViewController.shared()?.showTrackRecordingPlacePage()
|
MapViewController.shared()?.showTrackRecordingPlacePage()
|
||||||
|
case .failure:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,12 +76,12 @@ extension ElevationProfilePresenter: ElevationProfilePresenterProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func configure() {
|
func configure() {
|
||||||
|
view?.isChartViewHidden = false
|
||||||
|
|
||||||
let kMinPointsToDraw = 3
|
let kMinPointsToDraw = 3
|
||||||
guard let profileData, let chartData, chartData.points.count >= kMinPointsToDraw else {
|
guard let profileData, let chartData, chartData.points.count >= kMinPointsToDraw else {
|
||||||
view?.isChartViewHidden = true
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
view?.isChartViewHidden = false
|
|
||||||
view?.setChartData(ChartPresentationData(chartData, formatter: formatter))
|
view?.setChartData(ChartPresentationData(chartData, formatter: formatter))
|
||||||
view?.reloadDescription()
|
view?.reloadDescription()
|
||||||
|
|
||||||
|
|||||||
@@ -251,11 +251,11 @@ extension PlacePageInteractor: ActionBarViewControllerDelegate {
|
|||||||
showTrackDeletionConfirmationDialog()
|
showTrackDeletionConfirmationDialog()
|
||||||
case .saveTrackRecording:
|
case .saveTrackRecording:
|
||||||
// TODO: (KK) pass name typed by user
|
// TODO: (KK) pass name typed by user
|
||||||
TrackRecordingManager.shared.processAction(.stopAndSave(name: "")) { [weak self] result in
|
TrackRecordingManager.shared.stopAndSave() { [weak self] result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
break
|
break
|
||||||
case .error:
|
case .trackIsEmpty:
|
||||||
self?.presenter?.closeAnimated()
|
self?.presenter?.closeAnimated()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user