Compare commits

...
Sign in to create a new pull request.

7 commits

Author SHA1 Message Date
9d61378f60 [ios] add Recover label to the PP (uncompleted)
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2024-05-31 17:51:02 +04:00
6bf89b246a [ios] implement recovery for the recently deleted bookmarks
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2024-05-31 17:51:02 +04:00
f258f6ddff [ios] add UndoToast class
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2024-05-31 17:51:02 +04:00
2fd0a82c1f [ios] refactor the Toast class class to support the inharitance
Now Toast is a facade to hide the subclass implementations from the callers.

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2024-05-31 17:51:02 +04:00
663dddecfa [ios] add recoverBookmark and recoverTrack methods to BM manager
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2024-05-31 17:51:02 +04:00
702a2ea8f1 [ios] add onBookmarksCategoryUpdated observing to BM manager
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2024-05-31 17:51:02 +04:00
5d682070b6 [bookmarks] implement recently deleted bookmarks and recovery support
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2024-05-31 17:51:02 +04:00
20 changed files with 424 additions and 67 deletions

View file

@ -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")));

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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 {

View file

@ -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()
}
}

View file

@ -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)

View file

@ -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)")

View file

@ -185,6 +185,10 @@ extension BMCViewController: BMCView {
}
}
}
func isVisible() -> Bool {
isViewLoaded && view.window != nil
}
}
extension BMCViewController: UITableViewDataSource {

View file

@ -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()
}
}

View file

@ -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),
])
}
}

View file

@ -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()

View file

@ -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()
}
}

View file

@ -47,6 +47,8 @@ NS_SWIFT_NAME(ActionBarButton)
isSelected:(BOOL)isSelected
isEnabled:(BOOL)isEnabled;
- (void)setBookmarkSelected:(BOOL)isSelected;
@end
NS_ASSUME_NONNULL_END

View file

@ -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 {

View file

@ -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()
}
}

View file

@ -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];
}

View file

@ -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);

View file

@ -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;

View file

@ -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();