[ios] Implement the Recently Deleted screen to restore deleted categories #7978
|
@ -340,7 +340,8 @@ Java_app_organicmaps_bookmarks_data_BookmarkManager_nativeDeleteCategory(
|
|||
JNIEnv *, jobject, jlong catId)
|
||||
{
|
||||
auto const categoryId = static_cast<kml::MarkGroupId>(catId);
|
||||
return static_cast<jboolean>(frm()->GetBookmarkManager().GetEditSession().DeleteBmCategory(categoryId));
|
||||
// `permanently` should be set to false when the Recently Deleted Lists feature be implemented
|
||||
return static_cast<jboolean>(frm()->GetBookmarkManager().GetEditSession().DeleteBmCategory(categoryId, true /* permanently */));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
|
|
|
@ -31248,3 +31248,233 @@
|
|||
vi = Để điều hướng chính xác nhất, chúng tôi khuyên bạn nên tắt chế độ tiết kiệm năng lượng trong cài đặt pin của điện thoại.
|
||||
zh-Hans = 为了获得最准确的导航,我们建议在手机电池设置中禁用省电模式。
|
||||
zh-Hant = 為了獲得最準確的導航,我們建議在手機電池設定中停用省電模式。
|
||||
|
||||
[bookmarks_recently_deleted]
|
||||
comment = Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen.
|
||||
tags = ios
|
||||
en = Recently Deleted Lists
|
||||
af = Onlangs geskrap lyste
|
||||
ar = القوائم المحذوفة مؤخراً
|
||||
az = Son Silinmiş Siyahılar
|
||||
be = Нядаўна выдаленыя спісы
|
||||
bg = Наскоро изтрити списъци
|
||||
ca = Llistes suprimides recentment
|
||||
cs = Nedávno odstraněné seznamy
|
||||
da = Nyligt slettede lister
|
||||
de = Kürzlich gelöschte Listen
|
||||
el = Πρόσφατα διαγραμμένες λίστες
|
||||
es = Listas eliminadas recientemente
|
||||
et = Hiljuti kustutatud nimekirjad
|
||||
eu = Duela gutxi ezabatutako zerrendak
|
||||
fa = لیست های اخیرا حذف شده
|
||||
fi = Äskettäin poistetut luettelot
|
||||
fr = Listes récemment supprimées
|
||||
he = רשימות שנמחקו לאחרונה
|
||||
hi = हाल ही में हटाई गई सूचियाँ
|
||||
hu = Nemrég törölt listák
|
||||
id = Daftar yang Baru Saja Dihapus
|
||||
it = Elenchi eliminati di recente
|
||||
ja = 最近削除されたリスト
|
||||
ko = 최근 삭제한 목록
|
||||
lt = Neseniai ištrinti sąrašai
|
||||
mr = अलीकडे हटवलेल्या याद्या
|
||||
nb = Nylig slettede lister
|
||||
nl = Onlangs verwijderde lijsten
|
||||
pl = Ostatnio usunięte listy
|
||||
pt = Listas eliminadas recentemente
|
||||
pt-BR = Listas excluídas recentemente
|
||||
ro = Liste șterse recent
|
||||
ru = Недавно удаленные списки
|
||||
sk = Nedávno odstránené zoznamy
|
||||
sv = Nyligen borttagna listor
|
||||
sw = Orodha Zilizofutwa Hivi Karibuni
|
||||
th = รายการที่ถูกลบล่าสุด
|
||||
tr = Son Silinen Listeler
|
||||
uk = Нещодавно видалені списки
|
||||
vi = Danh sách đã xóa gần đây
|
||||
zh-Hans = 最近删除的列表
|
||||
zh-Hant = 最近刪除的列表
|
||||
|
||||
[clear]
|
||||
comment = Title for the "Clear" button on the Recently Deleted Lists screen.
|
||||
tags = ios
|
||||
en = Clear
|
||||
af = Vee uit
|
||||
ar = مسح
|
||||
az = Sil
|
||||
be = Ачысціць
|
||||
bg = Изчистване
|
||||
ca = Netejar
|
||||
cs = Vymazat
|
||||
da = Ryd
|
||||
de = Löschen
|
||||
el = Εκκαθάριση
|
||||
es = Limpiar
|
||||
et = Kustuta
|
||||
eu = Garbitu
|
||||
fa = پاک کردن
|
||||
fi = Tyhjennä
|
||||
fr = Effacer
|
||||
he = נקה
|
||||
hi = साफ करें
|
||||
hu = Törlés
|
||||
id = Hapus
|
||||
it = Cancella
|
||||
ja = クリア
|
||||
ko = 지우기
|
||||
lt = Išvalyti
|
||||
mr = साफ करा
|
||||
nb = Fjern
|
||||
nl = Wissen
|
||||
pl = Wyczyść
|
||||
pt = Limpar
|
||||
pt-BR = Limpar
|
||||
ro = Șterge
|
||||
ru = Очистить
|
||||
sk = Vymazať
|
||||
sv = Rensa
|
||||
sw = Futa
|
||||
th = ล้าง
|
||||
tr = Temizle
|
||||
uk = Очистити
|
||||
vi = Xóa
|
||||
zh-Hans = 清除
|
||||
zh-Hant = 清除
|
||||
|
||||
[delete_all]
|
||||
comment = Title for the "Delete All" button on the Recently Deleted Lists screen.
|
||||
tags = ios
|
||||
en = Delete All
|
||||
af = Vee alles uit
|
||||
ar = حذف الكل
|
||||
az = Hamısını Sil
|
||||
be = Выдаліць усё
|
||||
bg = Изтриване на всички
|
||||
ca = Eliminar tots
|
||||
cs = Smazat vše
|
||||
da = Slet alle
|
||||
de = Alle löschen
|
||||
el = Διαγραφή όλων
|
||||
es = Borrar todo
|
||||
et = Kustuta kõik
|
||||
eu = Ezabatu guztiak
|
||||
fa = حذف همه
|
||||
fi = Poista kaikki
|
||||
fr = Supprimer tout
|
||||
he = מחק הכל
|
||||
hi = सभी हटा दो
|
||||
hu = Mindent törölni
|
||||
id = Hapus Semua
|
||||
it = Cancella tutto
|
||||
ja = すべて削除する
|
||||
ko = 모두 삭제
|
||||
lt = Ištrinti viską
|
||||
mr = सर्व हटवा
|
||||
nb = Slett alle
|
||||
nl = Alles verwijderen
|
||||
pl = Usuń wszystko
|
||||
pt = Eliminar tudo
|
||||
pt-BR = Excluir tudo
|
||||
ro = Ștergeți toate
|
||||
ru = Удалить всё
|
||||
sk = Odstrániť všetko
|
||||
sv = Radera alla
|
||||
sw = Futa Zote
|
||||
th = ลบทั้งหมด
|
||||
tr = Tümünü Sil
|
||||
uk = Видалити все
|
||||
vi = Xóa hết
|
||||
zh-Hans = 全部删除
|
||||
zh-Hant = 刪除所有
|
||||
|
||||
[recover]
|
||||
comment = Title for the "Recover" button on the Recently Deleted Lists screen.
|
||||
tags = ios
|
||||
en = Recover
|
||||
af = Herstel
|
||||
ar = استرداد
|
||||
az = Bərpa et
|
||||
be = Аднавіць
|
||||
bg = Възстановяване
|
||||
ca = Recuperar
|
||||
cs = Obnovit
|
||||
da = Gendan
|
||||
de = Wiederherstellen
|
||||
el = Ανάκτηση
|
||||
es = Recuperar
|
||||
et = Taasta
|
||||
eu = Berreskuratu
|
||||
fa = بازیابی
|
||||
fi = Palauta
|
||||
fr = Récupérer
|
||||
he = לשחזר
|
||||
hi = पुनः प्राप्त करें
|
||||
hu = Visszaállítás
|
||||
id = Pulihkan
|
||||
it = Recupera
|
||||
ja = 復元
|
||||
ko = 복구
|
||||
lt = Atkurti
|
||||
mr = पुनर्प्राप्त करा
|
||||
nb = Gjenopprette
|
||||
nl = Herstel
|
||||
pl = Odzyskaj
|
||||
pt = Recuperar
|
||||
pt-BR = Recuperar
|
||||
ro = Recuperare
|
||||
ru = Восстановить
|
||||
sk = Obnoviť
|
||||
sv = Återställ
|
||||
sw = Rejesha
|
||||
th = กู้คืน
|
||||
tr = Kurtar
|
||||
uk = Відновити
|
||||
vi = Khôi phục
|
||||
zh-Hans = 恢复
|
||||
zh-Hant = 恢復
|
||||
|
||||
[recover_all]
|
||||
comment = Title for the "Recover All" button on the Recently Deleted Lists screen.
|
||||
tags = ios
|
||||
en = Recover All
|
||||
af = Herstel Alles
|
||||
ar = استرداد الكل
|
||||
az = Hamısını bərpa et
|
||||
be = Аднавіць усё
|
||||
bg = Възстанови всички
|
||||
ca = Recuperar-ho tot
|
||||
cs = Obnovit vše
|
||||
da = Gendan alle
|
||||
de = Alles wiederherstellen
|
||||
el = Ανάκτηση όλων
|
||||
es = Recuperar todo
|
||||
et = Taasta kõik
|
||||
eu = Guztia berreskuratu
|
||||
fa = بازیابی همه
|
||||
fi = Palauta kaikki
|
||||
fr = Tout récupérer
|
||||
he = לשחזר הכל
|
||||
hi = सभी पुनः प्राप्त करें
|
||||
hu = Mindent visszaállít
|
||||
id = Pulihkan Semua
|
||||
it = Recupera tutto
|
||||
ja = すべて復元
|
||||
ko = 모두 복구
|
||||
lt = Atkurti viską
|
||||
mr = सर्व पुनर्प्राप्त करा
|
||||
nb = Gjenopprett alt
|
||||
nl = Alles herstellen
|
||||
pl = Odzyskaj wszystko
|
||||
pt = Recuperar tudo
|
||||
pt-BR = Recuperar tudo
|
||||
ro = Recuperează tot
|
||||
ru = Восстановить всё
|
||||
sk = Obnoviť všetko
|
||||
sv = Återställ allt
|
||||
sw = Rejesha yote
|
||||
th = กู้คืนทั้งหมด
|
||||
tr = Hepsini Kurtar
|
||||
uk = Відновити все
|
||||
vi = Khôi phục tất cả
|
||||
zh-Hans = 全部恢复
|
||||
zh-Hant = 全部恢復
|
||||
|
|
|
@ -86,6 +86,9 @@
|
|||
9974CA2A23DF1968003FE824 /* ElevationProfileData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9974CA2823DF1968003FE824 /* ElevationProfileData.mm */; };
|
||||
9974CA2D23DF197B003FE824 /* ElevationProfileData+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 9974CA2B23DF197B003FE824 /* ElevationProfileData+Core.h */; };
|
||||
AC6A585728057EF6003EABAF /* StringUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = AC6A585628057CC1003EABAF /* StringUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
EDC4E3512C5D222D009286A2 /* RecentlyDeletedCategory.mm in Sources */ = {isa = PBXBuildFile; fileRef = EDC4E34E2C5D222D009286A2 /* RecentlyDeletedCategory.mm */; };
|
||||
EDC4E3522C5D222D009286A2 /* RecentlyDeletedCategory.h in Headers */ = {isa = PBXBuildFile; fileRef = EDC4E34F2C5D222D009286A2 /* RecentlyDeletedCategory.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
EDC4E3532C5D222D009286A2 /* RecentlyDeletedCategory+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = EDC4E3502C5D222D009286A2 /* RecentlyDeletedCategory+Core.h */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
|
@ -175,6 +178,9 @@
|
|||
9974CA2823DF1968003FE824 /* ElevationProfileData.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ElevationProfileData.mm; sourceTree = "<group>"; };
|
||||
9974CA2B23DF197B003FE824 /* ElevationProfileData+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ElevationProfileData+Core.h"; sourceTree = "<group>"; };
|
||||
AC6A585628057CC1003EABAF /* StringUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringUtils.h; sourceTree = "<group>"; };
|
||||
EDC4E34E2C5D222D009286A2 /* RecentlyDeletedCategory.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RecentlyDeletedCategory.mm; sourceTree = "<group>"; };
|
||||
EDC4E34F2C5D222D009286A2 /* RecentlyDeletedCategory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecentlyDeletedCategory.h; sourceTree = "<group>"; };
|
||||
EDC4E3502C5D222D009286A2 /* RecentlyDeletedCategory+Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RecentlyDeletedCategory+Core.h"; sourceTree = "<group>"; };
|
||||
FAA6D8CF29205D2D00E8D50D /* libcppjansson.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libcppjansson.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
|
@ -338,6 +344,7 @@
|
|||
47CA68E72506F61300671019 /* MWMTrack.mm */,
|
||||
47F0D2132516847F00BC685E /* MWMBookmarksSection.h */,
|
||||
47F0D2142516847F00BC685E /* MWMBookmarksSection.m */,
|
||||
EDC4E3542C5D2251009286A2 /* RecentlyDeletedCategory */,
|
||||
);
|
||||
path = Bookmarks;
|
||||
sourceTree = "<group>";
|
||||
|
@ -410,6 +417,16 @@
|
|||
path = ElevationProfile;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EDC4E3542C5D2251009286A2 /* RecentlyDeletedCategory */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EDC4E34F2C5D222D009286A2 /* RecentlyDeletedCategory.h */,
|
||||
EDC4E34E2C5D222D009286A2 /* RecentlyDeletedCategory.mm */,
|
||||
EDC4E3502C5D222D009286A2 /* RecentlyDeletedCategory+Core.h */,
|
||||
);
|
||||
path = RecentlyDeletedCategory;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
|
@ -452,6 +469,7 @@
|
|||
479F7063234FBC5900011E2E /* MWMCarPlayBookmarkObject.h in Headers */,
|
||||
47E8163723B188D3008FD836 /* MWMStorage.h in Headers */,
|
||||
47EEAFF62350CF48005CF316 /* AppInfo.h in Headers */,
|
||||
EDC4E3532C5D222D009286A2 /* RecentlyDeletedCategory+Core.h in Headers */,
|
||||
47F701F3238C877C00D18E95 /* PlacePageButtonsData+Core.h in Headers */,
|
||||
47CA68E12506C01F00671019 /* MWMBookmark+Core.h in Headers */,
|
||||
471527392491EDAA00E91BBA /* MWMBookmarkColor.h in Headers */,
|
||||
|
@ -459,6 +477,7 @@
|
|||
479F704A234F785B00011E2E /* MWMTypes.h in Headers */,
|
||||
47C637D72354AEBE00E12DE0 /* MWMMapOverlayManager.h in Headers */,
|
||||
47942D9C237D927800DEFAE3 /* PlacePageBookmarkData.h in Headers */,
|
||||
EDC4E3522C5D222D009286A2 /* RecentlyDeletedCategory.h in Headers */,
|
||||
47942D72237CC40B00DEFAE3 /* OpeningHours.h in Headers */,
|
||||
47942D6B237CC3D600DEFAE3 /* PlacePageData.h in Headers */,
|
||||
47D609DC234FE625008ECC47 /* MWMBookmarksObserver.h in Headers */,
|
||||
|
@ -567,6 +586,7 @@
|
|||
9940622123EAC57900493D1A /* ElevationHeightPoint.m in Sources */,
|
||||
47EEAFF42350CEDB005CF316 /* AppInfo.mm in Sources */,
|
||||
47E8163623B1889C008FD836 /* MWMStorage.mm in Sources */,
|
||||
EDC4E3512C5D222D009286A2 /* RecentlyDeletedCategory.mm in Sources */,
|
||||
47CA68DE2502022400671019 /* MWMBookmark.mm in Sources */,
|
||||
9957FAE9237AE5B000855F48 /* Logger.mm in Sources */,
|
||||
);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
@class MWMBookmarksSection;
|
||||
@class MWMCarPlayBookmarkObject;
|
||||
@class MWMTrack;
|
||||
@class RecentlyDeletedCategory;
|
||||
@class UIColor;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
@ -26,14 +27,18 @@ typedef void (^SearchBookmarksCompletionBlock)(NSArray<MWMBookmark *> *bookmarks
|
|||
typedef void (^SortBookmarksCompletionBlock)(NSArray<MWMBookmarksSection *> * _Nullable sortedSections);
|
||||
typedef void (^SharingResultCompletionHandler)(MWMBookmarksShareStatus status, NSURL * _Nullable urlToALocalFile);
|
||||
|
||||
@protocol RecentlyDeletedCategoriesManager <NSObject>
|
||||
- (uint64_t)recentlyDeletedCategoriesCount;
|
||||
- (NSArray<RecentlyDeletedCategory *> *)getRecentlyDeletedCategories;
|
||||
- (void)deleteRecentlyDeletedCategoryAtURLs:(NSArray<NSURL *> *)urls;
|
||||
- (void)recoverRecentlyDeletedCategoriesAtURLs:(NSArray<NSURL *> *)urls;
|
||||
@end
|
||||
|
||||
NS_SWIFT_NAME(BookmarksManager)
|
||||
@interface MWMBookmarksManager : NSObject
|
||||
@interface MWMBookmarksManager : NSObject<BookmarksObservable, RecentlyDeletedCategoriesManager>
|
||||
|
||||
+ (MWMBookmarksManager *)sharedManager;
|
||||
|
||||
- (void)addObserver:(id<MWMBookmarksObserver>)observer;
|
||||
- (void)removeObserver:(id<MWMBookmarksObserver>)observer;
|
||||
|
||||
- (BOOL)areBookmarksLoaded;
|
||||
- (void)loadBookmarks;
|
||||
- (void)loadBookmarkFile:(NSURL *)url;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#import "MWMBookmarkGroup.h"
|
||||
#import "MWMCarPlayBookmarkObject.h"
|
||||
#import "MWMTrack+Core.h"
|
||||
#import "RecentlyDeletedCategory+Core.h"
|
||||
|
||||
#include "Framework.h"
|
||||
|
||||
|
@ -355,7 +356,7 @@ static KmlFileType convertFileTypeToCore(MWMKmlFileType fileType) {
|
|||
|
||||
- (void)deleteCategory:(MWMMarkGroupID)groupId
|
||||
{
|
||||
self.bm.GetEditSession().DeleteBmCategory(groupId);
|
||||
self.bm.GetEditSession().DeleteBmCategory(groupId, false /* move to the Trash */);
|
||||
[self loopObservers:^(id<MWMBookmarksObserver> observer) {
|
||||
if ([observer respondsToSelector:@selector(onBookmarksCategoryDeleted:)])
|
||||
[observer onBookmarksCategoryDeleted:groupId];
|
||||
|
@ -787,6 +788,37 @@ static KmlFileType convertFileTypeToCore(MWMKmlFileType fileType) {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - RecentlyDeletedCategoriesManager
|
||||
- (uint64_t)recentlyDeletedCategoriesCount {
|
||||
return self.bm.GetRecentlyDeletedCategoriesCount();
|
||||
}
|
||||
|
||||
- (NSArray<RecentlyDeletedCategory *> *)getRecentlyDeletedCategories {
|
||||
auto const categoriesCollection = self.bm.GetRecentlyDeletedCategories();
|
||||
NSMutableArray<RecentlyDeletedCategory *> * recentlyDeletedCategories = [[NSMutableArray alloc] initWithCapacity:categoriesCollection->size()];
|
||||
|
||||
for (auto const & [filePath, categoryPtr] : * categoriesCollection) {
|
||||
ASSERT(categoryPtr, ("Recently deleted category shouldn't be nil."));
|
||||
RecentlyDeletedCategory * category = [[RecentlyDeletedCategory alloc] initWithCategoryData:categoryPtr->m_categoryData filePath:filePath];
|
||||
[recentlyDeletedCategories addObject:category];
|
||||
}
|
||||
return recentlyDeletedCategories;
|
||||
}
|
||||
|
||||
- (void)deleteRecentlyDeletedCategoryAtURLs:(NSArray<NSURL *> *)urls {
|
||||
std::vector<std::string> filePaths;
|
||||
for (NSURL * url in urls)
|
||||
filePaths.push_back(url.filePathURL.path.UTF8String);
|
||||
self.bm.DeleteRecentlyDeletedCategoriesAtPaths(filePaths);
|
||||
}
|
||||
|
||||
- (void)recoverRecentlyDeletedCategoriesAtURLs:(NSArray<NSURL *> *)urls {
|
||||
std::vector<std::string> filePaths;
|
||||
for (NSURL * url in urls)
|
||||
filePaths.push_back(url.filePathURL.path.UTF8String);
|
||||
self.bm.RecoverRecentlyDeletedCategoriesAtPaths(filePaths);
|
||||
}
|
||||
|
||||
#pragma mark - Helpers
|
||||
|
||||
- (void)loopObservers:(void (^)(id<MWMBookmarksObserver> observer))block
|
||||
|
|
|
@ -10,7 +10,13 @@ NS_SWIFT_NAME(BookmarksObserver)
|
|||
- (void)onBookmarksFileLoadSuccess;
|
||||
- (void)onBookmarksFileLoadError;
|
||||
- (void)onBookmarksCategoryDeleted:(MWMMarkGroupID)groupId;
|
||||
- (void)onRecentlyDeletedBookmarksCategoriesChanged;
|
||||
- (void)onBookmarkDeleted:(MWMMarkID)bookmarkId;
|
||||
|
||||
@end
|
||||
|
||||
@protocol BookmarksObservable<NSObject>
|
||||
- (void)addObserver:(id<MWMBookmarksObserver>)observer;
|
||||
- (void)removeObserver:(id<MWMBookmarksObserver>)observer;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
#import "RecentlyDeletedCategory.h"
|
||||
|
||||
#include "kml/types.hpp"
|
||||
|
||||
@interface RecentlyDeletedCategory (Core)
|
||||
|
||||
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath;
|
||||
|
||||
@end
|
|
@ -0,0 +1,15 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RecentlyDeletedCategory : NSObject
|
||||
|
||||
@property(nonatomic, readonly) NSString * title;
|
||||
@property(nonatomic, readonly) NSURL * fileURL;
|
||||
@property(nonatomic, readonly) NSDate * deletionDate;
|
||||
|
||||
- (instancetype)initTitle:(NSString *)title fileURL:(NSURL *)fileURL deletionDate:(NSDate *)deletionDate;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,35 @@
|
|||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
#import "RecentlyDeletedCategory+Core.h"
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
#include <map/bookmark_helpers.hpp>
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
#include <platform/platform_ios.h>
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
@implementation RecentlyDeletedCategory
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
- (instancetype)initTitle:(NSString *)title fileURL:(NSURL *)fileURL deletionDate:(NSDate *)deletionDate {
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
self = [super init];
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
if (self) {
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
_title = title;
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
_fileURL = fileURL;
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
_deletionDate = deletionDate;
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
}
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
return self;
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
}
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
@end
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
@implementation RecentlyDeletedCategory (Core)
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
self = [super init];
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
if (self) {
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
auto const name = GetPreferredBookmarkStr(data.m_name);
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
_title = [NSString stringWithCString:name.c_str() encoding:NSUTF8StringEncoding];
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
auto const pathString = [NSString stringWithCString:filePath.c_str() encoding:NSUTF8StringEncoding];
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
_fileURL = [NSURL fileURLWithPath:pathString];
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
NSTimeInterval creationTime = Platform::GetFileCreationTime(filePath);
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
_deletionDate = [NSDate dateWithTimeIntervalSince1970:creationTime];
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
}
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
return self;
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
}
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
||||
@end
|
||||
![]() nit: avoid unnecessary copy
nit: avoid unnecessary copy
```suggestion
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
```
|
|
@ -26,6 +26,7 @@ FOUNDATION_EXPORT const unsigned char CoreApiVersionString[];
|
|||
#import <CoreApi/MWMStorage.h>
|
||||
#import <CoreApi/MWMTrack.h>
|
||||
#import <CoreApi/MWMTypes.h>
|
||||
#import <CoreApi/RecentlyDeletedCategory.h>
|
||||
|
||||
#pragma mark - Place Page
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
enum BMCSection {
|
||||
case categories
|
||||
case actions
|
||||
case recentlyDeleted
|
||||
case notifications
|
||||
}
|
||||
|
||||
|
@ -10,6 +11,7 @@ enum BMCAction: BMCModel {
|
|||
case create
|
||||
case exportAll
|
||||
case `import`
|
||||
case recentlyDeleted(Int)
|
||||
}
|
||||
|
||||
extension BMCAction {
|
||||
|
@ -21,6 +23,8 @@ extension BMCAction {
|
|||
return L("bookmarks_export")
|
||||
case .import:
|
||||
return L("bookmarks_import")
|
||||
case .recentlyDeleted(let count):
|
||||
return L("bookmarks_recently_deleted") + " (\(count))"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,6 +36,8 @@ extension BMCAction {
|
|||
return UIImage(named: "ic24PxShare")!
|
||||
case .import:
|
||||
return UIImage(named: "ic24PxImport")!
|
||||
case .recentlyDeleted:
|
||||
return UIImage(named: "ic_route_manager_trash_open")!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,12 +40,8 @@ final class BMCViewController: MWMViewController {
|
|||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
viewModel.reloadData()
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
viewModel.addToObserverList()
|
||||
viewModel.reloadData()
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
|
@ -157,13 +153,18 @@ final class BMCViewController: MWMViewController {
|
|||
let deleteAction = UIAlertAction(title: delete, style: .destructive, handler: { [viewModel] _ in
|
||||
viewModel!.deleteCategory(at: index)
|
||||
})
|
||||
deleteAction.isEnabled = (viewModel.numberOfRows(section: .categories) > 1)
|
||||
deleteAction.isEnabled = (viewModel.canDeleteCategory())
|
||||
actionSheet.addAction(deleteAction)
|
||||
let cancel = L("cancel")
|
||||
actionSheet.addAction(UIAlertAction(title: cancel, style: .cancel, handler: nil))
|
||||
|
||||
present(actionSheet, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
private func openRecentlyDeleted() {
|
||||
let recentlyDeletedController = RecentlyDeletedCategoriesViewController(viewModel: RecentlyDeletedCategoriesViewModel(bookmarksManager: BookmarksManager.shared()))
|
||||
MapViewController.topViewController().navigationController?.pushViewController(recentlyDeletedController, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
extension BMCViewController: BMCView {
|
||||
|
@ -201,7 +202,7 @@ extension BMCViewController: UITableViewDataSource {
|
|||
func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
switch viewModel.sectionType(section: section) {
|
||||
case .categories: fallthrough
|
||||
case .actions: fallthrough
|
||||
case .actions, .recentlyDeleted: fallthrough
|
||||
case .notifications: return viewModel.numberOfRows(section: section)
|
||||
}
|
||||
}
|
||||
|
@ -217,6 +218,8 @@ extension BMCViewController: UITableViewDataSource {
|
|||
delegate: self)
|
||||
case .actions:
|
||||
return dequeCell(BMCActionsCell.self).config(model: viewModel.action(at: indexPath.row))
|
||||
case .recentlyDeleted:
|
||||
return dequeCell(BMCActionsCell.self).config(model: viewModel.recentlyDeletedCategories())
|
||||
case .notifications:
|
||||
return dequeCell(BMCNotificationsCell.self)
|
||||
}
|
||||
|
@ -229,7 +232,7 @@ extension BMCViewController: UITableViewDelegate {
|
|||
return false
|
||||
}
|
||||
|
||||
return viewModel.numberOfRows(section: .categories) > 1
|
||||
return viewModel.canDeleteCategory()
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView,
|
||||
|
@ -248,7 +251,7 @@ extension BMCViewController: UITableViewDelegate {
|
|||
switch viewModel.sectionType(section: section) {
|
||||
case .notifications: fallthrough
|
||||
case .categories: return 48
|
||||
case .actions: return 24
|
||||
case .actions, .recentlyDeleted: return 24
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -260,7 +263,7 @@ extension BMCViewController: UITableViewDelegate {
|
|||
categoriesHeader.title = L("bookmark_lists")
|
||||
categoriesHeader.delegate = self
|
||||
return categoriesHeader
|
||||
case .actions: return actionsHeader
|
||||
case .actions, .recentlyDeleted: return actionsHeader
|
||||
case .notifications: return notificationsHeader
|
||||
}
|
||||
}
|
||||
|
@ -275,7 +278,10 @@ extension BMCViewController: UITableViewDelegate {
|
|||
case .create: createNewCategory()
|
||||
case .exportAll: shareAllCategories(anchor: tableView.cellForRow(at: indexPath))
|
||||
case .import: showImportDialog()
|
||||
default:
|
||||
assertionFailure()
|
||||
}
|
||||
case .recentlyDeleted: openRecentlyDeleted()
|
||||
default:
|
||||
assertionFailure()
|
||||
}
|
||||
|
|
|
@ -27,74 +27,89 @@ final class BMCDefaultViewModel: NSObject {
|
|||
reloadData()
|
||||
}
|
||||
|
||||
private func setCategories() {
|
||||
categories = manager.sortedUserCategories()
|
||||
private func getCategories() -> [BookmarkGroup] {
|
||||
manager.sortedUserCategories()
|
||||
}
|
||||
|
||||
private func setActions() {
|
||||
actions = [.create]
|
||||
private func getActions() -> [BMCAction] {
|
||||
var actions: [BMCAction] = [.create]
|
||||
actions.append(.import)
|
||||
if !manager.areAllCategoriesEmpty() {
|
||||
actions.append(.exportAll)
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
private func setNotifications() {
|
||||
notifications = [.load]
|
||||
private func getNotifications() -> [BMCNotification] {
|
||||
[.load]
|
||||
}
|
||||
|
||||
func reloadData() {
|
||||
sections = []
|
||||
sections.removeAll()
|
||||
|
||||
if manager.areBookmarksLoaded() {
|
||||
sections.append(.categories)
|
||||
setCategories()
|
||||
categories = getCategories()
|
||||
|
||||
sections.append(.actions)
|
||||
setActions()
|
||||
actions = getActions()
|
||||
|
||||
if manager.recentlyDeletedCategoriesCount() != .zero {
|
||||
sections.append(.recentlyDeleted)
|
||||
}
|
||||
} else {
|
||||
sections.append(.notifications)
|
||||
setNotifications()
|
||||
notifications = getNotifications()
|
||||
}
|
||||
|
||||
view?.update(sections: [])
|
||||
}
|
||||
}
|
||||
|
||||
extension BMCDefaultViewModel {
|
||||
func numberOfSections() -> Int {
|
||||
return sections.count
|
||||
sections.count
|
||||
}
|
||||
|
||||
func sectionType(section: Int) -> BMCSection {
|
||||
return sections[section]
|
||||
sections[section]
|
||||
}
|
||||
|
||||
func sectionIndex(section: BMCSection) -> Int {
|
||||
return sections.firstIndex(of: section)!
|
||||
sections.firstIndex(of: section)!
|
||||
}
|
||||
|
||||
func numberOfRows(section: Int) -> Int {
|
||||
return numberOfRows(section: sectionType(section: section))
|
||||
numberOfRows(section: sectionType(section: section))
|
||||
}
|
||||
|
||||
func numberOfRows(section: BMCSection) -> Int {
|
||||
switch section {
|
||||
case .categories: return categories.count
|
||||
case .actions: return actions.count
|
||||
case .recentlyDeleted: return 1
|
||||
case .notifications: return notifications.count
|
||||
}
|
||||
}
|
||||
|
||||
func category(at index: Int) -> BookmarkGroup {
|
||||
return categories[index]
|
||||
categories[index]
|
||||
}
|
||||
|
||||
func canDeleteCategory() -> Bool {
|
||||
categories.count > 1
|
||||
}
|
||||
|
||||
func action(at index: Int) -> BMCAction {
|
||||
return actions[index]
|
||||
actions[index]
|
||||
}
|
||||
|
||||
func recentlyDeletedCategories() -> BMCAction {
|
||||
.recentlyDeleted(Int(manager.recentlyDeletedCategoriesCount()))
|
||||
}
|
||||
|
||||
func notification(at index: Int) -> BMCNotification {
|
||||
return notifications[index]
|
||||
notifications[index]
|
||||
}
|
||||
|
||||
func areAllCategoriesHidden() -> Bool {
|
||||
|
@ -130,7 +145,7 @@ extension BMCDefaultViewModel {
|
|||
}
|
||||
|
||||
func checkCategory(name: String) -> Bool {
|
||||
return manager.checkCategoryName(name)
|
||||
manager.checkCategoryName(name)
|
||||
}
|
||||
|
||||
func shareCategoryFile(at index: Int, fileType: KmlFileType, handler: @escaping SharingResultCompletionHandler) {
|
||||
|
@ -164,12 +179,11 @@ extension BMCDefaultViewModel {
|
|||
}
|
||||
|
||||
func areNotificationsEnabled() -> Bool {
|
||||
return manager.areNotificationsEnabled()
|
||||
manager.areNotificationsEnabled()
|
||||
}
|
||||
}
|
||||
|
||||
extension BMCDefaultViewModel: BookmarksObserver {
|
||||
|
||||
func onBookmarksLoadFinished() {
|
||||
reloadData()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
final class RecentlyDeletedCategoriesViewController: MWMViewController {
|
||||
|
||||
private enum LocalizedStrings {
|
||||
static let clear = L("clear")
|
||||
static let delete = L("delete")
|
||||
static let deleteAll = L("delete_all")
|
||||
static let recover = L("recover")
|
||||
static let recoverAll = L("recover_all")
|
||||
static let recentlyDeleted = L("bookmarks_recently_deleted")
|
||||
static let searchInTheList = L("search_in_the_list")
|
||||
}
|
||||
|
||||
private let tableView = UITableView(frame: .zero, style: .plain)
|
||||
|
||||
private lazy var clearButton = UIBarButtonItem(title: LocalizedStrings.clear, style: .done, target: self, action: #selector(clearButtonDidTap))
|
||||
private lazy var recoverButton = UIBarButtonItem(title: LocalizedStrings.recover, style: .done, target: self, action: #selector(recoverButtonDidTap))
|
||||
private lazy var deleteButton = UIBarButtonItem(title: LocalizedStrings.delete, style: .done, target: self, action: #selector(deleteButtonDidTap))
|
||||
private let searchController = UISearchController(searchResultsController: nil)
|
||||
private let viewModel: RecentlyDeletedCategoriesViewModel
|
||||
|
||||
init(viewModel: RecentlyDeletedCategoriesViewModel = RecentlyDeletedCategoriesViewModel(bookmarksManager: BookmarksManager.shared())) {
|
||||
self.viewModel = viewModel
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
viewModel.stateDidChange = { [weak self] state in
|
||||
self?.updateState(state)
|
||||
}
|
||||
viewModel.filteredDataSourceDidChange = { [weak self] dataSource in
|
||||
guard let self else { return }
|
||||
if dataSource.isEmpty {
|
||||
self.tableView.reloadData()
|
||||
} else {
|
||||
let indexes = IndexSet(integersIn: 0...dataSource.count - 1)
|
||||
self.tableView.update { self.tableView.reloadSections(indexes, with: .automatic) }
|
||||
}
|
||||
}
|
||||
viewModel.onCategoriesIsEmpty = { [weak self] in
|
||||
self?.goBack()
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setupView()
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
navigationController?.setToolbarHidden(true, animated: true)
|
||||
}
|
||||
|
||||
private func setupView() {
|
||||
extendedLayoutIncludesOpaqueBars = true
|
||||
setupNavigationBar()
|
||||
setupToolBar()
|
||||
setupSearchBar()
|
||||
setupTableView()
|
||||
layout()
|
||||
updateState(viewModel.state)
|
||||
}
|
||||
|
||||
private func setupNavigationBar() {
|
||||
title = LocalizedStrings.recentlyDeleted
|
||||
}
|
||||
|
||||
private func setupToolBar() {
|
||||
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
|
||||
toolbarItems = [flexibleSpace, recoverButton, flexibleSpace, deleteButton, flexibleSpace]
|
||||
navigationController?.isToolbarHidden = false
|
||||
}
|
||||
|
||||
private func setupSearchBar() {
|
||||
searchController.searchBar.placeholder = LocalizedStrings.searchInTheList
|
||||
searchController.obscuresBackgroundDuringPresentation = false
|
||||
searchController.hidesNavigationBarDuringPresentation = false
|
||||
searchController.searchBar.delegate = self
|
||||
searchController.searchBar.applyTheme()
|
||||
navigationItem.searchController = searchController
|
||||
navigationItem.hidesSearchBarWhenScrolling = true
|
||||
}
|
||||
|
||||
private func setupTableView() {
|
||||
tableView.styleName = "TableView:PressBackground";
|
||||
tableView.allowsMultipleSelectionDuringEditing = true
|
||||
tableView.register(cell: RecentlyDeletedTableViewCell.self)
|
||||
tableView.setEditing(true, animated: false)
|
||||
tableView.translatesAutoresizingMaskIntoConstraints = false
|
||||
tableView.dataSource = self
|
||||
tableView.delegate = self
|
||||
}
|
||||
|
||||
private func layout() {
|
||||
view.addSubview(tableView)
|
||||
NSLayoutConstraint.activate([
|
||||
tableView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
|
||||
])
|
||||
}
|
||||
|
||||
private func updateState(_ state: RecentlyDeletedCategoriesViewModel.State) {
|
||||
switch state {
|
||||
case .searching:
|
||||
navigationController?.setToolbarHidden(true, animated: false)
|
||||
searchController.searchBar.isUserInteractionEnabled = true
|
||||
case .nothingSelected:
|
||||
navigationController?.setToolbarHidden(false, animated: false)
|
||||
recoverButton.title = LocalizedStrings.recoverAll
|
||||
deleteButton.title = LocalizedStrings.deleteAll
|
||||
searchController.searchBar.isUserInteractionEnabled = true
|
||||
navigationItem.rightBarButtonItem = nil
|
||||
tableView.indexPathsForSelectedRows?.forEach { tableView.deselectRow(at: $0, animated: true)}
|
||||
case .someSelected:
|
||||
navigationController?.setToolbarHidden(false, animated: false)
|
||||
recoverButton.title = LocalizedStrings.recover
|
||||
deleteButton.title = LocalizedStrings.delete
|
||||
searchController.searchBar.isUserInteractionEnabled = false
|
||||
navigationItem.rightBarButtonItem = clearButton
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
@objc private func clearButtonDidTap() {
|
||||
viewModel.cancelSelecting()
|
||||
}
|
||||
|
||||
@objc private func recoverButtonDidTap() {
|
||||
viewModel.recoverSelectedCategories()
|
||||
}
|
||||
|
||||
@objc private func deleteButtonDidTap() {
|
||||
viewModel.deleteSelectedCategories()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
extension RecentlyDeletedCategoriesViewController: UITableViewDataSource {
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
viewModel.filteredDataSource.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
viewModel.filteredDataSource[section].content.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(cell: RecentlyDeletedTableViewCell.self, indexPath: indexPath)
|
||||
let category = viewModel.filteredDataSource[indexPath.section].content[indexPath.row]
|
||||
cell.configureWith(RecentlyDeletedTableViewCell.ViewModel(category))
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
extension RecentlyDeletedCategoriesViewController: UITableViewDelegate {
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
guard tableView.isEditing else {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
return
|
||||
}
|
||||
viewModel.selectCategory(at: indexPath)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
|
||||
guard tableView.isEditing else { return }
|
||||
guard let selectedIndexPaths = tableView.indexPathsForSelectedRows, !selectedIndexPaths.isEmpty else {
|
||||
viewModel.deselectAllCategories()
|
||||
return
|
||||
}
|
||||
viewModel.deselectCategory(at: indexPath)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
||||
let deleteAction = UIContextualAction(style: .destructive, title: LocalizedStrings.delete) { [weak self] (_, _, completion) in
|
||||
self?.viewModel.deleteCategory(at: indexPath)
|
||||
completion(true)
|
||||
}
|
||||
let recoverAction = UIContextualAction(style: .normal, title: LocalizedStrings.recover) { [weak self] (_, _, completion) in
|
||||
self?.viewModel.recoverCategory(at: indexPath)
|
||||
completion(true)
|
||||
}
|
||||
return UISwipeActionsConfiguration(actions: [deleteAction, recoverAction])
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UISearchBarDelegate
|
||||
extension RecentlyDeletedCategoriesViewController: UISearchBarDelegate {
|
||||
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
|
||||
searchBar.setShowsCancelButton(true, animated: true)
|
||||
viewModel.startSearching()
|
||||
}
|
||||
|
||||
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
|
||||
searchBar.setShowsCancelButton(false, animated: true)
|
||||
}
|
||||
|
||||
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
|
||||
searchBar.text = nil
|
||||
searchBar.resignFirstResponder()
|
||||
viewModel.cancelSearching()
|
||||
}
|
||||
|
||||
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
|
||||
viewModel.search(searchText)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
final class RecentlyDeletedCategoriesViewModel: NSObject {
|
||||
|
||||
typealias BookmarksManager = RecentlyDeletedCategoriesManager & BookmarksObservable
|
||||
|
||||
enum Section: CaseIterable {
|
||||
struct Model: Equatable {
|
||||
var content: [RecentlyDeletedCategory]
|
||||
}
|
||||
|
||||
case main
|
||||
}
|
||||
|
||||
enum State {
|
||||
case searching
|
||||
case nothingSelected
|
||||
case someSelected
|
||||
}
|
||||
|
||||
private var recentlyDeletedCategoriesManager: BookmarksManager
|
||||
private var dataSource: [Section.Model] = [] {
|
||||
didSet {
|
||||
if dataSource.isEmpty {
|
||||
onCategoriesIsEmpty?()
|
||||
}
|
||||
}
|
||||
}
|
||||
private(set) var state: State = .nothingSelected
|
||||
private(set) var filteredDataSource: [Section.Model] = []
|
||||
private(set) var selectedIndexPaths: [IndexPath] = []
|
||||
private(set) var searchText = String()
|
||||
|
||||
var stateDidChange: ((State) -> Void)?
|
||||
var filteredDataSourceDidChange: (([Section.Model]) -> Void)?
|
||||
var onCategoriesIsEmpty: (() -> Void)?
|
||||
|
||||
init(bookmarksManager: BookmarksManager) {
|
||||
self.recentlyDeletedCategoriesManager = bookmarksManager
|
||||
super.init()
|
||||
subscribeOnBookmarksManagerNotifications()
|
||||
fetchRecentlyDeletedCategories()
|
||||
}
|
||||
|
||||
deinit {
|
||||
unsubscribeFromBookmarksManagerNotifications()
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
private func subscribeOnBookmarksManagerNotifications() {
|
||||
recentlyDeletedCategoriesManager.add(self)
|
||||
}
|
||||
|
||||
private func unsubscribeFromBookmarksManagerNotifications() {
|
||||
recentlyDeletedCategoriesManager.remove(self)
|
||||
}
|
||||
|
||||
private func updateState(to newState: State) {
|
||||
guard state != newState else { return }
|
||||
state = newState
|
||||
stateDidChange?(state)
|
||||
}
|
||||
|
||||
private func updateFilteredDataSource(_ dataSource: [Section.Model]) {
|
||||
filteredDataSource = dataSource.filtered(using: searchText)
|
||||
filteredDataSourceDidChange?(filteredDataSource)
|
||||
}
|
||||
|
||||
private func updateSelectionAtIndexPath(_ indexPath: IndexPath, isSelected: Bool) {
|
||||
if isSelected {
|
||||
updateState(to: .someSelected)
|
||||
} else {
|
||||
let allDeselected = dataSource.allSatisfy { $0.content.isEmpty }
|
||||
updateState(to: allDeselected ? .nothingSelected : .someSelected)
|
||||
}
|
||||
}
|
||||
|
||||
private func removeCategories(at indexPaths: [IndexPath], completion: ([URL]) -> Void) {
|
||||
var fileToRemoveURLs: [URL]
|
||||
if indexPaths.isEmpty {
|
||||
// Remove all without selection.
|
||||
fileToRemoveURLs = dataSource.flatMap { $0.content.map { $0.fileURL } }
|
||||
dataSource.removeAll()
|
||||
} else {
|
||||
fileToRemoveURLs = [URL]()
|
||||
indexPaths.forEach { [weak self] indexPath in
|
||||
guard let self else { return }
|
||||
let fileToRemoveURL = self.filteredDataSource[indexPath.section].content[indexPath.row].fileURL
|
||||
self.dataSource[indexPath.section].content.removeAll { $0.fileURL == fileToRemoveURL }
|
||||
fileToRemoveURLs.append(fileToRemoveURL)
|
||||
}
|
||||
}
|
||||
updateFilteredDataSource(dataSource)
|
||||
updateState(to: .nothingSelected)
|
||||
completion(fileToRemoveURLs)
|
||||
}
|
||||
|
||||
private func removeSelectedCategories(completion: ([URL]) -> Void) {
|
||||
let removeAll = selectedIndexPaths.isEmpty || selectedIndexPaths.count == dataSource.flatMap({ $0.content }).count
|
||||
removeCategories(at: removeAll ? [] : selectedIndexPaths, completion: completion)
|
||||
selectedIndexPaths.removeAll()
|
||||
updateState(to: .nothingSelected)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
extension RecentlyDeletedCategoriesViewModel {
|
||||
func fetchRecentlyDeletedCategories() {
|
||||
let categories = recentlyDeletedCategoriesManager.getRecentlyDeletedCategories()
|
||||
guard !categories.isEmpty else { return }
|
||||
dataSource = [Section.Model(content: categories)]
|
||||
updateFilteredDataSource(dataSource)
|
||||
}
|
||||
|
||||
func deleteCategory(at indexPath: IndexPath) {
|
||||
removeCategories(at: [indexPath]) { recentlyDeletedCategoriesManager.deleteRecentlyDeletedCategory(at: $0) }
|
||||
}
|
||||
|
||||
func deleteSelectedCategories() {
|
||||
removeSelectedCategories { recentlyDeletedCategoriesManager.deleteRecentlyDeletedCategory(at: $0) }
|
||||
}
|
||||
|
||||
func recoverCategory(at indexPath: IndexPath) {
|
||||
removeCategories(at: [indexPath]) { recentlyDeletedCategoriesManager.recoverRecentlyDeletedCategories(at: $0) }
|
||||
}
|
||||
|
||||
func recoverSelectedCategories() {
|
||||
removeSelectedCategories { recentlyDeletedCategoriesManager.recoverRecentlyDeletedCategories(at: $0) }
|
||||
}
|
||||
|
||||
func startSelecting() {
|
||||
updateState(to: .nothingSelected)
|
||||
}
|
||||
|
||||
func selectCategory(at indexPath: IndexPath) {
|
||||
selectedIndexPaths.append(indexPath)
|
||||
updateState(to: .someSelected)
|
||||
}
|
||||
|
||||
func deselectCategory(at indexPath: IndexPath) {
|
||||
selectedIndexPaths.removeAll { $0 == indexPath }
|
||||
if selectedIndexPaths.isEmpty {
|
||||
updateState(to: state == .searching ? .searching : .nothingSelected)
|
||||
}
|
||||
}
|
||||
|
||||
func selectAllCategories() {
|
||||
selectedIndexPaths = dataSource.enumerated().flatMap { sectionIndex, section in
|
||||
section.content.indices.map { IndexPath(row: $0, section: sectionIndex) }
|
||||
}
|
||||
updateState(to: .someSelected)
|
||||
}
|
||||
|
||||
func deselectAllCategories() {
|
||||
selectedIndexPaths.removeAll()
|
||||
updateState(to: state == .searching ? .searching : .nothingSelected)
|
||||
}
|
||||
|
||||
func cancelSelecting() {
|
||||
selectedIndexPaths.removeAll()
|
||||
updateState(to: .nothingSelected)
|
||||
}
|
||||
|
||||
func startSearching() {
|
||||
updateState(to: .searching)
|
||||
}
|
||||
|
||||
func cancelSearching() {
|
||||
searchText.removeAll()
|
||||
selectedIndexPaths.removeAll()
|
||||
updateFilteredDataSource(dataSource)
|
||||
updateState(to: .nothingSelected)
|
||||
}
|
||||
|
||||
func search(_ searchText: String) {
|
||||
updateState(to: .searching)
|
||||
guard !searchText.isEmpty else {
|
||||
cancelSearching()
|
||||
return
|
||||
}
|
||||
self.searchText = searchText
|
||||
updateFilteredDataSource(dataSource)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - BookmarksObserver
|
||||
|
||||
extension RecentlyDeletedCategoriesViewModel: BookmarksObserver {
|
||||
func onBookmarksLoadFinished() {
|
||||
fetchRecentlyDeletedCategories()
|
||||
}
|
||||
|
||||
func onRecentlyDeletedBookmarksCategoriesChanged() {
|
||||
fetchRecentlyDeletedCategories()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private extension Array where Element == RecentlyDeletedCategoriesViewModel.Section.Model {
|
||||
func filtered(using searchText: String) -> [Element] {
|
||||
let filteredArray = map { section in
|
||||
let filteredContent = section.content.filter {
|
||||
guard !searchText.isEmpty else { return true }
|
||||
return $0.title.localizedCaseInsensitiveContains(searchText)
|
||||
}
|
||||
return RecentlyDeletedCategoriesViewModel.Section.Model(content: filteredContent)
|
||||
}
|
||||
return filteredArray
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
final class RecentlyDeletedTableViewCell: UITableViewCell {
|
||||
|
||||
struct ViewModel: Equatable, Hashable {
|
||||
let fileName: String
|
||||
let fileURL: URL
|
||||
let deletionDate: Date
|
||||
}
|
||||
|
||||
private static let dateFormatter: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateStyle = .medium
|
||||
formatter.timeStyle = .medium
|
||||
return formatter
|
||||
}()
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func configureWith(_ viewModel: ViewModel) {
|
||||
textLabel?.text = viewModel.fileName
|
||||
detailTextLabel?.text = Self.dateFormatter.string(from: viewModel.deletionDate)
|
||||
}
|
||||
}
|
||||
|
||||
extension RecentlyDeletedTableViewCell.ViewModel {
|
||||
init(_ category: RecentlyDeletedCategory) {
|
||||
self.fileName = category.title
|
||||
self.fileURL = category.fileURL
|
||||
self.deletionDate = category.deletionDate
|
||||
}
|
||||
}
|
|
@ -1,12 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_route_manager_trash_open.pdf"
|
||||
"filename" : "ic_route_manager_trash_open.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "مقاعد خارجية";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "القوائم المحذوفة مؤخراً";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "مسح";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "حذف الكل";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "استرداد";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "استرداد الكل";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Çöldə oturma";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Son Silinmiş Siyahılar";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Sil";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Hamısını Sil";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Bərpa et";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Hamısını bərpa et";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Месцы на адкрытым паветры";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Нядаўна выдаленыя спісы";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Ачысціць";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Выдаліць усё";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Аднавіць";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Аднавіць усё";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Места за сядане на открито";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Наскоро изтрити списъци";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Изчистване";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Изтриване на всички";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Възстановяване";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Възстанови всички";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Seients a l'aire lliure";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Llistes suprimides recentment";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Netejar";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Eliminar tots";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Recuperar";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Recuperar-ho tot";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Venkovní posezení";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Nedávno odstraněné seznamy";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Vymazat";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Smazat vše";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Obnovit";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Obnovit vše";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Udendørs siddepladser";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Nyligt slettede lister";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Ryd";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Slet alle";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Gendan";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Gendan alle";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Sitzplätze im Freien";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Kürzlich gelöschte Listen";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Löschen";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Alle löschen";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Wiederherstellen";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Alles wiederherstellen";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Εξωτερικά καθίσματα";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Πρόσφατα διαγραμμένες λίστες";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Εκκαθάριση";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Διαγραφή όλων";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Ανάκτηση";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Ανάκτηση όλων";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Outdoor seating";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Recently Deleted Lists";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Clear";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Delete All";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Recover";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Recover All";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Outdoor seating";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Recently Deleted Lists";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Clear";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Delete All";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Recover";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Recover All";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Asientos al aire libre";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Listas eliminadas recientemente";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Limpiar";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Borrar todo";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Recuperar";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Recuperar todo";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Asientos al aire libre";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Listas eliminadas recientemente";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Limpiar";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Borrar todo";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Recuperar";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Recuperar todo";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Istekohad õues";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Hiljuti kustutatud nimekirjad";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Kustuta";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Kustuta kõik";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Taasta";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Taasta kõik";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Kanpoko eserlekuak";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Duela gutxi ezabatutako zerrendak";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Garbitu";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Ezabatu guztiak";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Berreskuratu";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Guztia berreskuratu";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "نشستن در فضای باز";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "لیست های اخیرا حذف شده";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "پاک کردن";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "حذف همه";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "بازیابی";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "بازیابی همه";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Ulkona istuminen";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Äskettäin poistetut luettelot";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Tyhjennä";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Poista kaikki";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Palauta";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Palauta kaikki";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Places en terrasse";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Listes récemment supprimées";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Effacer";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Supprimer tout";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Récupérer";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Tout récupérer";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "ישיבה בחוץ";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "רשימות שנמחקו לאחרונה";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "נקה";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "מחק הכל";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "לשחזר";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "לשחזר הכל";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "घर के बाहर बैठने";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "हाल ही में हटाई गई सूचियाँ";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "साफ करें";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "सभी हटा दो";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "पुनः प्राप्त करें";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "सभी पुनः प्राप्त करें";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Kültéri ülőhelyek";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Nemrég törölt listák";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Törlés";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Mindent törölni";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Visszaállítás";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Mindent visszaállít";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Tempat duduk di luar ruangan";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Daftar yang Baru Saja Dihapus";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Hapus";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Hapus Semua";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Pulihkan";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Pulihkan Semua";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Posti a sedere all'aperto";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Elenchi eliminati di recente";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Cancella";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Cancella tutto";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Recupera";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Recupera tutto";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "屋外席";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "最近削除されたリスト";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "クリア";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "すべて削除する";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "復元";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "すべて復元";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "야외 좌석";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "최근 삭제한 목록";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "지우기";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "모두 삭제";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "복구";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "모두 복구";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "बाहेरची आसनव्यवस्था nb = Uteservering";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "अलीकडे हटवलेल्या याद्या";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "साफ करा";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "सर्व हटवा";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "पुनर्प्राप्त करा";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "सर्व पुनर्प्राप्त करा";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Outdoor seating";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Nylig slettede lister";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Fjern";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Slett alle";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Gjenopprette";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Gjenopprett alt";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Zitplaatsen buiten";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Onlangs verwijderde lijsten";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Wissen";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Alles verwijderen";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Herstel";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Alles herstellen";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Siedzenia na zewnątrz";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Ostatnio usunięte listy";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Wyczyść";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Usuń wszystko";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Odzyskaj";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Odzyskaj wszystko";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Assentos ao ar livre";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Listas excluídas recentemente";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Limpar";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Excluir tudo";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Recuperar";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Recuperar tudo";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Assentos ao ar livre";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Listas eliminadas recentemente";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Limpar";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Eliminar tudo";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Recuperar";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Recuperar tudo";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Scaune în aer liber";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Liste șterse recent";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Șterge";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Ștergeți toate";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Recuperare";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Recuperează tot";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Сидения на открытом воздухе";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Недавно удаленные списки";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Очистить";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Удалить всё";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Восстановить";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Восстановить всё";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Vonkajšie sedenie";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Nedávno odstránené zoznamy";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Vymazať";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Odstrániť všetko";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Obnoviť";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Obnoviť všetko";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Sittplatser utomhus";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Nyligen borttagna listor";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Rensa";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Radera alla";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Återställ";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Återställ allt";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Viti vya nje";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Orodha Zilizofutwa Hivi Karibuni";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Futa";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Futa Zote";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Rejesha";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Rejesha yote";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "ที่นั่งกลางแจ้ง";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "รายการที่ถูกลบล่าสุด";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "ล้าง";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "ลบทั้งหมด";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "กู้คืน";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "กู้คืนทั้งหมด";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Açık oturma alanı";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Son Silinen Listeler";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Temizle";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Tümünü Sil";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Kurtar";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Hepsini Kurtar";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Сидіння на відкритому повітрі";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Нещодавно видалені списки";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Очистити";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Видалити все";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Відновити";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Відновити все";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "Chỗ ngồi ngoài trời";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "Danh sách đã xóa gần đây";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "Xóa";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "Xóa hết";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "Khôi phục";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "Khôi phục tất cả";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "室外座位";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "最近删除的列表";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "清除";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "全部删除";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "恢复";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "全部恢复";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -1376,6 +1376,21 @@
|
|||
/* To indicate if restaurant or other place has outdoor seating */
|
||||
"outdoor_seating" = "戶外座位";
|
||||
|
||||
/* Title for the "Recently Deleted Lists" button on the Bookmarks and Lists screen. */
|
||||
"bookmarks_recently_deleted" = "最近刪除的列表";
|
||||
|
||||
/* Title for the "Clear" button on the Recently Deleted Lists screen. */
|
||||
"clear" = "清除";
|
||||
|
||||
/* Title for the "Delete All" button on the Recently Deleted Lists screen. */
|
||||
"delete_all" = "刪除所有";
|
||||
|
||||
/* Title for the "Recover" button on the Recently Deleted Lists screen. */
|
||||
"recover" = "恢復";
|
||||
|
||||
/* Title for the "Recover All" button on the Recently Deleted Lists screen. */
|
||||
"recover_all" = "全部恢復";
|
||||
|
||||
|
||||
/********** Types **********/
|
||||
|
||||
|
|
|
@ -493,6 +493,11 @@
|
|||
EDBD68072B625724005DD151 /* LocationServicesDisabledAlert.xib in Resources */ = {isa = PBXBuildFile; fileRef = EDBD68062B625724005DD151 /* LocationServicesDisabledAlert.xib */; };
|
||||
EDBD680B2B62572E005DD151 /* LocationServicesDisabledAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDBD680A2B62572E005DD151 /* LocationServicesDisabledAlert.swift */; };
|
||||
EDC3573B2B7B5029001AE9CA /* CALayer+SetCorner.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC3573A2B7B5029001AE9CA /* CALayer+SetCorner.swift */; };
|
||||
EDC4E34B2C5D1BEF009286A2 /* RecentlyDeletedCategoriesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC4E3472C5D1BEF009286A2 /* RecentlyDeletedCategoriesViewController.swift */; };
|
||||
EDC4E34C2C5D1BEF009286A2 /* RecentlyDeletedCategoriesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC4E3482C5D1BEF009286A2 /* RecentlyDeletedCategoriesViewModel.swift */; };
|
||||
EDC4E34D2C5D1BEF009286A2 /* RecentlyDeletedTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC4E3492C5D1BEF009286A2 /* RecentlyDeletedTableViewCell.swift */; };
|
||||
EDC4E3612C5E2576009286A2 /* RecentlyDeletedCategoriesViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC4E3412C5D1BD3009286A2 /* RecentlyDeletedCategoriesViewModelTests.swift */; };
|
||||
EDC4E3692C5E6F5B009286A2 /* MockRecentlyDeletedCategoriesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC4E3402C5D1BD3009286A2 /* MockRecentlyDeletedCategoriesManager.swift */; };
|
||||
EDE243DD2B6D2E640057369B /* AboutController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDE243D52B6CF3980057369B /* AboutController.swift */; };
|
||||
EDE243E52B6D3F400057369B /* OSMView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDE243E42B6D3F400057369B /* OSMView.swift */; };
|
||||
EDE243E72B6D55610057369B /* InfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDE243E02B6D3EA00057369B /* InfoView.swift */; };
|
||||
|
@ -1415,6 +1420,11 @@
|
|||
EDBD68062B625724005DD151 /* LocationServicesDisabledAlert.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = LocationServicesDisabledAlert.xib; sourceTree = "<group>"; };
|
||||
EDBD680A2B62572E005DD151 /* LocationServicesDisabledAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationServicesDisabledAlert.swift; sourceTree = "<group>"; };
|
||||
EDC3573A2B7B5029001AE9CA /* CALayer+SetCorner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CALayer+SetCorner.swift"; sourceTree = "<group>"; };
|
||||
EDC4E3402C5D1BD3009286A2 /* MockRecentlyDeletedCategoriesManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockRecentlyDeletedCategoriesManager.swift; sourceTree = "<group>"; };
|
||||
EDC4E3412C5D1BD3009286A2 /* RecentlyDeletedCategoriesViewModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentlyDeletedCategoriesViewModelTests.swift; sourceTree = "<group>"; };
|
||||
EDC4E3472C5D1BEF009286A2 /* RecentlyDeletedCategoriesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentlyDeletedCategoriesViewController.swift; sourceTree = "<group>"; };
|
||||
EDC4E3482C5D1BEF009286A2 /* RecentlyDeletedCategoriesViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentlyDeletedCategoriesViewModel.swift; sourceTree = "<group>"; };
|
||||
EDC4E3492C5D1BEF009286A2 /* RecentlyDeletedTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentlyDeletedTableViewCell.swift; sourceTree = "<group>"; };
|
||||
EDE243D52B6CF3980057369B /* AboutController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutController.swift; sourceTree = "<group>"; };
|
||||
EDE243E02B6D3EA00057369B /* InfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoView.swift; sourceTree = "<group>"; };
|
||||
EDE243E42B6D3F400057369B /* OSMView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSMView.swift; sourceTree = "<group>"; };
|
||||
|
@ -2070,6 +2080,7 @@
|
|||
3404F4A02028A6C00090E401 /* Categories */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EDC4E34A2C5D1BEF009286A2 /* RecentlyDeleted */,
|
||||
33046837219C605E0041F3A8 /* Category settings */,
|
||||
343D7B6D202AF4CA007D56A8 /* Actions */,
|
||||
3404F48F202898CC0090E401 /* BMCModels.swift */,
|
||||
|
@ -3076,6 +3087,7 @@
|
|||
ED1ADA312BC6B19E0029209F /* Tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EDC4E3442C5D1BD3009286A2 /* Bookmarks */,
|
||||
4B4153B82BF970B800EE4B02 /* Classes */,
|
||||
4B4153B62BF9709100EE4B02 /* Core */,
|
||||
);
|
||||
|
@ -3129,6 +3141,41 @@
|
|||
path = ColorPicker;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EDC4E3422C5D1BD3009286A2 /* RecentlyDeletedTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EDC4E3402C5D1BD3009286A2 /* MockRecentlyDeletedCategoriesManager.swift */,
|
||||
EDC4E3412C5D1BD3009286A2 /* RecentlyDeletedCategoriesViewModelTests.swift */,
|
||||
);
|
||||
path = RecentlyDeletedTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EDC4E3432C5D1BD3009286A2 /* Categories */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EDC4E3422C5D1BD3009286A2 /* RecentlyDeletedTests */,
|
||||
);
|
||||
path = Categories;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EDC4E3442C5D1BD3009286A2 /* Bookmarks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EDC4E3432C5D1BD3009286A2 /* Categories */,
|
||||
);
|
||||
path = Bookmarks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EDC4E34A2C5D1BEF009286A2 /* RecentlyDeleted */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EDC4E3472C5D1BEF009286A2 /* RecentlyDeletedCategoriesViewController.swift */,
|
||||
EDC4E3482C5D1BEF009286A2 /* RecentlyDeletedCategoriesViewModel.swift */,
|
||||
EDC4E3492C5D1BEF009286A2 /* RecentlyDeletedTableViewCell.swift */,
|
||||
);
|
||||
path = RecentlyDeleted;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EDE8EAE32C2DB74A002777F5 /* OpenInAppActionSheet */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -4351,6 +4398,7 @@
|
|||
3463BA671DE81DB90082417F /* MWMTrafficButtonViewController.mm in Sources */,
|
||||
ED79A5D52BDF8D6100952D1F /* SynchronizationError.swift in Sources */,
|
||||
993DF10323F6BDB100AC231A /* MainTheme.swift in Sources */,
|
||||
EDC4E34D2C5D1BEF009286A2 /* RecentlyDeletedTableViewCell.swift in Sources */,
|
||||
34AB66051FC5AA320078E451 /* MWMNavigationDashboardManager+Entity.mm in Sources */,
|
||||
993DF12A23F6BDB100AC231A /* Style.swift in Sources */,
|
||||
34ABA6171C2D185C00FE1BEC /* MWMAuthorizationOSMLoginViewController.mm in Sources */,
|
||||
|
@ -4443,6 +4491,7 @@
|
|||
342CC5F21C2D7730005F3FE5 /* MWMAuthorizationLoginViewController.mm in Sources */,
|
||||
340475591E081A4600C92850 /* WebViewController.m in Sources */,
|
||||
3404F4992028A20D0090E401 /* BMCCategoryCell.swift in Sources */,
|
||||
EDC4E34C2C5D1BEF009286A2 /* RecentlyDeletedCategoriesViewModel.swift in Sources */,
|
||||
F62607FD207B790300176C5A /* SpinnerAlert.swift in Sources */,
|
||||
3444DFD21F17620C00E73099 /* MWMMapWidgetsHelper.mm in Sources */,
|
||||
3472B5E1200F86C800DC6CD5 /* MWMEditorHelper.mm in Sources */,
|
||||
|
@ -4492,6 +4541,7 @@
|
|||
3454D7C21E07F045004AF2AD /* NSString+Categories.m in Sources */,
|
||||
34E7761F1F14DB48003040B3 /* PlacePageArea.swift in Sources */,
|
||||
ED79A5D82BDF8D6100952D1F /* DefaultLocalDirectoryMonitor.swift in Sources */,
|
||||
EDC4E34B2C5D1BEF009286A2 /* RecentlyDeletedCategoriesViewController.swift in Sources */,
|
||||
ED8270F02C2071A3005966DA /* SettingsTableViewDetailedSwitchCell.swift in Sources */,
|
||||
4728F69322CF89A400E00028 /* GradientView.swift in Sources */,
|
||||
F6381BF61CD12045004CA943 /* LocaleTranslator.mm in Sources */,
|
||||
|
@ -4677,9 +4727,11 @@
|
|||
ED1ADA332BC6B1B40029209F /* CarPlayServiceTests.swift in Sources */,
|
||||
EDF838C32C00B9D6007E4E67 /* UbiquitousDirectoryMonitorDelegateMock.swift in Sources */,
|
||||
EDF838BE2C00B9D0007E4E67 /* LocalDirectoryMonitorDelegateMock.swift in Sources */,
|
||||
EDC4E3692C5E6F5B009286A2 /* MockRecentlyDeletedCategoriesManager.swift in Sources */,
|
||||
EDF838BF2C00B9D0007E4E67 /* SynchronizationStateManagerTests.swift in Sources */,
|
||||
4B83AE4B2C2E642100B0C3BC /* TTSTesterTest.m in Sources */,
|
||||
EDF838C22C00B9D6007E4E67 /* MetadataItemStubs.swift in Sources */,
|
||||
EDC4E3612C5E2576009286A2 /* RecentlyDeletedCategoriesViewModelTests.swift in Sources */,
|
||||
4B4153B52BF9695500EE4B02 /* MWMTextToSpeechTests.mm in Sources */,
|
||||
EDF838C42C00B9D6007E4E67 /* FileManagerMock.swift in Sources */,
|
||||
);
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
class MockRecentlyDeletedCategoriesManager: NSObject, RecentlyDeletedCategoriesManager, BookmarksObservable {
|
||||
|
||||
var categories = [RecentlyDeletedCategory]()
|
||||
|
||||
func recentlyDeletedCategoriesCount() -> UInt64 {
|
||||
UInt64(categories.count)
|
||||
}
|
||||
|
||||
func getRecentlyDeletedCategories() -> [RecentlyDeletedCategory] {
|
||||
categories
|
||||
}
|
||||
|
||||
func deleteFile(at urls: [URL]) {
|
||||
categories.removeAll { urls.contains($0.fileURL) }
|
||||
}
|
||||
|
||||
func deleteAllRecentlyDeletedCategories() {
|
||||
categories.removeAll()
|
||||
}
|
||||
|
||||
func recoverRecentlyDeletedCategories(at urls: [URL]) {
|
||||
categories.removeAll { urls.contains($0.fileURL) }
|
||||
}
|
||||
|
||||
func deleteRecentlyDeletedCategory(at urls: [URL]) {
|
||||
categories.removeAll { urls.contains($0.fileURL) }
|
||||
}
|
||||
|
||||
func add(_ observer: any BookmarksObserver) {}
|
||||
|
||||
func remove(_ observer: any BookmarksObserver) {}
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
import XCTest
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
@testable import Organic_Maps__Debug_
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
final class RecentlyDeletedCategoriesViewModelTests: XCTestCase {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
var viewModel: RecentlyDeletedCategoriesViewModel!
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
var bookmarksManagerMock: MockRecentlyDeletedCategoriesManager!
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
override func setUp() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
super.setUp()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
bookmarksManagerMock = MockRecentlyDeletedCategoriesManager()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
setupBookmarksManagerStubs()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel = RecentlyDeletedCategoriesViewModel(bookmarksManager: bookmarksManagerMock)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
override func tearDown() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel = nil
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
bookmarksManagerMock = nil
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
super.tearDown()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
private func setupBookmarksManagerStubs() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
bookmarksManagerMock.categories = [
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
RecentlyDeletedCategory(title: "test1", fileURL: URL(string: "test1")!, deletionDate: Date()),
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
RecentlyDeletedCategory(title: "test2", fileURL: URL(string: "test2")!, deletionDate: Date()),
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
RecentlyDeletedCategory(title: "lol", fileURL: URL(string: "lol")!, deletionDate: Date()),
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
RecentlyDeletedCategory(title: "te1", fileURL: URL(string: "te1")!, deletionDate: Date()),
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
]
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testInitializationFetchesCategories() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .nothingSelected)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, Int(bookmarksManagerMock.recentlyDeletedCategoriesCount()))
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
// MARK: - Selection Tests
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testMultipleSelectionAndDeselection() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.selectAllCategories()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
let initialSelectedCount = viewModel.selectedIndexPaths.count
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(initialSelectedCount, viewModel.filteredDataSource.flatMap { $0.content }.count)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.deselectAllCategories()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertTrue(viewModel.selectedIndexPaths.isEmpty)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testSelectAndDeselectSpecificCategory() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
let specificIndexPath = IndexPath(row: 0, section: 0)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.selectCategory(at: specificIndexPath)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertTrue(viewModel.selectedIndexPaths.contains(specificIndexPath))
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.deselectCategory(at: specificIndexPath)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertFalse(viewModel.selectedIndexPaths.contains(specificIndexPath))
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testSelectAndDeselectSpecificCategories() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
let indexPath1 = IndexPath(row: 0, section: 0)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
let indexPath2 = IndexPath(row: 1, section: 0)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
let indexPath3 = IndexPath(row: 2, section: 0)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.selectCategory(at: indexPath1)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.selectCategory(at: indexPath2)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.selectCategory(at: indexPath3)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertTrue(viewModel.selectedIndexPaths.contains(indexPath1))
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertTrue(viewModel.selectedIndexPaths.contains(indexPath2))
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertTrue(viewModel.selectedIndexPaths.contains(indexPath3))
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.deselectCategory(at: indexPath1)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertFalse(viewModel.selectedIndexPaths.contains(indexPath1))
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .someSelected)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.deselectCategory(at: indexPath2)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.deselectCategory(at: indexPath3)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.selectedIndexPaths.count, .zero)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .nothingSelected)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testStateChangesOnSelection() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
let indexPath = IndexPath(row: 1, section: 0)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.selectCategory(at: indexPath)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .someSelected)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.deselectCategory(at: indexPath)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .nothingSelected)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testStateChangesOnDone() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
let indexPath = IndexPath(row: 1, section: 0)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.selectCategory(at: indexPath)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .someSelected)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.cancelSelecting()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, Int(bookmarksManagerMock.recentlyDeletedCategoriesCount()))
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
// MARK: - Searching Tests
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testSearchWithEmptyString() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.search("")
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, 4)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testSearchWithNoResults() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.search("xyz") // Assuming "xyz" matches no category names
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertTrue(viewModel.filteredDataSource.allSatisfy { $0.content.isEmpty })
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testCancelSearchRestoresDataSource() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
let searchText = "test"
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.search(searchText)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .searching)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertTrue(viewModel.filteredDataSource.allSatisfy { $0.content.allSatisfy { $0.title.localizedCaseInsensitiveContains(searchText) } })
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, 2)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.cancelSearching()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .nothingSelected)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, 4)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
// MARK: - Deletion Tests
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testDeleteCategory() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
let initialCount = bookmarksManagerMock.categories.count
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.deleteCategory(at: IndexPath(row: 0, section: 0))
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(bookmarksManagerMock.categories.count, initialCount - 1)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testDeleteAllWhenNoOneIsSelected() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.deleteSelectedCategories()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(bookmarksManagerMock.categories.count, .zero)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testDeleteAllWhenNoSoneAreSelected() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.selectCategory(at: IndexPath(row: 0, section: 0))
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.selectCategory(at: IndexPath(row: 1, section: 0))
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.deleteSelectedCategories()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .nothingSelected)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(bookmarksManagerMock.categories.count, 2)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, 2)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, Int(bookmarksManagerMock.recentlyDeletedCategoriesCount()))
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
// MARK: - Recovery Tests
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testRecoverCategory() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.recoverCategory(at: IndexPath(row: 0, section: 0))
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .nothingSelected)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(bookmarksManagerMock.categories.count, 3)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .nothingSelected)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testRecoverAll() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.recoverSelectedCategories()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .nothingSelected)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(bookmarksManagerMock.categories.count, 0)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testRecoverAllWhenSomeAreSelected() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.selectCategory(at: IndexPath(row: 0, section: 0))
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.selectCategory(at: IndexPath(row: 1, section: 0))
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.recoverSelectedCategories()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .nothingSelected)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(bookmarksManagerMock.categories.count, 2)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, Int(bookmarksManagerMock.recentlyDeletedCategoriesCount()))
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testSearchFiltersCategories() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
var searchText = "test"
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.search(searchText)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .searching)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertTrue(viewModel.filteredDataSource.allSatisfy { $0.content.allSatisfy { $0.title.localizedCaseInsensitiveContains(searchText) } })
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
searchText = "te"
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.search(searchText)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .searching)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertTrue(viewModel.filteredDataSource.allSatisfy { $0.content.allSatisfy { $0.title.localizedCaseInsensitiveContains(searchText) } })
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testDeleteAllCategories() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.deleteSelectedCategories()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertTrue(bookmarksManagerMock.categories.isEmpty)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testRecoverAllCategories() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.recoverSelectedCategories()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertTrue(bookmarksManagerMock.categories.isEmpty)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testDeleteAndRecoverAllCategoriesWhenEmpty() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
bookmarksManagerMock.categories = []
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.fetchRecentlyDeletedCategories()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.deleteSelectedCategories()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.recoverSelectedCategories()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertTrue(viewModel.filteredDataSource.isEmpty)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
func testMultipleStateTransitions() {
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.startSelecting()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .nothingSelected)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.startSearching()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .searching)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.cancelSearching()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
viewModel.cancelSelecting()
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
XCTAssertEqual(viewModel.state, .nothingSelected)
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
||||
}
|
||||
![]()
```suggestion
XCTAssertEqual(viewModel.filteredDataSource.flatMap { $0.content }.count, bookmarksManagerMock.getRecentlyDeletedCategoriesCount())
```
![]() ditto? ditto?
![]() ditto? ditto?
![]() ditto? ditto?
|
|
@ -225,6 +225,14 @@ std::string GetBookmarksDirectory()
|
|||
return base::JoinPath(GetPlatform().SettingsDir(), "bookmarks");
|
||||
}
|
||||
|
||||
std::string GetTrashDirectory()
|
||||
{
|
||||
std::string const trashDir = base::JoinPath(GetPlatform().SettingsDir(), std::string{kTrashDirectoryName});
|
||||
if (!Platform::IsFileExistsByFullPath(trashDir) && !Platform::MkDirChecked(trashDir))
|
||||
CHECK(false, ("Failed to create .Trash directory."));
|
||||
return trashDir;
|
||||
}
|
||||
|
||||
std::string RemoveInvalidSymbols(std::string const & name)
|
||||
{
|
||||
strings::UniString filtered;
|
||||
|
@ -283,6 +291,15 @@ std::string GenerateValidAndUniqueFilePathForGPX(std::string const & fileName)
|
|||
return GenerateUniqueFileName(GetBookmarksDirectory(), std::move(filePath), kGpxExtension);
|
||||
}
|
||||
|
||||
std::string GenerateValidAndUniqueTrashedFilePath(std::string const & fileName)
|
||||
{
|
||||
std::string extension = base::GetFileExtension(fileName);
|
||||
std::string filePath = RemoveInvalidSymbols(fileName);
|
||||
if (filePath.empty())
|
||||
filePath = kDefaultBookmarksFileName;
|
||||
return GenerateUniqueFileName(GetTrashDirectory(), std::move(filePath), extension);
|
||||
}
|
||||
|
||||
std::string const kDefaultBookmarksFileName = "Bookmarks";
|
||||
|
||||
// Populate empty category & track names based on file name: assign file name to category name,
|
||||
|
|
|
@ -69,6 +69,9 @@ std::string_view constexpr kKmzExtension = ".kmz";
|
|||
std::string_view constexpr kKmlExtension = ".kml";
|
||||
std::string_view constexpr kKmbExtension = ".kmb";
|
||||
std::string_view constexpr kGpxExtension = ".gpx";
|
||||
|
||||
std::string_view constexpr kTrashDirectoryName = ".Trash";
|
||||
|
||||
extern std::string const kDefaultBookmarksFileName;
|
||||
|
||||
enum class KmlFileType
|
||||
|
@ -92,10 +95,12 @@ inline std::string DebugPrint(KmlFileType fileType)
|
|||
/// @name File name/path helpers.
|
||||
/// @{
|
||||
std::string GetBookmarksDirectory();
|
||||
std::string GetTrashDirectory();
|
||||
std::string RemoveInvalidSymbols(std::string const & name);
|
||||
std::string GenerateUniqueFileName(const std::string & path, std::string name, std::string_view ext = kKmlExtension);
|
||||
std::string GenerateValidAndUniqueFilePathForKML(std::string const & fileName);
|
||||
std::string GenerateValidAndUniqueFilePathForGPX(std::string const & fileName);
|
||||
std::string GenerateValidAndUniqueTrashedFilePath(std::string const & fileName);
|
||||
/// @}
|
||||
|
||||
/// @name SerDes helpers.
|
||||
|
|
|
@ -404,6 +404,53 @@ void BookmarkManager::ResetRecentlyDeletedBookmark()
|
|||
m_recentlyDeletedBookmark.reset();
|
||||
}
|
||||
![]() A detailed comment is needed on why is this call required. Is there any way to avoid using it? A detailed comment is needed on why is this call required. Is there any way to avoid using it?
kirylkaveryn
commented
We should track the Situation:
Expectation: the file should be recovered. But we have: how to decide what is the latest file version? Updating The way to avoid this: create the versionig system or some kind of transactions journal. We should track the `delete` / `recover` action.
Situation:
device 1:
- user deletes the category (file is marked by the .deleted ext)
- could is synced
device 2:
- user disables the cloud
- user recovers the category (remove the .deleted ext)
- user enables the could
Expectation: the file should be recovered.
But we have:
on cloud:
file.kml.deleted (`lastModified` 0001)
on device 1:
file.kml (`lastModified` 0001)
how to decide what is the latest file version?
Updating `lastModified` allows to keep track the file's lifecycle.
The implementation without using `lastModified` does not allow to track it.
The way to avoid this: create the versionig system or some kind of transactions journal.
|
||||
|
||||
size_t BookmarkManager::GetRecentlyDeletedCategoriesCount() const
|
||||
![]() Is this method covered by any test? It should be used by UI to quickly retrieve the count without expensive parsing of all deleted categories. Is this method covered by any test? It should be used by UI to quickly retrieve the count without expensive parsing of all deleted categories.
kirylkaveryn
commented
I fixed the current cpp test and added checks for the GetRecentlyDeletedCategoriesCount I fixed the current cpp test and added checks for the GetRecentlyDeletedCategoriesCount
|
||||
{
|
||||
Platform::FilesList files;
|
||||
Platform::GetFilesByExt(GetTrashDirectory(), kKmlExtension, files);
|
||||
return files.size();
|
||||
}
|
||||
|
||||
BookmarkManager::KMLDataCollectionPtr BookmarkManager::GetRecentlyDeletedCategories()
|
||||
![]() Is this method called synchronously from the main thread? It may cause a huge delay for large deleted lists in such a case. Please also make sure that it's only called when necessary (when Recently Deleted UI is displayed). Is this method called synchronously from the main thread? It may cause a huge delay for large deleted lists in such a case. Please also make sure that it's only called when necessary (when Recently Deleted UI is displayed).
kirylkaveryn
commented
Yes
This is why the The > Is this method called synchronously from the main thread?
Yes
> It may cause a huge delay for large deleted lists in such a case. Please also make sure that it's only called when necessary (when Recently Deleted UI is displayed).
This is why the `GetRecentlyDeletedCategoriesCount` was added.
The `GetRecentlyDeletedCategories` was called only when the `RecentlyDeletedCategoriesViewController` is opening and during the it's updates (when the category was deleted by the cloud for example).
|
||||
{
|
||||
auto collection = LoadBookmarks(GetTrashDirectory(), kKmlExtension, KmlFileType::Text,
|
||||
[](kml::FileData const &)
|
||||
{
|
||||
return true;
|
||||
});
|
||||
![]() Will this work?
Will this work?
```suggestion
auto collection = LoadBookmarks(GetTrashDirectory(), kKmlExtension, KmlFileType::Text, [](kml::FileData const &), {});
```
kirylkaveryn
commented
No. The method was designed to work with the No. The method was designed to work with the ` using BookmarksChecker = std::function<bool(kml::FileData const &)>;`
that should returns bool:
<img width="1183" alt="image" src="https://github.com/user-attachments/assets/17737cb6-ffcc-4f27-b2e5-e2ac5a5c0e42">
![]() Sorry for the typo, of course I meant an empty std::function/lambda, which compiles: Sorry for the typo, of course I meant an empty std::function/lambda, which compiles:
`auto collection = LoadBookmarks(GetTrashDirectory(), kKmlExtension, KmlFileType::Text, {});`
|
||||
return collection;
|
||||
}
|
||||
|
||||
bool BookmarkManager::IsRecentlyDeletedCategory(std::string const & filePath) const
|
||||
{
|
||||
return filePath.find(GetTrashDirectory()) != std::string::npos;
|
||||
}
|
||||
|
||||
void BookmarkManager::RecoverRecentlyDeletedCategoriesAtPaths(std::vector<std::string> const & filePaths)
|
||||
{
|
||||
CHECK_THREAD_CHECKER(m_threadChecker, ());
|
||||
![]() This check should not be needed as we run the task below on file thread anyway. @vng right? This check should not be needed as we run the task below on file thread anyway. @vng right?
kirylkaveryn
commented
Thanks! Thanks!
|
||||
for (auto const & deletedFilePath : filePaths)
|
||||
{
|
||||
CHECK(IsRecentlyDeletedCategory(deletedFilePath), ("The category at path", deletedFilePath, "should be in the trash."));
|
||||
CHECK(Platform::IsFileExistsByFullPath(deletedFilePath), ("File should exist to be recovered.", deletedFilePath));
|
||||
auto recoveredFilePath = GenerateValidAndUniqueFilePathForKML(base::GetNameFromFullPathWithoutExt(deletedFilePath));
|
||||
base::MoveFileX(deletedFilePath, recoveredFilePath);
|
||||
LOG(LINFO, ("Recently deleted category at", deletedFilePath, "is recovered"));
|
||||
ReloadBookmark(recoveredFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
void BookmarkManager::DeleteRecentlyDeletedCategoriesAtPaths(std::vector<std::string> const & deletedFilePaths)
|
||||
{
|
||||
CHECK_THREAD_CHECKER(m_threadChecker, ());
|
||||
![]() This call may contain a lot of file delete operations. Would it be better to remove the thread checker line and run it on the async File thread instead of the UI thread? It doesn't return anything. But I assume that the caller expects that after returning to the main bookmarks screen files are already deleted, and an updated count in Recently Deleted is displayed. Correct? This call may contain a lot of file delete operations. Would it be better to remove the thread checker line and run it on the async File thread instead of the UI thread? It doesn't return anything. But I assume that the caller expects that after returning to the main bookmarks screen files are already deleted, and an updated count in Recently Deleted is displayed. Correct?
rtsisyk
commented
As far as I can see, all deletion operations in the existing code are currently performed in the UI thread. As far as I can see, all deletion operations in the existing code are currently performed in the UI thread.
kirylkaveryn
commented
The main idea is to call this method synchronously on the main and Not pass the completion handler -> update the state immediately without waiting for deletion ansync finish. For example:
The main idea is to call this method synchronously on the main and Not pass the completion handler -> update the state immediately without waiting for deletion ansync finish.
Deletion is quite fast and it is better to rely on the actual directory content.
For example:
1. user press `delete all`
2. deletion task is dispatched to the file thread
3. the screen is closed
4. the code checks the trash directory and may see that not all the files is deleted - it will cause a bug because the GetRecentlyDeletedCategoriesCount returns > 0
rtsisyk
commented
Enhancing the error handling for I/O operations in the Storage thread across the code base is a good task to be tackled separately. This PR follows the existing approach. Enhancing the error handling for I/O operations in the Storage thread across the code base is a good task to be tackled separately. This PR follows the existing approach.
![]() I understand the reasoning for the synchronous call. Won't the android linter/runtime checker complain for file operations made on the UI thread in this case? I understand the reasoning for the synchronous call.
Won't the android linter/runtime checker complain for file operations made on the UI thread in this case?
|
||||
for (auto const & deletedFilePath : deletedFilePaths)
|
||||
{
|
||||
CHECK(IsRecentlyDeletedCategory(deletedFilePath), ("The category at path", deletedFilePath, "should be in the trash."));
|
||||
base::DeleteFileX(deletedFilePath);
|
||||
LOG(LINFO, ("Recently deleted category at", deletedFilePath, "is deleted"));
|
||||
}
|
||||
}
|
||||
|
||||
void BookmarkManager::DetachUserMark(kml::MarkId bmId, kml::MarkGroupId catId)
|
||||
{
|
||||
GetGroup(catId)->DetachUserMark(bmId);
|
||||
|
@ -2057,9 +2104,7 @@ void BookmarkManager::LoadBookmarkRoutine(std::string const & filePath, bool isT
|
|||
else if (ext == kGpxExtension)
|
||||
kmlData = LoadKmlFile(fileToLoad, KmlFileType::Gpx);
|
||||
else
|
||||
{
|
||||
ASSERT(false, ("Unsupported bookmarks extension", ext));
|
||||
}
|
||||
|
||||
base::DeleteFileX(fileToLoad);
|
||||
|
||||
|
@ -2485,7 +2530,7 @@ void BookmarkManager::CheckAndResetLastIds()
|
|||
idStorage.ResetTrackId();
|
||||
}
|
||||
|
||||
bool BookmarkManager::DeleteBmCategory(kml::MarkGroupId groupId, bool deleteFile)
|
||||
bool BookmarkManager::DeleteBmCategory(kml::MarkGroupId groupId, bool permanently)
|
||||
{
|
||||
CHECK_THREAD_CHECKER(m_threadChecker, ());
|
||||
auto it = m_categories.find(groupId);
|
||||
|
@ -2495,8 +2540,20 @@ bool BookmarkManager::DeleteBmCategory(kml::MarkGroupId groupId, bool deleteFile
|
|||
ClearGroup(groupId);
|
||||
m_changesTracker.OnDeleteGroup(groupId);
|
||||
|
||||
if (deleteFile)
|
||||
FileWriter::DeleteFileX(it->second->GetFileName());
|
||||
auto const & filePath = it->second->GetFileName();
|
||||
![]()
```suggestion
if (base::MoveFileX(filePath, trashedFilePath))
LOG(LINFO, ("Category at", filePath, "is trashed to the", trashedFilePath));
else
LOG(LERROR, ("Failed to move", filePath, "into the trash at", trashedFilePath));
```
|
||||
if (permanently)
|
||||
![]() gettimeofday and utime are not available on Windows.
gettimeofday and utime are not available on Windows.
```suggestion
#ifdef _MSC_VER
_utime(filePath.c_str(), nullptr);
#else
utime(filePath.c_str(), nullptr);
#endif
```
|
||||
{
|
||||
![]() Should it be
to avoid unnecessary allocation/copy? Should it be
```suggestion
auto const & filePath = it->second->GetFileName();
```
to avoid unnecessary allocation/copy?
rtsisyk
commented
How much $ How much $$$ does it save?
![]() It's not about $ It's not about $$$. It's about clarity and clear code. And understanding what's going on.
|
||||
base::DeleteFileX(filePath);
|
||||
LOG(LINFO, ("Category at", filePath, "is deleted"));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const trashedFilePath = GenerateValidAndUniqueTrashedFilePath(base::FileNameFromFullPath(filePath));
|
||||
if (base::MoveFileX(filePath, trashedFilePath))
|
||||
LOG(LINFO, ("Category at", filePath, "is trashed to the", trashedFilePath));
|
||||
else
|
||||
LOG(LERROR, ("Failed to move", filePath, "into the trash at", trashedFilePath));
|
||||
}
|
||||
|
||||
DeleteCompilations(it->second->GetCategoryData().m_compilationIds);
|
||||
m_categories.erase(it);
|
||||
|
@ -3573,9 +3630,9 @@ void BookmarkManager::EditSession::SetCategoryCustomProperty(kml::MarkGroupId ca
|
|||
m_bmManager.SetCategoryCustomProperty(categoryId, key, value);
|
||||
}
|
||||
|
||||
bool BookmarkManager::EditSession::DeleteBmCategory(kml::MarkGroupId groupId)
|
||||
bool BookmarkManager::EditSession::DeleteBmCategory(kml::MarkGroupId groupId, bool permanently)
|
||||
{
|
||||
return m_bmManager.DeleteBmCategory(groupId, true);
|
||||
return m_bmManager.DeleteBmCategory(groupId, permanently);
|
||||
}
|
||||
|
||||
void BookmarkManager::EditSession::NotifyChanges()
|
||||
|
|
|
@ -161,8 +161,10 @@ public:
|
|||
void SetCategoryAccessRules(kml::MarkGroupId categoryId, kml::AccessRules accessRules);
|
||||
void SetCategoryCustomProperty(kml::MarkGroupId categoryId, std::string const & key,
|
||||
std::string const & value);
|
||||
bool DeleteBmCategory(kml::MarkGroupId groupId);
|
||||
|
||||
|
||||
/// Removes the category from the list of categories and deletes the related file.
|
||||
/// @param permanently If true, the file will be removed from the disk. If false, the file will be marked as deleted and moved into a trash.
|
||||
bool DeleteBmCategory(kml::MarkGroupId groupId, bool permanently);
|
||||
void NotifyChanges();
|
||||
|
||||
private:
|
||||
|
@ -377,6 +379,13 @@ public:
|
|||
|
||||
bool HasRecentlyDeletedBookmark() const { return m_recentlyDeletedBookmark.operator bool(); };
|
||||
void ResetRecentlyDeletedBookmark();
|
||||
|
||||
size_t GetRecentlyDeletedCategoriesCount() const;
|
||||
BookmarkManager::KMLDataCollectionPtr GetRecentlyDeletedCategories();
|
||||
bool IsRecentlyDeletedCategory(std::string const & filePath) const;
|
||||
|
||||
void RecoverRecentlyDeletedCategoriesAtPaths(std::vector<std::string> const & filePaths);
|
||||
void DeleteRecentlyDeletedCategoriesAtPaths(std::vector<std::string> const & filePaths);
|
||||
|
||||
// Used for LoadBookmarks() and unit tests only. Does *not* update last modified time.
|
||||
void CreateCategories(KMLDataCollection && dataCollection, bool autoSave = false);
|
||||
|
@ -581,7 +590,7 @@ private:
|
|||
void SetCategoryTags(kml::MarkGroupId categoryId, std::vector<std::string> const & tags);
|
||||
void SetCategoryAccessRules(kml::MarkGroupId categoryId, kml::AccessRules accessRules);
|
||||
void SetCategoryCustomProperty(kml::MarkGroupId categoryId, std::string const & key, std::string const & value);
|
||||
bool DeleteBmCategory(kml::MarkGroupId groupId, bool deleteFile);
|
||||
bool DeleteBmCategory(kml::MarkGroupId groupId, bool permanently);
|
||||
void ClearCategories();
|
||||
|
||||
void MoveBookmark(kml::MarkId bmID, kml::MarkGroupId curGroupID, kml::MarkGroupId newGroupID);
|
||||
|
|
|
@ -247,7 +247,7 @@ UNIT_CLASS_TEST(Runner, Bookmarks_ExportKML)
|
|||
bmManager.GetEditSession().ClearGroup(groupId1);
|
||||
TEST_EQUAL(bmManager.GetUserMarkIds(groupId1).size(), 0, ());
|
||||
|
||||
bmManager.GetEditSession().DeleteBmCategory(groupId1);
|
||||
bmManager.GetEditSession().DeleteBmCategory(groupId1, true);
|
||||
TEST_EQUAL(bmManager.HasBmCategory(groupId1), false, ());
|
||||
TEST_EQUAL(bmManager.GetBmGroupsCount(), 0, ());
|
||||
|
||||
|
@ -262,7 +262,7 @@ UNIT_CLASS_TEST(Runner, Bookmarks_ExportKML)
|
|||
CheckBookmarks(bmManager, groupId2);
|
||||
TEST_EQUAL(bmManager.IsVisible(groupId2), true, ());
|
||||
|
||||
bmManager.GetEditSession().DeleteBmCategory(groupId2);
|
||||
bmManager.GetEditSession().DeleteBmCategory(groupId2, true);
|
||||
TEST_EQUAL(bmManager.HasBmCategory(groupId2), false, ());
|
||||
|
||||
BookmarkManager::KMLDataCollection kmlDataCollection3;
|
||||
|
@ -1095,7 +1095,7 @@ UNIT_CLASS_TEST(Runner, Bookmarks_SpecialXMLNames)
|
|||
auto const fileNameTmp = fileName + ".backup";
|
||||
TEST(base::CopyFileX(fileName, fileNameTmp), ());
|
||||
|
||||
bmManager.GetEditSession().DeleteBmCategory(catId);
|
||||
bmManager.GetEditSession().DeleteBmCategory(catId, true);
|
||||
|
||||
auto const file2Name = "file2";
|
||||
BookmarkManager::KMLDataCollection kmlDataCollection2;
|
||||
|
@ -1530,4 +1530,46 @@ UNIT_CLASS_TEST(Runner, Bookmarks_BrokenFile)
|
|||
auto kmlData = LoadKmlFile(fileName, KmlFileType::Binary);
|
||||
TEST(kmlData == nullptr, ());
|
||||
}
|
||||
|
||||
UNIT_CLASS_TEST(Runner, Bookmarks_RecentlyDeleted)
|
||||
{
|
||||
BookmarkManager bmManager(BM_CALLBACKS);
|
||||
bmManager.EnableTestMode(true);
|
||||
auto const dir = GetBookmarksDirectory();
|
||||
bool const delDirOnExit = Platform::MkDir(dir) == Platform::ERR_OK;
|
||||
SCOPE_GUARD(dirDeleter, [&](){ if (delDirOnExit) (void)Platform::RmDir(dir); });
|
||||
|
||||
std::string const filePath = base::JoinPath(dir, "file" + std::string{kKmlExtension});
|
||||
BookmarkManager::KMLDataCollection kmlDataCollection;
|
||||
kmlDataCollection.emplace_back(filePath, LoadKmlData(MemReader(kmlString, std::strlen(kmlString)), KmlFileType::Text));
|
||||
|
||||
FileWriter w(filePath);
|
||||
w.Write(kmlDataCollection.data(), kmlDataCollection.size());
|
||||
|
||||
TEST(kmlDataCollection.back().second, ());
|
||||
bmManager.CreateCategories(std::move(kmlDataCollection));
|
||||
TEST_EQUAL(bmManager.GetBmGroupsCount(), 1, ());
|
||||
TEST_EQUAL(bmManager.GetRecentlyDeletedCategoriesCount(), 0, ());
|
||||
|
||||
auto const groupId = bmManager.GetUnsortedBmGroupsIdList().front();
|
||||
|
||||
bmManager.GetEditSession().DeleteBmCategory(groupId, false /* permanently */);
|
||||
TEST_EQUAL(bmManager.GetBmGroupsCount(), 0, ());
|
||||
|
||||
auto const deletedCategories = bmManager.GetRecentlyDeletedCategories();
|
||||
TEST_EQUAL(deletedCategories->size(), 1, ());
|
||||
TEST_EQUAL(bmManager.GetRecentlyDeletedCategoriesCount(), 1, ());
|
||||
|
||||
auto const & deletedCategory = deletedCategories->front();
|
||||
auto const deletedFilePath = deletedCategory.first;
|
||||
TEST_EQUAL(base::FileNameFromFullPath(deletedCategory.first), base::FileNameFromFullPath(filePath), ());
|
||||
|
||||
bmManager.DeleteRecentlyDeletedCategoriesAtPaths({ deletedFilePath });
|
||||
TEST_EQUAL(bmManager.GetBmGroupsCount(), 0, ());
|
||||
TEST_EQUAL(bmManager.GetRecentlyDeletedCategoriesCount(), 0, ());
|
||||
TEST_EQUAL(bmManager.GetRecentlyDeletedCategories()->size(), 0, ());
|
||||
|
||||
TEST(!Platform::IsFileExistsByFullPath(filePath), ());
|
||||
TEST(!Platform::IsFileExistsByFullPath(deletedFilePath), ());
|
||||
}
|
||||
} // namespace bookmarks_test
|
||||
|
|
|
@ -212,7 +212,7 @@ void BookmarkDialog::OnDeleteClick()
|
|||
}
|
||||
else
|
||||
{
|
||||
bm.GetEditSession().DeleteBmCategory(categoryIt->second);
|
||||
bm.GetEditSession().DeleteBmCategory(categoryIt->second, true);
|
||||
FillTree();
|
||||
}
|
||||
return;
|
||||
|
|
|
@ -204,7 +204,7 @@ void Screenshoter::ProcessNextKml()
|
|||
auto es = bookmarkManager.GetEditSession();
|
||||
auto const idList = bookmarkManager.GetUnsortedBmGroupsIdList();
|
||||
for (auto catId : idList)
|
||||
es.DeleteBmCategory(catId);
|
||||
es.DeleteBmCategory(catId, true);
|
||||
|
||||
bookmarkManager.CreateCategories(std::move(collection));
|
||||
|
||||
|
|
nit: avoid unnecessary copy