forked from organicmaps/organicmaps
Compare commits
7 commits
master
...
ios/restor
Author | SHA1 | Date | |
---|---|---|---|
9d61378f60 | |||
6bf89b246a | |||
f258f6ddff | |||
2fd0a82c1f | |||
663dddecfa | |||
702a2ea8f1 | |||
5d682070b6 |
20 changed files with 424 additions and 67 deletions
|
@ -143,6 +143,10 @@ NS_SWIFT_NAME(BookmarksManager)
|
|||
- (void)moveTrack:(MWMTrackID)trackId
|
||||
toGroupId:(MWMMarkGroupID)groupId;
|
||||
|
||||
- (void)recoverBookmark:(MWMMarkID)bookmarkId;
|
||||
- (void)recoverTrack:(MWMTrackID)trackId;
|
||||
- (BOOL)hasRecentlyDeletedBookmark:(MWMMarkID)bookmarkId;
|
||||
|
||||
- (instancetype)init __attribute__((unavailable("call +manager instead")));
|
||||
- (instancetype)copy __attribute__((unavailable("call +manager instead")));
|
||||
- (instancetype)copyWithZone:(NSZone *)zone __attribute__((unavailable("call +manager instead")));
|
||||
|
|
|
@ -191,6 +191,13 @@ static KmlFileType convertFileTypeToCore(MWMKmlFileType fileType) {
|
|||
self.bm.SetAsyncLoadingCallbacks(std::move(bookmarkCallbacks));
|
||||
}
|
||||
|
||||
- (void)notifyObserversOnCategoryUpdated:(MWMMarkGroupID)groupId {
|
||||
[self loopObservers:^(id<MWMBookmarksObserver> observer) {
|
||||
if ([observer respondsToSelector:@selector(onBookmarksCategoryUpdated:)])
|
||||
[observer onBookmarksCategoryUpdated:groupId];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Bookmarks loading
|
||||
|
||||
- (BOOL)areBookmarksLoaded
|
||||
|
@ -489,15 +496,17 @@ static KmlFileType convertFileTypeToCore(MWMKmlFileType fileType) {
|
|||
|
||||
- (void)deleteBookmark:(MWMMarkID)bookmarkId
|
||||
{
|
||||
auto const groupId = self.bm.GetBookmark(bookmarkId)->GetGroupId();
|
||||
self.bm.GetEditSession().DeleteBookmark(bookmarkId);
|
||||
[self loopObservers:^(id<MWMBookmarksObserver> observer) {
|
||||
if ([observer respondsToSelector:@selector(onBookmarkDeleted:)])
|
||||
[observer onBookmarkDeleted:bookmarkId];
|
||||
}];
|
||||
if (self.bm.HasBmCategory(groupId))
|
||||
[self notifyObserversOnCategoryUpdated:groupId];
|
||||
}
|
||||
|
||||
- (void)deleteTrack:(MWMTrackID)trackId {
|
||||
auto const groupId = self.bm.GetTrack(trackId)->GetGroupId();
|
||||
self.bm.GetEditSession().DeleteTrack(trackId);
|
||||
if (self.bm.HasBmCategory(groupId))
|
||||
[self notifyObserversOnCategoryUpdated:groupId];
|
||||
}
|
||||
|
||||
- (MWMBookmark *)bookmarkWithId:(MWMMarkID)bookmarkId {
|
||||
|
@ -698,6 +707,10 @@ static KmlFileType convertFileTypeToCore(MWMKmlFileType fileType) {
|
|||
bookmark->SetDescription(description.UTF8String);
|
||||
if (title.UTF8String != bookmark->GetPreferredName())
|
||||
bookmark->SetCustomName(title.UTF8String);
|
||||
|
||||
[self notifyObserversOnCategoryUpdated:groupId];
|
||||
if (currentGroupId != groupId)
|
||||
[self notifyObserversOnCategoryUpdated:currentGroupId];
|
||||
}
|
||||
|
||||
- (void)updateBookmark:(MWMMarkID)bookmarkId setColor:(MWMBookmarkColor)color {
|
||||
|
@ -711,6 +724,8 @@ static KmlFileType convertFileTypeToCore(MWMKmlFileType fileType) {
|
|||
self.bm.SetLastEditedBmColor(kmlColor);
|
||||
|
||||
bookmark->SetColor(kmlColor);
|
||||
|
||||
[self notifyObserversOnCategoryUpdated:bookmark->GetGroupId()];
|
||||
}
|
||||
|
||||
- (void)moveBookmark:(MWMMarkID)bookmarkId toGroupId:(MWMMarkGroupID)groupId {
|
||||
|
@ -720,6 +735,28 @@ static KmlFileType convertFileTypeToCore(MWMKmlFileType fileType) {
|
|||
auto editSession = self.bm.GetEditSession();
|
||||
editSession.MoveBookmark(bookmarkId, currentGroupId, groupId);
|
||||
}
|
||||
|
||||
[self notifyObserversOnCategoryUpdated:groupId];
|
||||
if (currentGroupId != groupId)
|
||||
[self notifyObserversOnCategoryUpdated:currentGroupId];
|
||||
}
|
||||
|
||||
- (void)recoverBookmark:(MWMMarkID)bookmarkId {
|
||||
Bookmark * bookmark = self.bm.GetEditSession().RecoverRecentlyDeletedBookmark(bookmarkId);
|
||||
if (bookmark)
|
||||
[self notifyObserversOnCategoryUpdated:bookmark->GetGroupId()];
|
||||
}
|
||||
|
||||
- (void)recoverTrack:(MWMTrackID)trackId {
|
||||
Track * track = self.bm.GetEditSession().RecoverRecentlyDeletedTrack(trackId);
|
||||
if (track)
|
||||
[self notifyObserversOnCategoryUpdated:track->GetGroupId()];
|
||||
}
|
||||
|
||||
- (BOOL)hasRecentlyDeletedBookmark:(MWMMarkID)bookmarkId {
|
||||
auto const bookmark = self.bm.GetBookmark(bookmarkId);
|
||||
ASSERT(bookmark, ());
|
||||
return self.bm.GetRecentlyDeletedBookmarkByPoint(bookmark->GetData().m_point);
|
||||
}
|
||||
|
||||
- (void)updateTrack:(MWMTrackID)trackId
|
||||
|
@ -742,6 +779,10 @@ static KmlFileType convertFileTypeToCore(MWMKmlFileType fileType) {
|
|||
track->SetColor(newColor);
|
||||
|
||||
track->SetName(title.UTF8String);
|
||||
|
||||
[self notifyObserversOnCategoryUpdated:groupId];
|
||||
if (currentGroupId != groupId)
|
||||
[self notifyObserversOnCategoryUpdated:currentGroupId];
|
||||
}
|
||||
|
||||
- (void)updateTrack:(MWMTrackID)trackId setColor:(UIColor *)color {
|
||||
|
@ -755,6 +796,8 @@ static KmlFileType convertFileTypeToCore(MWMKmlFileType fileType) {
|
|||
|
||||
if (newColor != currentColor)
|
||||
track->SetColor(newColor);
|
||||
|
||||
[self notifyObserversOnCategoryUpdated:track->GetGroupId()];
|
||||
}
|
||||
|
||||
- (void)moveTrack:(MWMTrackID)trackId toGroupId:(MWMMarkGroupID)groupId {
|
||||
|
@ -764,6 +807,10 @@ static KmlFileType convertFileTypeToCore(MWMKmlFileType fileType) {
|
|||
auto editSession = self.bm.GetEditSession();
|
||||
editSession.MoveTrack(trackId, currentGroupId, groupId);
|
||||
}
|
||||
|
||||
[self notifyObserversOnCategoryUpdated:groupId];
|
||||
if (currentGroupId != groupId)
|
||||
[self notifyObserversOnCategoryUpdated:currentGroupId];
|
||||
}
|
||||
|
||||
- (void)setCategory:(MWMMarkGroupID)groupId authorType:(MWMBookmarkGroupAuthorType)author
|
||||
|
|
|
@ -9,6 +9,7 @@ NS_SWIFT_NAME(BookmarksObserver)
|
|||
- (void)onBookmarksLoadFinished;
|
||||
- (void)onBookmarksFileLoadSuccess;
|
||||
- (void)onBookmarksFileLoadError;
|
||||
- (void)onBookmarksCategoryUpdated:(MWMMarkGroupID)groupId;
|
||||
- (void)onBookmarksCategoryDeleted:(MWMMarkGroupID)groupId;
|
||||
- (void)onBookmarkDeleted:(MWMMarkID)bookmarkId;
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property(nonatomic, readonly) BOOL isMyPosition;
|
||||
@property(nonatomic, readonly) BOOL isPreviewPlus;
|
||||
@property(nonatomic, readonly) BOOL isRoutePoint;
|
||||
@property(nonatomic, readonly) BOOL hasRecentlyDeletedBookmark;
|
||||
@property(nonatomic, readonly) CLLocationCoordinate2D locationCoordinate;
|
||||
@property(nonatomic, copy, nullable) MWMVoidBlock onBookmarkStatusUpdate;
|
||||
@property(nonatomic, copy, nullable) MWMVoidBlock onMapNodeStatusUpdate;
|
||||
|
|
|
@ -97,6 +97,10 @@ static PlacePageRoadType convertRoadType(RoadWarningMarkType roadType) {
|
|||
return GetFramework().HasPlacePageInfo();
|
||||
}
|
||||
|
||||
- (BOOL)hasRecentlyDeletedBookmark {
|
||||
return [MWMBookmarksManager.sharedManager hasRecentlyDeletedBookmark:_bookmarkData.bookmarkId];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)updateBookmarkStatus {
|
||||
|
|
|
@ -121,14 +121,24 @@ extension BookmarksListInteractor: IBookmarksListInteractor {
|
|||
return BookmarksListSortingType(bookmarksManager.lastSortingType(markGroupId))
|
||||
}
|
||||
|
||||
func deleteBookmark(_ bookmarkId: MWMMarkID) {
|
||||
func deleteBookmark(_ bookmarkId: MWMMarkID, completion: (Bookmark) -> Void) {
|
||||
completion(bookmarksManager.bookmark(withId: bookmarkId))
|
||||
bookmarksManager.deleteBookmark(bookmarkId)
|
||||
}
|
||||
|
||||
func deleteTrack(_ trackId: MWMTrackID) {
|
||||
func deleteTrack(_ trackId: MWMTrackID, completion: (Track) -> Void) {
|
||||
completion(bookmarksManager.track(withId: trackId))
|
||||
bookmarksManager.deleteTrack(trackId)
|
||||
}
|
||||
|
||||
func recoverBookmark(_ bookmarkId: MWMMarkID) {
|
||||
bookmarksManager.recoverBookmark(bookmarkId)
|
||||
}
|
||||
|
||||
func recoverTrack(_ trackId: MWMTrackID) {
|
||||
bookmarksManager.recoverTrack(trackId)
|
||||
}
|
||||
|
||||
func moveBookmark(_ bookmarkId: MWMMarkID, toGroupId groupId: MWMMarkGroupID) {
|
||||
bookmarksManager.moveBookmark(bookmarkId, toGroupId: groupId)
|
||||
}
|
||||
|
@ -183,4 +193,9 @@ extension BookmarksListInteractor: BookmarksObserver {
|
|||
func onBookmarksCategoryDeleted(_ groupId: MWMMarkGroupID) {
|
||||
reloadCategory()
|
||||
}
|
||||
|
||||
func onBookmarksCategoryUpdated(_ groupId: MWMMarkGroupID) {
|
||||
guard groupId == markGroupId else { return }
|
||||
reloadCategory()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,8 +110,10 @@ protocol IBookmarksListInteractor {
|
|||
completion: @escaping ([BookmarksSection]) -> Void)
|
||||
func resetSort()
|
||||
func lastSortingType() -> BookmarksListSortingType?
|
||||
func deleteBookmark(_ bookmarkId: MWMMarkID)
|
||||
func deleteTrack(_ trackId: MWMTrackID)
|
||||
func deleteBookmark(_ bookmarkId: MWMMarkID, completion: (Bookmark) -> Void)
|
||||
func deleteTrack(_ trackId: MWMTrackID, completion: (Track) -> Void)
|
||||
func recoverBookmark(_ bookmarkId: MWMMarkID)
|
||||
func recoverTrack(_ trackId: MWMTrackID)
|
||||
func moveBookmark(_ bookmarkId: MWMMarkID, toGroupId: MWMMarkGroupID)
|
||||
func moveTrack(_ trackId: MWMTrackID, toGroupId: MWMMarkGroupID)
|
||||
func updateBookmark(_ bookmarkId: MWMMarkID, setGroupId groupId: MWMMarkGroupID, title: String, color: BookmarkColor, description: String)
|
||||
|
|
|
@ -273,11 +273,25 @@ extension BookmarksListPresenter: IBookmarksListPresenter {
|
|||
switch section {
|
||||
case let bookmarksSection as IBookmarksSectionViewModel:
|
||||
guard let bookmark = bookmarksSection.bookmarks[index] as? BookmarkViewModel else { fatalError() }
|
||||
interactor.deleteBookmark(bookmark.bookmarkId)
|
||||
interactor.deleteBookmark(bookmark.bookmarkId) { bookmark in
|
||||
// TODO: localize texts
|
||||
Toast.undoToast(deletedObject: bookmark.bookmarkName,
|
||||
undoAction: { [weak self, interactor] in
|
||||
interactor.recoverBookmark(bookmark.bookmarkId)
|
||||
self?.reload()
|
||||
}).show()
|
||||
}
|
||||
reload()
|
||||
case let tracksSection as ITracksSectionViewModel:
|
||||
guard let track = tracksSection.tracks[index] as? TrackViewModel else { fatalError() }
|
||||
interactor.deleteTrack(track.trackId)
|
||||
interactor.deleteTrack(track.trackId) { track in
|
||||
// TODO: localize texts
|
||||
Toast.undoToast(deletedObject: track.trackName,
|
||||
undoAction: { [weak self, interactor] in
|
||||
interactor.recoverTrack(track.trackId)
|
||||
self?.reload()
|
||||
}).show()
|
||||
}
|
||||
reload()
|
||||
default:
|
||||
fatalError("Cannot delete item: unsupported section type: \(section.self)")
|
||||
|
|
|
@ -185,6 +185,10 @@ extension BMCViewController: BMCView {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isVisible() -> Bool {
|
||||
isViewLoaded && view.window != nil
|
||||
}
|
||||
}
|
||||
|
||||
extension BMCViewController: UITableViewDataSource {
|
||||
|
|
|
@ -3,6 +3,7 @@ protocol BMCView: AnyObject {
|
|||
func delete(at indexPaths: [IndexPath])
|
||||
func insert(at indexPaths: [IndexPath])
|
||||
func conversionFinished(success: Bool)
|
||||
func isVisible() -> Bool
|
||||
}
|
||||
|
||||
final class BMCDefaultViewModel: NSObject {
|
||||
|
@ -169,10 +170,12 @@ extension BMCDefaultViewModel: BookmarksObserver {
|
|||
}
|
||||
|
||||
func onBookmarksCategoryDeleted(_ groupId: MWMMarkGroupID) {
|
||||
guard let view, view.isVisible() else { return }
|
||||
reloadData()
|
||||
}
|
||||
|
||||
func onBookmarkDeleted(_: MWMMarkID) {
|
||||
func onBookmarksCategoryUpdated(_ groupId: MWMMarkGroupID) {
|
||||
guard let view, view.isVisible() else { return }
|
||||
reloadData()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,62 +1,52 @@
|
|||
@objc(MWMToast)
|
||||
final class Toast: NSObject {
|
||||
class Toast: NSObject {
|
||||
@objc(MWMToastAlignment)
|
||||
enum Alignment: Int {
|
||||
case bottom
|
||||
case top
|
||||
}
|
||||
|
||||
private var blurView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
|
||||
private var timer: Timer?
|
||||
|
||||
private static let duration: TimeInterval = 3.0
|
||||
private static var toasts: [Toast] = []
|
||||
|
||||
@objc static func toast(withText text: String) -> Toast {
|
||||
let toast = Toast(text)
|
||||
toasts.append(toast)
|
||||
return toast
|
||||
private let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
|
||||
private var timer: Timer?
|
||||
|
||||
var contentView: UIView { blurView.contentView }
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
setupBlurView()
|
||||
Self.toasts.append(self)
|
||||
}
|
||||
|
||||
@objc static func hideAll() {
|
||||
toasts.forEach { $0.hide() }
|
||||
}
|
||||
|
||||
private init(_ text: String) {
|
||||
|
||||
private func setupBlurView() {
|
||||
blurView.layer.setCorner(radius: 8)
|
||||
blurView.clipsToBounds = true
|
||||
blurView.alpha = 0
|
||||
|
||||
let label = UILabel()
|
||||
label.text = text
|
||||
label.textAlignment = .center
|
||||
label.numberOfLines = 0
|
||||
label.font = .regular14()
|
||||
label.textColor = .white
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
blurView.contentView.addSubview(label)
|
||||
blurView.isUserInteractionEnabled = false
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
label.leadingAnchor.constraint(equalTo: blurView.contentView.leadingAnchor, constant: 8),
|
||||
label.trailingAnchor.constraint(equalTo: blurView.contentView.trailingAnchor, constant: -8),
|
||||
label.topAnchor.constraint(equalTo: blurView.contentView.topAnchor, constant: 8),
|
||||
label.bottomAnchor.constraint(equalTo: blurView.contentView.bottomAnchor, constant: -8)
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
deinit {
|
||||
timer?.invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
extension Toast {
|
||||
@objc static func hideAll() {
|
||||
toasts.forEach { $0.hide() }
|
||||
}
|
||||
|
||||
@objc func show() {
|
||||
show(in: UIApplication.shared.keyWindow, alignment: .bottom)
|
||||
Self.hideAll()
|
||||
show(in: UIApplication.shared.keyWindow, alignment: .bottom, duration: Toast.duration)
|
||||
}
|
||||
|
||||
@objc func show(withAlignment alignment: Alignment, pinToSafeArea: Bool = true) {
|
||||
show(in: UIApplication.shared.keyWindow, alignment: alignment, pinToSafeArea: pinToSafeArea)
|
||||
@objc func show(withAlignment alignment: Alignment, pinToSafeArea: Bool = true, duration: TimeInterval = Toast.duration) {
|
||||
show(in: UIApplication.shared.keyWindow, alignment: alignment, pinToSafeArea: pinToSafeArea, duration: duration)
|
||||
}
|
||||
|
||||
@objc func show(in view: UIView?, alignment: Alignment, pinToSafeArea: Bool = true) {
|
||||
@objc func show(in view: UIView?, alignment: Alignment, pinToSafeArea: Bool = true, duration: TimeInterval = Toast.duration) {
|
||||
guard let view = view else { return }
|
||||
blurView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(blurView)
|
||||
|
@ -85,7 +75,7 @@ final class Toast: NSObject {
|
|||
self.blurView.alpha = 1
|
||||
}
|
||||
|
||||
timer = Timer.scheduledTimer(timeInterval: 3,
|
||||
timer = Timer.scheduledTimer(timeInterval: duration,
|
||||
target: self,
|
||||
selector: #selector(hide),
|
||||
userInfo: nil,
|
||||
|
@ -102,3 +92,111 @@ final class Toast: NSObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Toast {
|
||||
@objc static func toast(withText text: String) -> Toast {
|
||||
DefaultToast(text)
|
||||
}
|
||||
|
||||
@objc static func undoToast(withText text: String, undoAction: @escaping () -> Void) -> Toast {
|
||||
UndoToast(text, undoAction: undoAction)
|
||||
}
|
||||
|
||||
@objc static func undoToast(deletedObject: String, undoAction: @escaping () -> Void) -> Toast {
|
||||
// TODO: localize text
|
||||
undoToast(withText: "The \(deletedObject) was deleted", undoAction: undoAction)
|
||||
}
|
||||
}
|
||||
|
||||
private final class DefaultToast: Toast {
|
||||
|
||||
private let messageLabel = UILabel()
|
||||
|
||||
fileprivate convenience init(_ text: String) {
|
||||
self.init()
|
||||
setupMessageLabel(text)
|
||||
layoutViews()
|
||||
}
|
||||
|
||||
private func setupMessageLabel(_ text: String) {
|
||||
messageLabel.text = text
|
||||
messageLabel.textAlignment = .center
|
||||
messageLabel.numberOfLines = 0
|
||||
messageLabel.font = .regular14()
|
||||
messageLabel.textColor = .white
|
||||
messageLabel.isUserInteractionEnabled = false
|
||||
}
|
||||
|
||||
private func layoutViews() {
|
||||
contentView.addSubview(messageLabel)
|
||||
|
||||
messageLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
messageLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8),
|
||||
messageLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8),
|
||||
messageLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
|
||||
messageLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
private final class UndoToast: Toast {
|
||||
|
||||
private let messageLabel = UILabel()
|
||||
private let undoButton = UIButton()
|
||||
private var undoAction: (() -> Void)?
|
||||
|
||||
fileprivate convenience init(_ text: String, undoAction: @escaping () -> Void) {
|
||||
self.init()
|
||||
self.undoAction = undoAction
|
||||
setupMessageLabel(text)
|
||||
setupUndoButton()
|
||||
layoutViews()
|
||||
}
|
||||
|
||||
private func setupMessageLabel(_ text: String) {
|
||||
messageLabel.text = text
|
||||
messageLabel.textAlignment = .center
|
||||
messageLabel.numberOfLines = 0
|
||||
messageLabel.font = .regular14()
|
||||
messageLabel.textColor = .white
|
||||
messageLabel.isUserInteractionEnabled = false
|
||||
}
|
||||
|
||||
private func setupUndoButton() {
|
||||
undoButton.setTitle(L("undo"), for: .normal)
|
||||
undoButton.setTitleColor(.white, for: .normal)
|
||||
undoButton.setTitleColor(.lightGray, for: .highlighted)
|
||||
undoButton.titleLabel?.font = .bold17()
|
||||
undoButton.backgroundColor = .clear
|
||||
undoButton.addTarget(self, action: #selector(undoButtonDidTap), for: .touchUpInside)
|
||||
}
|
||||
|
||||
@objc private func undoButtonDidTap() {
|
||||
undoAction?()
|
||||
self.hide()
|
||||
}
|
||||
|
||||
private func layoutViews() {
|
||||
contentView.addSubview(messageLabel)
|
||||
contentView.addSubview(undoButton)
|
||||
|
||||
messageLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
undoButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
messageLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
NSLayoutConstraint.activate([
|
||||
contentView.heightAnchor.constraint(greaterThanOrEqualToConstant: 40),
|
||||
|
||||
messageLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12),
|
||||
messageLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
|
||||
messageLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8),
|
||||
|
||||
undoButton.leadingAnchor.constraint(equalTo: messageLabel.trailingAnchor, constant: 20),
|
||||
undoButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -25),
|
||||
undoButton.centerYAnchor.constraint(equalTo: messageLabel.centerYAnchor),
|
||||
undoButton.heightAnchor.constraint(equalToConstant: 30),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -235,6 +235,10 @@ extension EditBookmarkViewController: MWMNoteCellDelegate {
|
|||
extension EditBookmarkViewController: MWMButtonCellDelegate {
|
||||
func cellDidPressButton(_ cell: UITableViewCell) {
|
||||
BookmarksManager.shared().deleteBookmark(bookmarkId)
|
||||
Toast.undoToast(deletedObject: bookmarkTitle ?? L("bookmark"),
|
||||
undoAction: { [bookmarkId] in
|
||||
BookmarksManager.shared().recoverBookmark(bookmarkId)
|
||||
}).show()
|
||||
if let placePageData = placePageData {
|
||||
FrameworkHelper.updateAfterDeleteBookmark()
|
||||
placePageData.updateBookmarkStatus()
|
||||
|
|
|
@ -186,7 +186,12 @@ extension EditTrackViewController: BookmarkTitleCellDelegate {
|
|||
|
||||
extension EditTrackViewController: MWMButtonCellDelegate {
|
||||
func cellDidPressButton(_ cell: UITableViewCell) {
|
||||
bookmarksManager.deleteTrack(trackId)
|
||||
BookmarksManager.shared().deleteTrack(trackId)
|
||||
Toast.undoToast(withText: trackTitle ?? L("track_title"),
|
||||
undoAction: { [trackId] in
|
||||
BookmarksManager.shared().recoverTrack(trackId)
|
||||
}).show()
|
||||
// TODO: When the PlacePage screen will be implemented it should be reloaded here (see EditBookmarkViewController).
|
||||
goBack()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@ NS_SWIFT_NAME(ActionBarButton)
|
|||
isSelected:(BOOL)isSelected
|
||||
isEnabled:(BOOL)isEnabled;
|
||||
|
||||
- (void)setBookmarkSelected:(BOOL)isSelected;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -164,11 +164,17 @@ NSString *titleForButton(MWMActionBarButtonType type, BOOL isSelected) {
|
|||
}
|
||||
|
||||
- (void)setBookmarkSelected:(BOOL)isSelected {
|
||||
if (isSelected)
|
||||
[self.button.imageView startAnimating];
|
||||
if (self.type != MWMActionBarButtonTypeBookmark)
|
||||
return;
|
||||
|
||||
if (isSelected) {
|
||||
self.label.text = L(@"delete");
|
||||
if (!self.button.isSelected)
|
||||
[self.button.imageView startAnimating];
|
||||
} else {
|
||||
self.label.text = L(self.button.isSelected ? @"Recover" : @"save");
|
||||
}
|
||||
self.button.selected = isSelected;
|
||||
self.label.text = L(isSelected ? @"delete" : @"save");
|
||||
}
|
||||
|
||||
- (void)setupBookmarkButton:(BOOL)isSelected {
|
||||
|
|
|
@ -187,6 +187,7 @@ extension PlacePageCommonLayout {
|
|||
bookmarkViewController.bookmarkData = bookmarkData
|
||||
isBookmark = true
|
||||
}
|
||||
// actionBarViewController.setBookmarkSelected(isBookmark)
|
||||
if let title = placePageData.previewData.title, let headerViewController = headerViewControllers.compactMap({ $0 as? PlacePageHeaderViewController }).first {
|
||||
let secondaryTitle = placePageData.previewData.secondaryTitle
|
||||
headerViewController.setTitle(title, secondaryTitle: secondaryTitle)
|
||||
|
@ -195,6 +196,7 @@ extension PlacePageCommonLayout {
|
|||
self.presenter?.layoutIfNeeded()
|
||||
UIView.animate(withDuration: kDefaultAnimationDuration) { [unowned self] in
|
||||
self.bookmarkViewController.view.isHidden = !isBookmark
|
||||
self.actionBar?.placePageData = self.placePageData
|
||||
self.presenter?.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -187,11 +187,28 @@ using namespace storage;
|
|||
|
||||
- (void)removeBookmark:(PlacePageData *)data
|
||||
{
|
||||
auto &f = GetFramework();
|
||||
f.GetBookmarkManager().GetEditSession().DeleteBookmark(data.bookmarkData.bookmarkId);
|
||||
PlacePageBookmarkData * bookmarkData = data.bookmarkData;
|
||||
NSString * bookmarkTitle = data.previewData.title;
|
||||
|
||||
[MWMBookmarksManager.sharedManager deleteBookmark:bookmarkData.bookmarkId];
|
||||
//
|
||||
// [[MWMToast undoToastWithDeletedObject:bookmarkTitle undoAction:^{
|
||||
// [MWMBookmarksManager.sharedManager recoverBookmark:bookmarkData.bookmarkId];
|
||||
//
|
||||
// // Skip updating PP if it's closed.
|
||||
// if (![self isPPShown])
|
||||
// return;
|
||||
//
|
||||
// auto & f = GetFramework();
|
||||
// auto & info = f.GetCurrentPlacePageInfo();
|
||||
// auto buildInfo = info.GetBuildInfo();
|
||||
// buildInfo.m_match = place_page::BuildInfo::Match::Everything;
|
||||
// buildInfo.m_userMarkId = bookmarkData.bookmarkId;
|
||||
// f.UpdatePlacePageInfoForCurrentSelection(buildInfo);
|
||||
// [data updateBookmarkStatus];
|
||||
// }] show];
|
||||
|
||||
[MWMFrameworkHelper updateAfterDeleteBookmark];
|
||||
|
||||
[data updateBookmarkStatus];
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "base/macros.hpp"
|
||||
#include "base/stl_helpers.hpp"
|
||||
#include "base/string_utils.hpp"
|
||||
#include "base/math.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
|
@ -300,25 +301,36 @@ Bookmark * BookmarkManager::CreateBookmark(kml::BookmarkData && bmData)
|
|||
return AddBookmark(std::make_unique<Bookmark>(std::move(bmData)));
|
||||
}
|
||||
|
||||
Bookmark * BookmarkManager::CreateBookmark(kml::BookmarkData && bm, kml::MarkGroupId groupId)
|
||||
Bookmark * BookmarkManager::CreateBookmark(kml::BookmarkData && bookmarkData, kml::MarkGroupId groupId)
|
||||
{
|
||||
CHECK_THREAD_CHECKER(m_threadChecker, ());
|
||||
|
||||
auto const & c = classif();
|
||||
CHECK(c.HasTypesMapping(), ());
|
||||
std::stringstream ss;
|
||||
for (size_t i = 0; i < bm.m_featureTypes.size(); ++i)
|
||||
for (size_t i = 0; i < bookmarkData.m_featureTypes.size(); ++i)
|
||||
{
|
||||
ss << c.GetReadableObjectName(c.GetTypeForIndex(bm.m_featureTypes[i]));
|
||||
if (i + 1 < bm.m_featureTypes.size())
|
||||
ss << c.GetReadableObjectName(c.GetTypeForIndex(bookmarkData.m_featureTypes[i]));
|
||||
if (i + 1 < bookmarkData.m_featureTypes.size())
|
||||
ss << ",";
|
||||
}
|
||||
|
||||
bm.m_timestamp = kml::TimestampClock::now();
|
||||
bm.m_viewportScale = static_cast<uint8_t>(df::GetZoomLevel(m_viewport.GetScale()));
|
||||
bookmarkData.m_timestamp = kml::TimestampClock::now();
|
||||
bookmarkData.m_viewportScale = static_cast<uint8_t>(df::GetZoomLevel(m_viewport.GetScale()));
|
||||
|
||||
auto * bookmark = CreateBookmark(std::move(bm));
|
||||
auto const recentlyDeletedBookmark = GetRecentlyDeletedBookmarkByPoint(bookmarkData.m_point);
|
||||
if (recentlyDeletedBookmark)
|
||||
{
|
||||
// Recover the bookmark from the recently deleted.
|
||||
bookmarkData = recentlyDeletedBookmark->GetData();
|
||||
if (HasBmCategory(recentlyDeletedBookmark->GetGroupId()))
|
||||
groupId = recentlyDeletedBookmark->GetGroupId();
|
||||
RemoveBookmarkFromRecentlyDeleted(* recentlyDeletedBookmark);
|
||||
}
|
||||
|
||||
auto bookmark = CreateBookmark(std::move(bookmarkData));
|
||||
bookmark->Attach(groupId);
|
||||
|
||||
auto * group = GetBmCategory(groupId);
|
||||
group->AttachUserMark(bookmark->GetId());
|
||||
m_changesTracker.OnAttachBookmark(bookmark->GetId(), groupId);
|
||||
|
@ -372,15 +384,67 @@ void BookmarkManager::DeleteBookmark(kml::MarkId bmId)
|
|||
ASSERT(IsBookmark(bmId), ());
|
||||
auto const it = m_bookmarks.find(bmId);
|
||||
CHECK(it != m_bookmarks.end(), ());
|
||||
auto const groupId = it->second->GetGroupId();
|
||||
|
||||
auto const bookmark = it->second.get();
|
||||
auto const groupId = bookmark->GetGroupId();
|
||||
if (groupId != kml::kInvalidMarkGroupId)
|
||||
{
|
||||
AddBookmarkToRecentlyDeleted(std::move(it->second));
|
||||
DetachUserMark(bmId, groupId);
|
||||
|
||||
}
|
||||
m_changesTracker.OnDeleteMark(bmId);
|
||||
m_bookmarks.erase(it);
|
||||
}
|
||||
|
||||
void BookmarkManager::AddBookmarkToRecentlyDeleted(std::unique_ptr<Bookmark> && bookmark)
|
||||
{
|
||||
m_recentlyDeletedBookmarks.emplace(bookmark.get()->GetId(), std::move(bookmark));
|
||||
}
|
||||
|
||||
void BookmarkManager::RemoveBookmarkFromRecentlyDeleted(const Bookmark & bookmark)
|
||||
{
|
||||
m_recentlyDeletedBookmarks.erase(bookmark.GetId());
|
||||
}
|
||||
|
||||
Bookmark * BookmarkManager::GetRecentlyDeletedBookmarkByPoint(const m2::PointD point)
|
||||
{
|
||||
for (auto const & pair : m_recentlyDeletedBookmarks)
|
||||
{
|
||||
auto const bookmark = pair.second.get();
|
||||
auto const bookmarkPoint = bookmark->GetData().m_point;
|
||||
// The point comparison is done with a small epsilon to avoid floating point errors because the coordinates are stored as 8 char length format in kml.
|
||||
constexpr double eps = 1e-6;
|
||||
if (base::AlmostEqualAbsOrRel(bookmarkPoint.x, point.x, eps) && base::AlmostEqualAbsOrRel(bookmarkPoint.y, point.y, eps))
|
||||
return bookmark;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Bookmark * BookmarkManager::RecoverRecentlyDeletedBookmark(kml::MarkId bookmarkId)
|
||||
{
|
||||
CHECK_THREAD_CHECKER(m_threadChecker, ());
|
||||
auto it = m_recentlyDeletedBookmarks.find(bookmarkId);
|
||||
if (it == m_recentlyDeletedBookmarks.end())
|
||||
return nullptr;
|
||||
|
||||
auto const recentlyDeletedBookmark = it->second.get();
|
||||
auto data = recentlyDeletedBookmark->GetData();
|
||||
|
||||
auto groupId = recentlyDeletedBookmark->GetGroupId();
|
||||
if (!HasBmCategory(groupId))
|
||||
groupId = LastEditedBMCategory();
|
||||
|
||||
auto bookmark = CreateBookmark(std::move(data));
|
||||
bookmark->Attach(groupId);
|
||||
|
||||
auto * group = GetBmCategory(groupId);
|
||||
group->AttachUserMark(bookmark->GetId());
|
||||
m_changesTracker.OnAttachBookmark(bookmark->GetId(), groupId);
|
||||
group->SetIsVisible(true);
|
||||
|
||||
RemoveBookmarkFromRecentlyDeleted(* recentlyDeletedBookmark);
|
||||
return bookmark;
|
||||
}
|
||||
|
||||
void BookmarkManager::DetachUserMark(kml::MarkId bmId, kml::MarkGroupId catId)
|
||||
{
|
||||
GetGroup(catId)->DetachUserMark(bmId);
|
||||
|
@ -391,6 +455,7 @@ void BookmarkManager::DetachUserMark(kml::MarkId bmId, kml::MarkGroupId catId)
|
|||
m_changesTracker.OnDetachBookmark(bmId, catId);
|
||||
}
|
||||
|
||||
|
||||
void BookmarkManager::DeleteCompilations(kml::GroupIdCollection const & compilations)
|
||||
{
|
||||
for (auto const compilationId : compilations)
|
||||
|
@ -456,11 +521,48 @@ void BookmarkManager::DeleteTrack(kml::TrackId trackId)
|
|||
auto it = m_tracks.find(trackId);
|
||||
auto const groupId = it->second->GetGroupId();
|
||||
if (groupId != kml::kInvalidMarkGroupId)
|
||||
{
|
||||
AddTrackToRecentlyDeleted(std::move(it->second));
|
||||
GetBmCategory(groupId)->DetachTrack(trackId);
|
||||
}
|
||||
m_changesTracker.OnDeleteLine(trackId);
|
||||
m_tracks.erase(it);
|
||||
}
|
||||
|
||||
Track * BookmarkManager::RecoverRecentlyDeletedTrack(kml::TrackId trackId)
|
||||
{
|
||||
CHECK_THREAD_CHECKER(m_threadChecker, ());
|
||||
auto it = m_recentlyDeletedTracks.find(trackId);
|
||||
if (it == m_recentlyDeletedTracks.end())
|
||||
return nullptr;
|
||||
|
||||
auto recentlyDeletedTrack = it->second.get();
|
||||
auto data = recentlyDeletedTrack->GetData();
|
||||
|
||||
auto groupId = recentlyDeletedTrack->GetGroupId();
|
||||
if (!HasBmCategory(groupId))
|
||||
groupId = LastEditedBMCategory();
|
||||
|
||||
auto track = CreateTrack(std::move(data));
|
||||
track->Attach(groupId);
|
||||
|
||||
auto * group = GetBmCategory(groupId);
|
||||
group->AttachTrack(trackId);
|
||||
|
||||
RemoveTrackFromRecentlyDeleted(* recentlyDeletedTrack);
|
||||
return track;
|
||||
}
|
||||
|
||||
void BookmarkManager::AddTrackToRecentlyDeleted(std::unique_ptr<Track> && track)
|
||||
{
|
||||
m_recentlyDeletedTracks.emplace(track.get()->GetId(), std::move(track));
|
||||
}
|
||||
|
||||
void BookmarkManager::RemoveTrackFromRecentlyDeleted(const Track & track)
|
||||
{
|
||||
m_recentlyDeletedTracks.erase(track.GetId());
|
||||
}
|
||||
|
||||
void BookmarkManager::GetDirtyGroups(kml::GroupIdSet & dirtyGroups) const
|
||||
{
|
||||
CHECK_THREAD_CHECKER(m_threadChecker, ());
|
||||
|
@ -3446,11 +3548,21 @@ Bookmark * BookmarkManager::EditSession::CreateBookmark(kml::BookmarkData && bmD
|
|||
return m_bmManager.CreateBookmark(std::move(bmData), groupId);
|
||||
}
|
||||
|
||||
Bookmark * BookmarkManager::EditSession::RecoverRecentlyDeletedBookmark(kml::MarkId bookmarkId)
|
||||
{
|
||||
return m_bmManager.RecoverRecentlyDeletedBookmark(bookmarkId);
|
||||
}
|
||||
|
||||
Track * BookmarkManager::EditSession::CreateTrack(kml::TrackData && trackData)
|
||||
{
|
||||
return m_bmManager.CreateTrack(std::move(trackData));
|
||||
}
|
||||
|
||||
Track * BookmarkManager::EditSession::RecoverRecentlyDeletedTrack(kml::TrackId trackId)
|
||||
{
|
||||
return m_bmManager.RecoverRecentlyDeletedTrack(trackId);
|
||||
}
|
||||
|
||||
Bookmark * BookmarkManager::EditSession::GetBookmarkForEdit(kml::MarkId bmId)
|
||||
{
|
||||
return m_bmManager.GetBookmarkForEdit(bmId);
|
||||
|
|
|
@ -165,6 +165,9 @@ public:
|
|||
|
||||
void NotifyChanges();
|
||||
|
||||
Bookmark * RecoverRecentlyDeletedBookmark(kml::MarkId bookmarkId);
|
||||
Track * RecoverRecentlyDeletedTrack(kml::TrackId trackId);
|
||||
|
||||
private:
|
||||
BookmarkManager & m_bmManager;
|
||||
};
|
||||
|
@ -288,6 +291,8 @@ public:
|
|||
kml::MarkGroupId LastEditedBMCategory();
|
||||
kml::PredefinedColor LastEditedBMColor() const;
|
||||
|
||||
Bookmark * GetRecentlyDeletedBookmarkByPoint(const m2::PointD point);
|
||||
|
||||
void SetLastEditedBmCategory(kml::MarkGroupId groupId);
|
||||
void SetLastEditedBmColor(kml::PredefinedColor color);
|
||||
|
||||
|
@ -560,14 +565,21 @@ private:
|
|||
void DetachUserMark(kml::MarkId bmId, kml::MarkGroupId catId);
|
||||
void DeleteCompilations(kml::GroupIdCollection const & compilations);
|
||||
|
||||
Track * CreateTrack(kml::TrackData && trackData);
|
||||
void AddBookmarkToRecentlyDeleted(std::unique_ptr<Bookmark> && bookmark);
|
||||
void RemoveBookmarkFromRecentlyDeleted(const Bookmark & bookmark);
|
||||
Bookmark * RecoverRecentlyDeletedBookmark(kml::MarkId bookmarkId);
|
||||
|
||||
Track * CreateTrack(kml::TrackData && trackData);
|
||||
Track * GetTrackForEdit(kml::TrackId trackId);
|
||||
void AttachTrack(kml::TrackId trackId, kml::MarkGroupId groupId);
|
||||
void DetachTrack(kml::TrackId trackId, kml::MarkGroupId groupId);
|
||||
void DeleteTrack(kml::TrackId trackId);
|
||||
void MoveTrack(kml::TrackId trackID, kml::MarkGroupId curGroupID, kml::MarkGroupId newGroupID);
|
||||
|
||||
void AddTrackToRecentlyDeleted(std::unique_ptr<Track> && track);
|
||||
void RemoveTrackFromRecentlyDeleted(const Track & track);
|
||||
Track * RecoverRecentlyDeletedTrack(kml::TrackId trackId);
|
||||
|
||||
void ClearGroup(kml::MarkGroupId groupId);
|
||||
void SetIsVisible(kml::MarkGroupId groupId, bool visible);
|
||||
|
||||
|
@ -751,7 +763,9 @@ private:
|
|||
|
||||
MarksCollection m_userMarks;
|
||||
BookmarksCollection m_bookmarks;
|
||||
BookmarksCollection m_recentlyDeletedBookmarks;
|
||||
TracksCollection m_tracks;
|
||||
TracksCollection m_recentlyDeletedTracks;
|
||||
|
||||
StaticMarkPoint * m_selectionMark = nullptr;
|
||||
MyPositionMarkPoint * m_myPositionMark = nullptr;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "map/place_page_info.hpp"
|
||||
|
||||
#include "map/framework.hpp"
|
||||
#include "map/bookmark_helpers.hpp"
|
||||
|
||||
#include "indexer/feature_utils.hpp"
|
||||
|
@ -222,6 +222,8 @@ std::string Info::GetBookmarkName()
|
|||
return bookmarkName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Info::SetTitlesForBookmark()
|
||||
{
|
||||
m_uiTitle = GetBookmarkName();
|
||||
|
|
Loading…
Add table
Reference in a new issue