@@ -40,7 +40,7 @@ - (void)finishWithError:(NSError *)error fetchedData:(id)fetchedData block:(void
[super finishWithError:error fetchedData:fetchedData];
if (block) {
dispatchOnMainQueue(^{
block(error ?: fetchedData);
block(error ?: fetchedData);
});
}
}
@@ -71,16 +71,16 @@ - (void)fetchLanguageLinksForArticleURL:(NSURL *)url
parameters:params
progress:NULL
success:^(NSURLSessionDataTask *operation, NSDictionary *indexedLanguageLinks) {
[[MWNetworkActivityIndicatorManager sharedManager] pop];
NSAssert(indexedLanguageLinks.count < 2,
@"Expected language links to return one or no objects for the title we fetched, but got: %@",
indexedLanguageLinks);
NSArray *languageLinksForTitle = [[indexedLanguageLinks allValues] firstObject];
[self finishWithError:nil fetchedData:languageLinksForTitle block:success];
[[MWNetworkActivityIndicatorManager sharedManager] pop];
NSAssert(indexedLanguageLinks.count < 2,
@"Expected language links to return one or no objects for the title we fetched, but got: %@",
indexedLanguageLinks);
NSArray *languageLinksForTitle = [[indexedLanguageLinks allValues] firstObject];
[self finishWithError:nil fetchedData:languageLinksForTitle block:success];
}
failure:^(NSURLSessionDataTask *operation, NSError *error) {
[[MWNetworkActivityIndicatorManager sharedManager] pop];
[self finishWithError:error fetchedData:nil block:failure];
[[MWNetworkActivityIndicatorManager sharedManager] pop];
[self finishWithError:error fetchedData:nil block:failure];
}];
}

@@ -11,14 +11,14 @@ - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data er
}
NSDictionary *pagesByID = json[@"query"][@"pages"];
return [[pagesByID bk_map:^id(id key, NSDictionary *result) {
return [result[@"langlinks"] bk_map:^MWKLanguageLink *(NSDictionary *jsonLink) {
return [[MWKLanguageLink alloc] initWithLanguageCode:jsonLink[@"lang"]
pageTitleText:jsonLink[@"*"]
name:jsonLink[@"autonym"]
localizedName:jsonLink[@"langname"]];
}];
return [result[@"langlinks"] bk_map:^MWKLanguageLink *(NSDictionary *jsonLink) {
return [[MWKLanguageLink alloc] initWithLanguageCode:jsonLink[@"lang"]
pageTitleText:jsonLink[@"*"]
name:jsonLink[@"autonym"]
localizedName:jsonLink[@"langname"]];
}];
}] bk_reject:^BOOL(id key, id obj) {
return WMF_IS_EQUAL(obj, [NSNull null]);
return WMF_IS_EQUAL(obj, [NSNull null]);
}];
}

@@ -84,10 +84,10 @@ - (NSUInteger)indexForEntry:(id<MWKListObject>)entry {

- (id<MWKListObject> __nullable)entryForListIndex:(MWKListIndex)listIndex {
return [self.entries bk_match:^BOOL(id<MWKListObject> obj) {
if ([[obj listIndex] isEqual:listIndex]) {
return YES;
}
return NO;
if ([[obj listIndex] isEqual:listIndex]) {
return YES;
}
return NO;
}];
}

@@ -150,20 +150,20 @@ - (void)sortEntries {

- (AnyPromise *)save {
return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
dispatchOnMainQueue(^{
if (self.dirty) {
[self performSaveWithCompletion:^{
self.dirty = NO;
resolve(nil);
dispatchOnMainQueue(^{
if (self.dirty) {
[self performSaveWithCompletion:^{
self.dirty = NO;
resolve(nil);
}
error:^(NSError *error) {
resolve(error);
}];
} else {
self.dirty = NO;
resolve(nil);
}
error:^(NSError *error) {
resolve(error);
}];
} else {
self.dirty = NO;
resolve(nil);
}
});
});
}];
}

@@ -6,30 +6,30 @@ + (NSValueTransformer *)locationJSONTransformer {
return [MTLValueTransformer transformerUsingForwardBlock:^id(NSArray *value,
BOOL *success,
NSError *__autoreleasing *error) {
NSDictionary *coords = [value firstObject];
NSNumber *lat = coords[@"lat"];
NSNumber *lon = coords[@"lon"];
NSDictionary *coords = [value firstObject];
NSNumber *lat = coords[@"lat"];
NSNumber *lon = coords[@"lon"];

if (![lat isKindOfClass:[NSNumber class]] || ![lon isKindOfClass:[NSNumber class]]) {
WMFSafeAssign(success, NO);
return nil;
}
if (![lat isKindOfClass:[NSNumber class]] || ![lon isKindOfClass:[NSNumber class]]) {
WMFSafeAssign(success, NO);
return nil;
}

return [[CLLocation alloc] initWithLatitude:[lat doubleValue] longitude:[lon doubleValue]];
return [[CLLocation alloc] initWithLatitude:[lat doubleValue] longitude:[lon doubleValue]];
}];
}

+ (NSValueTransformer *)distanceFromQueryCoordinatesJSONTransformer {
return [MTLValueTransformer transformerUsingForwardBlock:^id(NSArray *value,
BOOL *success,
NSError *__autoreleasing *error) {
NSDictionary *coords = [value firstObject];
NSNumber *distance = coords[@"dist"];
if (![distance isKindOfClass:[NSNumber class]]) {
WMFSafeAssign(success, NO);
return nil;
}
return distance;
NSDictionary *coords = [value firstObject];
NSNumber *distance = coords[@"dist"];
if (![distance isKindOfClass:[NSNumber class]]) {
WMFSafeAssign(success, NO);
return nil;
}
return distance;
}];
}

@@ -14,12 +14,12 @@ @implementation MWKRecentSearchList

- (instancetype)initWithDataStore:(MWKDataStore *)dataStore {
NSArray *entries = [[dataStore recentSearchListData] wmf_mapAndRejectNil:^id(id obj) {
@try {
return [[MWKRecentSearchEntry alloc] initWithDict:obj];
} @catch (NSException *e) {
NSLog(@"Encountered exception while reading entry %@: %@", e, obj);
return nil;
}
@try {
return [[MWKRecentSearchEntry alloc] initWithDict:obj];
} @catch (NSException *e) {
NSLog(@"Encountered exception while reading entry %@: %@", e, obj);
return nil;
}
}];

self = [super initWithEntries:entries];
@@ -39,7 +39,7 @@ - (BOOL)isEntryValid:(MWKRecentSearchEntry *)entry {

- (void)importEntries:(NSArray *)entries {
[super importEntries:[entries bk_select:^BOOL(MWKRecentSearchEntry *entry) {
return [self isEntryValid:entry];
return [self isEntryValid:entry];
}]];
}

@@ -68,7 +68,7 @@ - (void)performSaveWithCompletion:(dispatch_block_t)completion error:(WMFErrorHa

- (NSArray *)dataExport {
return [self.entries bk_map:^id(MWKRecentSearchEntry *obj) {
return [obj dataExport];
return [obj dataExport];
}];
}

@@ -49,31 +49,31 @@ - (void)migrateLegacyDataIfNeeded {

NSArray<MWKSavedPageEntry *> *entries =
[[MWKSavedPageList savedEntryDataFromExportedData:[self.dataStore savedPageListData]] wmf_mapAndRejectNil:^id(id obj) {
@try {
return [[MWKSavedPageEntry alloc] initWithDict:obj];
} @catch (NSException *e) {
return nil;
}
@try {
return [[MWKSavedPageEntry alloc] initWithDict:obj];
} @catch (NSException *e) {
return nil;
}
}];

if ([entries count] > 0) {
[self.dataSource readWriteAndReturnUpdatedKeysWithBlock:^NSArray<NSString *> *_Nonnull(YapDatabaseReadWriteTransaction *_Nonnull transaction, YapDatabaseViewTransaction *_Nonnull view) {
NSMutableArray *urls = [NSMutableArray arrayWithCapacity:[entries count]];
[entries enumerateObjectsUsingBlock:^(MWKSavedPageEntry *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
if (obj.url.wmf_title.length == 0) {
//HACK: Added check from pre-existing logic. Apparently there was a time when this URL could be bad. Copying here to keep exisitng functionality
return;
}
MWKHistoryEntry *history = [self historyEntryWithSavedPageEntry:obj];
MWKHistoryEntry *existing = [transaction objectForKey:[history databaseKey] inCollection:[MWKHistoryEntry databaseCollectionName]];
if (existing) {
existing.dateSaved = history.dateSaved;
history = existing;
}
[transaction setObject:history forKey:[history databaseKey] inCollection:[MWKHistoryEntry databaseCollectionName]];
[urls addObject:[history databaseKey]];
}];
return urls;
NSMutableArray *urls = [NSMutableArray arrayWithCapacity:[entries count]];
[entries enumerateObjectsUsingBlock:^(MWKSavedPageEntry *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
if (obj.url.wmf_title.length == 0) {
//HACK: Added check from pre-existing logic. Apparently there was a time when this URL could be bad. Copying here to keep exisitng functionality
return;
}
MWKHistoryEntry *history = [self historyEntryWithSavedPageEntry:obj];
MWKHistoryEntry *existing = [transaction objectForKey:[history databaseKey] inCollection:[MWKHistoryEntry databaseCollectionName]];
if (existing) {
existing.dateSaved = history.dateSaved;
history = existing;
}
[transaction setObject:history forKey:[history databaseKey] inCollection:[MWKHistoryEntry databaseCollectionName]];
[urls addObject:[history databaseKey]];
}];
return urls;
}];

[[NSUserDefaults standardUserDefaults] wmf_setDidMigrateSavedPageList:YES];
@@ -92,8 +92,8 @@ - (nullable MWKHistoryEntry *)mostRecentEntry {

- (nullable MWKHistoryEntry *)entryForURL:(NSURL *)url {
return [self.dataSource readAndReturnResultsWithBlock:^id _Nonnull(YapDatabaseReadTransaction *_Nonnull transaction, YapDatabaseViewTransaction *_Nonnull view) {
MWKHistoryEntry *entry = [transaction objectForKey:[MWKHistoryEntry databaseKeyForURL:url] inCollection:[MWKHistoryEntry databaseCollectionName]];
return entry;
MWKHistoryEntry *entry = [transaction objectForKey:[MWKHistoryEntry databaseKeyForURL:url] inCollection:[MWKHistoryEntry databaseCollectionName]];
return entry;
}];
}

@@ -102,15 +102,15 @@ - (void)enumerateItemsWithBlock:(void (^)(MWKHistoryEntry *_Nonnull entry, BOOL
return;
}
[self.dataSource readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction, YapDatabaseViewTransaction *_Nonnull view) {
if ([view numberOfItemsInAllGroups] == 0) {
return;
}
[view enumerateKeysAndObjectsInGroup:[[view allGroups] firstObject]
usingBlock:^(NSString *_Nonnull collection, NSString *_Nonnull key, MWKHistoryEntry *_Nonnull object, NSUInteger index, BOOL *_Nonnull stop) {
if (object.dateSaved) {
block(object, stop);
}
}];
if ([view numberOfItemsInAllGroups] == 0) {
return;
}
[view enumerateKeysAndObjectsInGroup:[[view allGroups] firstObject]
usingBlock:^(NSString *_Nonnull collection, NSString *_Nonnull key, MWKHistoryEntry *_Nonnull object, NSUInteger index, BOOL *_Nonnull stop) {
if (object.dateSaved) {
block(object, stop);
}
}];
}];
}

@@ -133,8 +133,8 @@ - (MWKHistoryEntry *)addEntry:(MWKHistoryEntry *)entry {
}

[self.dataSource readWriteAndReturnUpdatedKeysWithBlock:^NSArray *_Nonnull(YapDatabaseReadWriteTransaction *_Nonnull transaction, YapDatabaseViewTransaction *_Nonnull view) {
[transaction setObject:entry forKey:[entry databaseKey] inCollection:[MWKHistoryEntry databaseCollectionName]];
return @[ [entry databaseKey] ];
[transaction setObject:entry forKey:[entry databaseKey] inCollection:[MWKHistoryEntry databaseCollectionName]];
return @[ [entry databaseKey] ];
}];

return entry;
@@ -158,17 +158,17 @@ - (MWKHistoryEntry *)addSavedPageWithURL:(NSURL *)url {
__block MWKHistoryEntry *entry = nil;

[self.dataSource readWriteAndReturnUpdatedKeysWithBlock:^NSArray<NSString *> *_Nonnull(YapDatabaseReadWriteTransaction *_Nonnull transaction, YapDatabaseViewTransaction *_Nonnull view) {
entry = [transaction objectForKey:[MWKHistoryEntry databaseKeyForURL:url] inCollection:[MWKHistoryEntry databaseCollectionName]];
if (!entry) {
entry = [[MWKHistoryEntry alloc] initWithURL:url];
}
if (!entry.dateSaved) {
entry.dateSaved = [NSDate date];
}
entry = [transaction objectForKey:[MWKHistoryEntry databaseKeyForURL:url] inCollection:[MWKHistoryEntry databaseCollectionName]];
if (!entry) {
entry = [[MWKHistoryEntry alloc] initWithURL:url];
}
if (!entry.dateSaved) {
entry.dateSaved = [NSDate date];
}

[transaction setObject:entry forKey:[MWKHistoryEntry databaseKeyForURL:url] inCollection:[MWKHistoryEntry databaseCollectionName]];
[transaction setObject:entry forKey:[MWKHistoryEntry databaseKeyForURL:url] inCollection:[MWKHistoryEntry databaseCollectionName]];

return @[ [entry databaseKey] ];
return @[ [entry databaseKey] ];
}];

return entry;
@@ -179,28 +179,28 @@ - (void)removeEntryWithURL:(NSURL *)url {
return;
}
[self.dataSource readWriteAndReturnUpdatedKeysWithBlock:^NSArray<NSString *> *_Nonnull(YapDatabaseReadWriteTransaction *_Nonnull transaction, YapDatabaseViewTransaction *_Nonnull view) {
MWKHistoryEntry *entry = [transaction objectForKey:[MWKHistoryEntry databaseKeyForURL:url] inCollection:[MWKHistoryEntry databaseCollectionName]];
entry.dateSaved = nil;
[transaction setObject:entry forKey:[MWKHistoryEntry databaseKeyForURL:url] inCollection:[MWKHistoryEntry databaseCollectionName]];
return @[ [MWKHistoryEntry databaseKeyForURL:url] ];
MWKHistoryEntry *entry = [transaction objectForKey:[MWKHistoryEntry databaseKeyForURL:url] inCollection:[MWKHistoryEntry databaseCollectionName]];
entry.dateSaved = nil;
[transaction setObject:entry forKey:[MWKHistoryEntry databaseKeyForURL:url] inCollection:[MWKHistoryEntry databaseCollectionName]];
return @[ [MWKHistoryEntry databaseKeyForURL:url] ];
}];
}

- (void)removeAllEntries {
[self.dataSource readWriteAndReturnUpdatedKeysWithBlock:^NSArray<NSString *> *_Nonnull(YapDatabaseReadWriteTransaction *_Nonnull transaction, YapDatabaseViewTransaction *_Nonnull view) {
NSMutableArray<NSString *> *keys = [NSMutableArray arrayWithCapacity:[self numberOfItems]];
[transaction enumerateKeysAndObjectsInCollection:[MWKHistoryEntry databaseCollectionName]
usingBlock:^(NSString *_Nonnull key, MWKHistoryEntry *_Nonnull object, BOOL *_Nonnull stop) {
if (object.dateSaved != nil) {
[keys addObject:key];
}
}];
[keys enumerateObjectsUsingBlock:^(NSString *_Nonnull key, NSUInteger idx, BOOL *_Nonnull stop) {
MWKHistoryEntry *entry = [[transaction objectForKey:key inCollection:[MWKHistoryEntry databaseCollectionName]] copy];
entry.dateSaved = nil;
[transaction setObject:entry forKey:key inCollection:[MWKHistoryEntry databaseCollectionName]];
}];
return keys;
NSMutableArray<NSString *> *keys = [NSMutableArray arrayWithCapacity:[self numberOfItems]];
[transaction enumerateKeysAndObjectsInCollection:[MWKHistoryEntry databaseCollectionName]
usingBlock:^(NSString *_Nonnull key, MWKHistoryEntry *_Nonnull object, BOOL *_Nonnull stop) {
if (object.dateSaved != nil) {
[keys addObject:key];
}
}];
[keys enumerateObjectsUsingBlock:^(NSString *_Nonnull key, NSUInteger idx, BOOL *_Nonnull stop) {
MWKHistoryEntry *entry = [[transaction objectForKey:key inCollection:[MWKHistoryEntry databaseCollectionName]] copy];
entry.dateSaved = nil;
[transaction setObject:entry forKey:key inCollection:[MWKHistoryEntry databaseCollectionName]];
}];
return keys;
}];
}

@@ -61,61 +61,61 @@ + (NSValueTransformer *)thumbnailURLJSONTransformer {
transformerUsingForwardBlock:^NSURL *(NSString *urlString,
BOOL *success,
NSError *__autoreleasing *error) {
return [NSURL wmf_optionalURLWithString:urlString];
return [NSURL wmf_optionalURLWithString:urlString];
}
reverseBlock:^NSString *(NSURL *thumbnailURL,
BOOL *success,
NSError *__autoreleasing *error) {
return [thumbnailURL absoluteString];
return [thumbnailURL absoluteString];
}];
}

+ (NSValueTransformer *)wikidataDescriptionJSONTransformer {
return [MTLValueTransformer transformerUsingForwardBlock:^id(NSArray *value, BOOL *success, NSError *__autoreleasing *error) {
NSString *description = [value firstObject];
return [description wmf_stringByCapitalizingFirstCharacter];
NSString *description = [value firstObject];
return [description wmf_stringByCapitalizingFirstCharacter];
}];
}

+ (MTLValueTransformer *)extractJSONTransformer {
return [MTLValueTransformer transformerUsingForwardBlock:^id(NSString *extract, BOOL *success, NSError *__autoreleasing *error) {
// HAX: sometimes the api gives us "..." for the extract, which is not useful and messes up how random
// weights relative quality of the random titles it retrieves.
if ([extract isEqualToString:@"..."]) {
extract = nil;
}
// HAX: sometimes the api gives us "..." for the extract, which is not useful and messes up how random
// weights relative quality of the random titles it retrieves.
if ([extract isEqualToString:@"..."]) {
extract = nil;
}

return [extract wmf_summaryFromText];
return [extract wmf_summaryFromText];
}];
}

+ (NSValueTransformer *)isDisambiguationJSONTransformer {
return [MTLValueTransformer
transformerUsingForwardBlock:^(NSDictionary *value, BOOL *success, NSError **error) {
NSString *disambiguation = value[@"pageprops.disambiguation"];
if (disambiguation) {
return @YES;
}
// HAX: occasionally the search api doesn't report back "disambiguation" page term ( T121288 ),
// so double-check wiki data description for "disambiguation page" string.
NSArray *descriptions = value[@"terms.description"];
return @(descriptions.count && [descriptions.firstObject containsString:@"disambiguation page"]);
NSString *disambiguation = value[@"pageprops.disambiguation"];
if (disambiguation) {
return @YES;
}
// HAX: occasionally the search api doesn't report back "disambiguation" page term ( T121288 ),
// so double-check wiki data description for "disambiguation page" string.
NSArray *descriptions = value[@"terms.description"];
return @(descriptions.count && [descriptions.firstObject containsString:@"disambiguation page"]);
}];
}

+ (NSValueTransformer *)isListJSONTransformer {
return [MTLValueTransformer
transformerUsingForwardBlock:^(NSArray *value, BOOL *success, NSError **error) {
// HAX: check wiki data description for "Wikimedia list article" string. Not perfect
// and enwiki specific, but confirmed with max that without doing separate wikidata query, there's no way to tell if it's a list at the moment.
return @(value.count && [value.firstObject containsString:@"Wikimedia list article"]);
// HAX: check wiki data description for "Wikimedia list article" string. Not perfect
// and enwiki specific, but confirmed with max that without doing separate wikidata query, there's no way to tell if it's a list at the moment.
return @(value.count && [value.firstObject containsString:@"Wikimedia list article"]);
}];
}

+ (NSValueTransformer *)revIDJSONTransformer {
return [MTLValueTransformer
transformerUsingForwardBlock:^id(NSArray *value, BOOL *success, NSError **error) {
return (value.count > 0 && value.firstObject[@"revid"]) ? value.firstObject[@"revid"] : @(0);
return (value.count > 0 && value.firstObject[@"revid"]) ? value.firstObject[@"revid"] : @(0);
}];
}

@@ -243,7 +243,7 @@ - (NSString *)summary {
return nil;
}
return [[[textNodes bk_map:^id(TFHppleElement *node) {
return node.raw;
return node.raw;
}] componentsJoinedByString:@" "] wmf_summaryFromText];
}

@@ -252,10 +252,10 @@ - (NSString *)summary {
- (nullable NSArray<NSURL *> *)disambiguationURLs {
NSArray *textNodes = [self elementsInTextMatchingXPath:WMFSectionDisambiguationTitlesXPathSelector];
return [textNodes wmf_mapAndRejectNil:^id(TFHppleElement *node) {
if (node.text.length == 0 || [node.text containsString:@"redlink=1"] || ![node.text containsString:WMFInternalLinkPathPrefix]) {
return nil;
}
return [NSURL wmf_URLWithSiteURL:self.url escapedDenormalizedInternalLink:node.text];
if (node.text.length == 0 || [node.text containsString:@"redlink=1"] || ![node.text containsString:WMFInternalLinkPathPrefix]) {
return nil;
}
return [NSURL wmf_URLWithSiteURL:self.url escapedDenormalizedInternalLink:node.text];
}];
}

@@ -276,16 +276,16 @@ - (NSString *)summary {
- (nullable NSArray<NSString *> *)pageIssues {
NSArray *issueNodes = [self elementsInTextMatchingXPath:WMFSectionPageIssuesXPathSelector];
return [issueNodes wmf_mapAndRejectNil:^id(TFHppleElement *node) {
if (node.raw.length == 0) {
return nil;
}
NSArray *unhiddenIssueNodes = [[TFHpple hppleWithHTMLData:[node.raw dataUsingEncoding:NSUTF8StringEncoding]] searchWithXPathQuery:WMFSectionPageIssueUnhiddenXPathSelector];
if (node.raw.length == 0) {
return nil;
}
NSArray *unhiddenIssueNodes = [[TFHpple hppleWithHTMLData:[node.raw dataUsingEncoding:NSUTF8StringEncoding]] searchWithXPathQuery:WMFSectionPageIssueUnhiddenXPathSelector];

NSArray *issuesStrings = [unhiddenIssueNodes wmf_mapAndRejectNil:^id(TFHppleElement *node) {
return ([[node.content stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0) ? nil : node.content;
}];
NSArray *issuesStrings = [unhiddenIssueNodes wmf_mapAndRejectNil:^id(TFHppleElement *node) {
return ([[node.content stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0) ? nil : node.content;
}];

return issuesStrings.count > 0 ? [issuesStrings componentsJoinedByString:@""] : nil;
return issuesStrings.count > 0 ? [issuesStrings componentsJoinedByString:@""] : nil;
}];
}

@@ -96,15 +96,15 @@ - (NSArray *)sectionsFromDataStore {

NSArray *files = [fm contentsOfDirectoryAtPath:path error:nil];
files = [files sortedArrayUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
int sectionId1 = [obj1 intValue];
int sectionId2 = [obj2 intValue];
if (sectionId1 < sectionId2) {
return NSOrderedAscending;
} else if (sectionId1 == sectionId2) {
return NSOrderedSame;
} else {
return NSOrderedDescending;
}
int sectionId1 = [obj1 intValue];
int sectionId2 = [obj2 intValue];
if (sectionId1 < sectionId2) {
return NSOrderedAscending;
} else if (sectionId1 == sectionId2) {
return NSOrderedSame;
} else {
return NSOrderedDescending;
}
}];

NSMutableArray *sections = [[NSMutableArray alloc] init];
@@ -180,35 +180,35 @@ - (void)buildSectionHierarchy {
__block MWKSection *currentParent = nil;
[self.sections makeObjectsPerformSelector:@selector(removeAllChildren)];
[self.sections enumerateObjectsUsingBlock:^(MWKSection *currentSection, NSUInteger idx, BOOL *stop) {
if (!currentSection.level) {
currentParent = nil;
return;
}
if ([currentParent isAtLevelAboveSection:currentSection]) {
MWKSection *lastChild = currentParent.children.lastObject;
if ([lastChild isAtSameLevelAsSection:currentSection] || ![lastChild isAtLevelAboveSection:currentSection]) {
[currentParent addChild:currentSection];
} else {
[lastChild addChild:currentSection];
}
} else {
currentParent = currentSection;
}
if (!currentSection.level) {
currentParent = nil;
return;
}
if ([currentParent isAtLevelAboveSection:currentSection]) {
MWKSection *lastChild = currentParent.children.lastObject;
if ([lastChild isAtSameLevelAsSection:currentSection] || ![lastChild isAtLevelAboveSection:currentSection]) {
[currentParent addChild:currentSection];
} else {
[lastChild addChild:currentSection];
}
} else {
currentParent = currentSection;
}
}];
}

- (NSArray *)topLevelSections {
__block MWKSection *currentParent = nil;
return [self.sections bk_reduce:[NSMutableArray arrayWithCapacity:self.sections.count]
withBlock:^NSMutableArray *(NSMutableArray *topLevelSections, MWKSection *section) {
if (!section.level) {
[topLevelSections addObject:section];
currentParent = nil;
} else if (currentParent == nil || ![currentParent isAtLevelAboveSection:section]) {
currentParent = section;
[topLevelSections addObject:section];
}
return topLevelSections;
if (!section.level) {
[topLevelSections addObject:section];
currentParent = nil;
} else if (currentParent == nil || ![currentParent isAtLevelAboveSection:section]) {
currentParent = section;
[topLevelSections addObject:section];
}
return topLevelSections;
}];
}

@@ -5,14 +5,14 @@ @implementation MWKSectionMetaData

+ (NSValueTransformer *)numberJSONTransformer {
return [MTLValueTransformer transformerUsingForwardBlock:^id(NSString *value, BOOL *success, NSError *__autoreleasing *error) {
NSArray *indexes = [value componentsSeparatedByString:@"."];
NSArray *indexes = [value componentsSeparatedByString:@"."];

NSIndexPath *indexPath = [indexes bk_reduce:[NSIndexPath new]
withBlock:^id(NSIndexPath *sum, NSString *obj) {
return [sum indexPathByAddingIndex:(NSUInteger)[obj integerValue]];
}];
NSIndexPath *indexPath = [indexes bk_reduce:[NSIndexPath new]
withBlock:^id(NSIndexPath *sum, NSString *obj) {
return [sum indexPathByAddingIndex:(NSUInteger)[obj integerValue]];
}];

return indexPath;
return indexPath;
}];
}

@@ -29,26 +29,26 @@ - (BOOL)isFetching {
- (AnyPromise *)fetchSiteInfoForSiteURL:(NSURL *)siteURL {
NSParameterAssert(siteURL);
return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
NSDictionary *params = @{
@"action" : @"query",
@"meta" : @"siteinfo",
@"format" : @"json",
@"siprop" : @"general"
};
NSDictionary *params = @{
@"action" : @"query",
@"meta" : @"siteinfo",
@"format" : @"json",
@"siprop" : @"general"
};

[self.operationManager wmf_GETAndRetryWithURL:siteURL
parameters:params
retry:NULL
success:^(NSURLSessionDataTask *operation, id responseObject) {
[[MWNetworkActivityIndicatorManager sharedManager] pop];
NSDictionary *generalProps = [responseObject valueForKeyPath:@"query.general"];
MWKSiteInfo *info = [[MWKSiteInfo alloc] initWithSiteURL:siteURL mainPageTitleText:generalProps[@"mainpage"]];
resolve(info);
}
failure:^(NSURLSessionDataTask *operation, NSError *error) {
[[MWNetworkActivityIndicatorManager sharedManager] pop];
resolve(error);
}];
[self.operationManager wmf_GETAndRetryWithURL:siteURL
parameters:params
retry:NULL
success:^(NSURLSessionDataTask *operation, id responseObject) {
[[MWNetworkActivityIndicatorManager sharedManager] pop];
NSDictionary *generalProps = [responseObject valueForKeyPath:@"query.general"];
MWKSiteInfo *info = [[MWKSiteInfo alloc] initWithSiteURL:siteURL mainPageTitleText:generalProps[@"mainpage"]];
resolve(info);
}
failure:^(NSURLSessionDataTask *operation, NSError *error) {
[[MWNetworkActivityIndicatorManager sharedManager] pop];
resolve(error);
}];
}];
}

@@ -41,14 +41,14 @@ - (MWKLanguageLinkFetcher *)fetcher {
- (void)fetchLanguagesWithSuccess:(dispatch_block_t)success
failure:(void (^__nullable)(NSError *__nonnull))failure {
[[QueuesSingleton sharedInstance].languageLinksFetcher wmf_cancelAllTasksWithCompletionHandler:^{
[self.fetcher fetchLanguageLinksForArticleURL:self.articleURL
success:^(NSArray *languageLinks) {
self.availableLanguages = languageLinks;
if (success) {
success();
[self.fetcher fetchLanguageLinksForArticleURL:self.articleURL
success:^(NSArray *languageLinks) {
self.availableLanguages = languageLinks;
if (success) {
success();
}
}
}
failure:failure];
failure:failure];
}];
}

@@ -59,27 +59,27 @@ - (void)setAvailableLanguages:(NSArray *)availableLanguages {

- (void)updateLanguageArrays {
self.otherLanguages = [[self.languageController.otherLanguages bk_select:^BOOL(MWKLanguageLink *language) {
return [self languageIsAvailable:language];
return [self languageIsAvailable:language];
}] bk_map:^id(MWKLanguageLink *language) {
return [self titleLanguageForLanguage:language];
return [self titleLanguageForLanguage:language];
}];

self.preferredLanguages = [[self.languageController.preferredLanguages bk_select:^BOOL(MWKLanguageLink *language) {
return [self languageIsAvailable:language];
return [self languageIsAvailable:language];
}] bk_map:^id(MWKLanguageLink *language) {
return [self titleLanguageForLanguage:language];
return [self titleLanguageForLanguage:language];
}];

self.allLanguages = [[self.languageController.allLanguages bk_select:^BOOL(MWKLanguageLink *language) {
return [self languageIsAvailable:language];
return [self languageIsAvailable:language];
}] bk_map:^id(MWKLanguageLink *language) {
return [self titleLanguageForLanguage:language];
return [self titleLanguageForLanguage:language];
}];
}

- (nullable MWKLanguageLink *)titleLanguageForLanguage:(MWKLanguageLink *)language {
return [self.availableLanguages bk_match:^BOOL(MWKLanguageLink *availableLanguage) {
return [language.languageCode isEqualToString:availableLanguage.languageCode];
return [language.languageCode isEqualToString:availableLanguage.languageCode];
}];
}

@@ -14,7 +14,7 @@ @implementation MWNetworkActivityIndicatorManager
+ (MWNetworkActivityIndicatorManager *)sharedManager {
static dispatch_once_t once;
dispatch_once(&once, ^{
sharedManager = [[MWNetworkActivityIndicatorManager alloc] init];
sharedManager = [[MWNetworkActivityIndicatorManager alloc] init];
});

return sharedManager;
@@ -28,17 +28,17 @@ - (void)setCount:(NSInteger)count {

- (void)push {
dispatch_async(dispatch_get_main_queue(), ^() {
@synchronized(self) {
self.count += 1;
}
@synchronized(self) {
self.count += 1;
}
});
}

- (void)pop {
dispatch_async(dispatch_get_main_queue(), ^() {
@synchronized(self) {
self.count -= 1;
}
@synchronized(self) {
self.count -= 1;
}
});
}

@@ -8,16 +8,16 @@ @implementation NSArray (BKIndex)
- (NSDictionary *)bk_index:(id<NSCopying> (^)(id))index {
return [self bk_reduce:[NSMutableDictionary dictionaryWithCapacity:self.count]
withBlock:^NSMutableDictionary *(NSMutableDictionary *acc, id obj) {
id<NSCopying> key = index(obj);
acc[key] = obj;
return acc;
id<NSCopying> key = index(obj);
acc[key] = obj;
return acc;
}];
}

- (NSDictionary *)bk_indexWithKeypath:(NSString *)keypath {
NSParameterAssert(keypath.length);
return [self bk_index:^id<NSCopying>(id obj) {
return [obj valueForKeyPath:keypath];
return [obj valueForKeyPath:keypath];
}];
}

@@ -10,12 +10,12 @@ - (NSArray *)wmf_strictMap:(id (^)(id obj))block {
NSMutableArray *result = [NSMutableArray arrayWithCapacity:self.count];

[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id value = block(obj);
NSParameterAssert(value != nil);
if (!value) {
value = [NSNull null];
}
[result addObject:value];
id value = block(obj);
NSParameterAssert(value != nil);
if (!value) {
value = [NSNull null];
}
[result addObject:value];
}];

return result;
@@ -27,11 +27,11 @@ - (NSArray *)wmf_mapAndRejectNil:(id _Nullable (^_Nonnull)(id _Nonnull obj))flat
}
return [self bk_reduce:[[NSMutableArray alloc] initWithCapacity:self.count]
withBlock:^id(NSMutableArray *sum, id obj) {
id result = flatMap(obj);
if (result) {
[sum addObject:result];
}
return sum;
id result = flatMap(obj);
if (result) {
[sum addObject:result];
}
return sum;
}];
}

@@ -16,12 +16,12 @@ - (NSAttributedString *)wmf_attributedStringChangingAttribute:(NSString *)attrib
inRange:NSMakeRange(0, self.length)
options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
usingBlock:^(id value, NSRange range, BOOL *stop) {
id newValue = block(value);
if (newValue) {
[mutableCopy addAttribute:attribute value:newValue range:range];
} else {
[mutableCopy removeAttribute:attribute range:range];
}
id newValue = block(value);
if (newValue) {
[mutableCopy addAttribute:attribute value:newValue range:range];
} else {
[mutableCopy removeAttribute:attribute range:range];
}
}];

[mutableCopy endEditing];
@@ -6,8 +6,8 @@ + (instancetype)wmf_utcGregorianCalendar {
static dispatch_once_t onceToken;
static NSCalendar *utcGregorianCalendar;
dispatch_once(&onceToken, ^{
utcGregorianCalendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
utcGregorianCalendar.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
utcGregorianCalendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
utcGregorianCalendar.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
});
return utcGregorianCalendar;
}
@@ -6,7 +6,7 @@ + (NSCharacterSet *)wmf_invertedWhitespaceCharSet {
static NSCharacterSet *invertedWhitespaceCharSet;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
invertedWhitespaceCharSet = [[NSCharacterSet whitespaceAndNewlineCharacterSet] invertedSet];
invertedWhitespaceCharSet = [[NSCharacterSet whitespaceAndNewlineCharacterSet] invertedSet];
});
return invertedWhitespaceCharSet;
}
@@ -8,12 +8,12 @@ + (NSDateFormatter *)wmf_iso8601Formatter {
static NSDateFormatter *iso8601Formatter = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// need to use "en" locale, otherwise the timestamp will fail to parse when the current locale is arabic on iOS 6
iso8601Formatter = [NSDateFormatter new];
iso8601Formatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
iso8601Formatter.dateFormat = WMF_ISO8601_FORMAT;
;
iso8601Formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en"];
// need to use "en" locale, otherwise the timestamp will fail to parse when the current locale is arabic on iOS 6
iso8601Formatter = [NSDateFormatter new];
iso8601Formatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
iso8601Formatter.dateFormat = WMF_ISO8601_FORMAT;
;
iso8601Formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en"];
});
return iso8601Formatter;
}
@@ -56,8 +56,8 @@ + (instancetype)wmf_utcMediumDateFormatterWithoutTime {
static NSDateFormatter *_dateFormatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_dateFormatter = [[self wmf_mediumDateFormatterWithoutTime] copy];
_dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
_dateFormatter = [[self wmf_mediumDateFormatterWithoutTime] copy];
_dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
});
return _dateFormatter;
}
@@ -66,9 +66,9 @@ + (instancetype)wmf_mediumDateFormatterWithoutTime {
static NSDateFormatter *_dateFormatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_dateFormatter = [[NSDateFormatter alloc] init];
_dateFormatter.dateStyle = NSDateFormatterMediumStyle;
_dateFormatter.timeStyle = NSDateFormatterNoStyle;
_dateFormatter = [[NSDateFormatter alloc] init];
_dateFormatter.dateStyle = NSDateFormatterMediumStyle;
_dateFormatter.timeStyle = NSDateFormatterNoStyle;
});
return _dateFormatter;
}
@@ -77,7 +77,7 @@ + (instancetype)wmf_englishHyphenatedYearMonthDayFormatter {
static NSDateFormatter *_dateFormatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_dateFormatter = [self wmf_newEnglishYearMonthDayFormatterWithSeparator:@"-"];
_dateFormatter = [self wmf_newEnglishYearMonthDayFormatterWithSeparator:@"-"];
});
return _dateFormatter;
}
@@ -86,8 +86,8 @@ + (instancetype)wmf_englishUTCSlashDelimitedYearMonthDayFormatter {
static NSDateFormatter *_dateFormatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_dateFormatter = [self wmf_newEnglishYearMonthDayFormatterWithSeparator:@"/"];
_dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
_dateFormatter = [self wmf_newEnglishYearMonthDayFormatterWithSeparator:@"/"];
_dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
});
return _dateFormatter;
}
@@ -104,8 +104,8 @@ + (instancetype)wmf_dayNameMonthNameDayOfMonthNumberDateFormatter {
static NSDateFormatter *_dateFormatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_dateFormatter = [[NSDateFormatter alloc] init];
[_dateFormatter setLocalizedDateFormatFromTemplate:@"EEEEMMMMdd"];
_dateFormatter = [[NSDateFormatter alloc] init];
[_dateFormatter setLocalizedDateFormatFromTemplate:@"EEEEMMMMdd"];
});
return _dateFormatter;
}
@@ -114,8 +114,8 @@ + (instancetype)wmf_utcDayNameMonthNameDayOfMonthNumberDateFormatter {
static NSDateFormatter *_dateFormatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_dateFormatter = [[self wmf_dayNameMonthNameDayOfMonthNumberDateFormatter] copy];
_dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
_dateFormatter = [[self wmf_dayNameMonthNameDayOfMonthNumberDateFormatter] copy];
_dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
});
return _dateFormatter;
}
@@ -124,8 +124,8 @@ + (instancetype)wmf_shortDayNameShortMonthNameDayOfMonthNumberDateFormatter {
static NSDateFormatter *_dateFormatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_dateFormatter = [[NSDateFormatter alloc] init];
[_dateFormatter setLocalizedDateFormatFromTemplate:@"EEEMMMdd"];
_dateFormatter = [[NSDateFormatter alloc] init];
[_dateFormatter setLocalizedDateFormatFromTemplate:@"EEEMMMdd"];
});
return _dateFormatter;
}
@@ -134,8 +134,8 @@ + (instancetype)wmf_utcShortDayNameShortMonthNameDayOfMonthNumberDateFormatter {
static NSDateFormatter *_dateFormatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_dateFormatter = [[self wmf_shortDayNameShortMonthNameDayOfMonthNumberDateFormatter] copy];
_dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
_dateFormatter = [[self wmf_shortDayNameShortMonthNameDayOfMonthNumberDateFormatter] copy];
_dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
});
return _dateFormatter;
}
@@ -4,7 +4,7 @@ @implementation NSDictionary (WMFExtensions)

- (BOOL)wmf_containsNullObjects {
NSNull *null = [self bk_match:^BOOL(id key, id obj) {
return [obj isKindOfClass:[NSNull class]];
return [obj isKindOfClass:[NSNull class]];
}];
return (null != nil);
}
@@ -18,23 +18,23 @@ - (BOOL)wmf_recursivelyContainsNullObjects {
__block BOOL hasNull = NO;
[self enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {

if ([obj isKindOfClass:[NSDictionary class]]) {
if ([obj isKindOfClass:[NSDictionary class]]) {

hasNull = [obj wmf_recursivelyContainsNullObjects];
if (hasNull) {
*stop = YES;
return;
}
}
hasNull = [obj wmf_recursivelyContainsNullObjects];
if (hasNull) {
*stop = YES;
return;
}
}

if ([obj isKindOfClass:[NSArray class]]) {
if ([obj isKindOfClass:[NSArray class]]) {

hasNull = [obj wmf_recursivelyContainsNullObjects];
if (hasNull) {
*stop = YES;
return;
}
}
hasNull = [obj wmf_recursivelyContainsNullObjects];
if (hasNull) {
*stop = YES;
return;
}
}

}];

@@ -43,7 +43,7 @@ - (BOOL)wmf_recursivelyContainsNullObjects {

- (NSDictionary *)wmf_dictionaryByRemovingNullObjects {
return [self bk_reject:^BOOL(id key, id obj) {
return [obj isKindOfClass:[NSNull class]];
return [obj isKindOfClass:[NSNull class]];
}];
}

@@ -12,11 +12,11 @@ - (nullable id)wmf_instanceOfClass:(Class)aClass
error:(NSError *_Nullable __autoreleasing *)outError {
NSParameterAssert(key);
NSError * (^errorWithCode)(WMFInvalidValueForKeyError) = ^(WMFInvalidValueForKeyError code) {
return [NSError errorWithDomain:WMFInvalidValueForKeyErrorDomain
code:code
userInfo:@{
WMFFailingDictionaryUserInfoKey : self
}];
return [NSError errorWithDomain:WMFInvalidValueForKeyErrorDomain
code:code
userInfo:@{
WMFFailingDictionaryUserInfoKey : self
}];
};
id value = self[key];
if (!value) {
@@ -4,37 +4,37 @@ @implementation NSHTTPCookieStorage (WMFCloneCookie)

- (void)wmf_recreateCookie:(NSString *)cookieToRecreate usingCookieAsTemplate:(NSString *)templateCookie {
void (^cloneCookie)(NSString *, NSString *) = ^void(NSString *name1, NSString *name2) {
NSUInteger (^getIndexOfCookie)(NSString *) = ^NSUInteger(NSString *name) {
return [self.cookies indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
NSHTTPCookie *cookie = (NSHTTPCookie *)obj;
if (cookie.properties[@"Name"]) {
if ([cookie.properties[@"Name"] isEqualToString:name]) {
*stop = YES;
return YES;
}
}
return NO;
}];
};

NSUInteger indexCookie1 = getIndexOfCookie(name1);
NSUInteger indexCookie2 = getIndexOfCookie(name2);

if ((indexCookie1 != NSNotFound) && (indexCookie2 != NSNotFound)) {
NSHTTPCookie *cookie1 = self.cookies[indexCookie1];
NSHTTPCookie *cookie2 = self.cookies[indexCookie2];
NSString *cookie1Name = cookie1.name;
NSString *cookie1Value = cookie1.value;

[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie1];

NSMutableDictionary *cookie2Props = [cookie2.properties mutableCopy];
cookie2Props[@"Created"] = [NSDate date];
cookie2Props[@"Name"] = cookie1Name;
cookie2Props[@"Value"] = cookie1Value;
NSHTTPCookie *newCookie = [NSHTTPCookie cookieWithProperties:cookie2Props];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:newCookie];
}
NSUInteger (^getIndexOfCookie)(NSString *) = ^NSUInteger(NSString *name) {
return [self.cookies indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
NSHTTPCookie *cookie = (NSHTTPCookie *)obj;
if (cookie.properties[@"Name"]) {
if ([cookie.properties[@"Name"] isEqualToString:name]) {
*stop = YES;
return YES;
}
}
return NO;
}];
};

NSUInteger indexCookie1 = getIndexOfCookie(name1);
NSUInteger indexCookie2 = getIndexOfCookie(name2);

if ((indexCookie1 != NSNotFound) && (indexCookie2 != NSNotFound)) {
NSHTTPCookie *cookie1 = self.cookies[indexCookie1];
NSHTTPCookie *cookie2 = self.cookies[indexCookie2];
NSString *cookie1Name = cookie1.name;
NSString *cookie1Value = cookie1.value;

[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie1];

NSMutableDictionary *cookie2Props = [cookie2.properties mutableCopy];
cookie2Props[@"Created"] = [NSDate date];
cookie2Props[@"Name"] = cookie1Name;
cookie2Props[@"Value"] = cookie1Value;
NSHTTPCookie *newCookie = [NSHTTPCookie cookieWithProperties:cookie2Props];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:newCookie];
}
};

cloneCookie(cookieToRecreate, templateCookie);
@@ -10,7 +10,7 @@ - (id)bk_reduce:(id)acc withBlock:(id (^)(id acc, NSUInteger idx))reducer {
}
__block id result = acc;
[self enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
result = reducer(acc, idx);
result = reducer(acc, idx);
}];
return result;
}
@@ -74,7 +74,7 @@ - (NSString *)wmf_stringByRemovingHTML {
- (NSString *)wmf_randomlyRepeatMaxTimes:(NSUInteger)maxTimes;
{
float (^rnd)() = ^() {
return (float)(rand() % (maxTimes - 1) + 1);
return (float)(rand() % (maxTimes - 1) + 1);
};

NSString *randStr = [@"" stringByPaddingToLength:rnd() * [self length] withString:self startingAtIndex:0];
@@ -45,9 +45,9 @@ - (NSString *)wmf_stringByCollapsingConsecutiveNewlines {
static NSRegularExpression *newlinesRegex;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
newlinesRegex = [NSRegularExpression regularExpressionWithPattern:@"\n{2,}"
options:0
error:nil];
newlinesRegex = [NSRegularExpression regularExpressionWithPattern:@"\n{2,}"
options:0
error:nil];
});
return [newlinesRegex stringByReplacingMatchesInString:self
options:0
@@ -60,10 +60,10 @@ - (NSString *)wmf_stringByRecursivelyRemovingParenthesizedContent {
static NSRegularExpression *parensRegex;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
parensRegex = [NSRegularExpression
regularExpressionWithPattern:@"[(][^()]+[)]"
options:0
error:nil];
parensRegex = [NSRegularExpression
regularExpressionWithPattern:@"[(][^()]+[)]"
options:0
error:nil];
});

NSString *string = [self copy];
@@ -86,10 +86,10 @@ - (NSString *)wmf_stringByRemovingBracketedContent {
static NSRegularExpression *bracketedRegex;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
bracketedRegex = [NSRegularExpression
regularExpressionWithPattern:@"\\[[^]]+]"
options:0
error:nil];
bracketedRegex = [NSRegularExpression
regularExpressionWithPattern:@"\\[[^]]+]"
options:0
error:nil];
});

return [bracketedRegex stringByReplacingMatchesInString:self
@@ -103,10 +103,10 @@ - (NSString *)wmf_stringByRemovingWhiteSpaceBeforePeriodsCommasSemicolonsAndDash
static NSRegularExpression *spacePeriodRegex;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
spacePeriodRegex = [NSRegularExpression
regularExpressionWithPattern:@"\\s+([\\.。.。,、;\\-\u2014])"
options:0
error:nil];
spacePeriodRegex = [NSRegularExpression
regularExpressionWithPattern:@"\\s+([\\.。.。,、;\\-\u2014])"
options:0
error:nil];
});

return [spacePeriodRegex stringByReplacingMatchesInString:self
@@ -121,10 +121,10 @@ - (NSString *)wmf_stringByCollapsingConsecutiveSpaces {
static NSRegularExpression *spacesRegex;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
spacesRegex = [NSRegularExpression
regularExpressionWithPattern:@" {2,}"
options:0
error:nil];
spacesRegex = [NSRegularExpression
regularExpressionWithPattern:@" {2,}"
options:0
error:nil];
});

return [spacesRegex stringByReplacingMatchesInString:self
@@ -137,10 +137,10 @@ - (NSString *)wmf_stringByCollapsingAllWhitespaceToSingleSpaces {
static NSRegularExpression *whitespaceRegex;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
whitespaceRegex = [NSRegularExpression
regularExpressionWithPattern:@"\\s+"
options:0
error:nil];
whitespaceRegex = [NSRegularExpression
regularExpressionWithPattern:@"\\s+"
options:0
error:nil];
});

return [whitespaceRegex stringByReplacingMatchesInString:self
@@ -160,10 +160,10 @@ - (NSString *)wmf_stringByRemovingLeadingOrTrailingSpacesNewlinesOrColons {
static NSRegularExpression *leadTrailColonRegex;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
leadTrailColonRegex = [NSRegularExpression
regularExpressionWithPattern:@"^[\\s\n]+|[\\s\n:]+$"
options:0
error:nil];
leadTrailColonRegex = [NSRegularExpression
regularExpressionWithPattern:@"^[\\s\n]+|[\\s\n:]+$"
options:0
error:nil];
});

return [leadTrailColonRegex stringByReplacingMatchesInString:self
@@ -202,20 +202,20 @@ - (void)wmf_enumerateHTMLImageTagContentsWithHandler:(nonnull void (^)(NSString
static NSRegularExpression *imageTagRegex;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *pattern = @"(?:<img\\s)([^>]*)(?:>)";
imageTagRegex = [NSRegularExpression regularExpressionWithPattern:pattern
options:NSRegularExpressionCaseInsensitive
error:nil];
NSString *pattern = @"(?:<img\\s)([^>]*)(?:>)";
imageTagRegex = [NSRegularExpression regularExpressionWithPattern:pattern
options:NSRegularExpressionCaseInsensitive
error:nil];
});

[imageTagRegex enumerateMatchesInString:self
options:0
range:NSMakeRange(0, self.length)
usingBlock:^(NSTextCheckingResult *_Nullable imageTagResult, NSMatchingFlags flags, BOOL *_Nonnull stop) {
//get just the image tag contents - everything between <img and >
NSString *imageTagContents = [imageTagRegex replacementStringForResult:imageTagResult inString:self offset:0 template:@"$1"];
handler(imageTagContents, imageTagResult.range);
*stop = false;
//get just the image tag contents - everything between <img and >
NSString *imageTagContents = [imageTagRegex replacementStringForResult:imageTagResult inString:self offset:0 template:@"$1"];
handler(imageTagContents, imageTagResult.range);
*stop = false;
}];
}

@@ -64,7 +64,7 @@ + (NSRegularExpression *)invalidPercentEscapesRegex {
static dispatch_once_t onceToken;
static NSRegularExpression *percentEscapesRegex;
dispatch_once(&onceToken, ^{
percentEscapesRegex = [NSRegularExpression regularExpressionWithPattern:@"%[^0-9A-F]|%[0-9A-F][^0-9A-F]" options:NSRegularExpressionCaseInsensitive error:nil];
percentEscapesRegex = [NSRegularExpression regularExpressionWithPattern:@"%[^0-9A-F]|%[0-9A-F][^0-9A-F]" options:NSRegularExpressionCaseInsensitive error:nil];
});
return percentEscapesRegex;
}
@@ -5,7 +5,7 @@ @implementation NSURL (WMFQueryParameters)
- (nullable NSString *)wmf_valueForQueryKey:(NSString *)key {
NSURLQueryItem *matchingItem = [[[NSURLComponents componentsWithURL:self resolvingAgainstBaseURL:YES] queryItems]
bk_match:^BOOL(NSURLQueryItem *item) {
return [item.name isEqualToString:key];
return [item.name isEqualToString:key];
}];
return matchingItem.value;
}
@@ -17,11 +17,11 @@ - (NSURL *)wmf_urlWithValue:(NSString *)value forQueryKey:(NSString *)key {
// Change the value if the key already exists.
NSArray<NSURLQueryItem *> *queryItems = [components.queryItems
bk_map:^id(NSURLQueryItem *item) {
if ([item.name isEqualToString:key]) {
return [NSURLQueryItem queryItemWithName:item.name value:value];
} else {
return item;
}
if ([item.name isEqualToString:key]) {
return [NSURLQueryItem queryItemWithName:item.name value:value];
} else {
return item;
}
}];
components.queryItems = queryItems;
} else {
@@ -43,7 +43,7 @@ - (NSURL *)wmf_urlWithoutQueryKey:(NSString *)key {
NSURLComponents *components = [NSURLComponents componentsWithURL:self resolvingAgainstBaseURL:YES];
NSArray<NSURLQueryItem *> *queryItems = [components.queryItems
bk_select:^BOOL(NSURLQueryItem *item) {
return ([item.name isEqualToString:key]) ? NO : YES;
return ([item.name isEqualToString:key]) ? NO : YES;
}];
components.queryItems = queryItems;
return components.URL;
@@ -174,11 +174,11 @@ - (NSString *)wmf_searchTerm {
NSURLComponents *components = [NSURLComponents componentsWithString:self.webpageURL.absoluteString];
NSArray *queryItems = components.queryItems;
NSURLQueryItem *item = [queryItems bk_match:^BOOL(NSURLQueryItem *obj) {
if ([[obj name] isEqualToString:@"search"]) {
return YES;
} else {
return NO;
}
if ([[obj name] isEqualToString:@"search"]) {
return YES;
} else {
return NO;
}
}];

return [item value];
@@ -52,9 +52,9 @@ - (void)viewDidLoad {
@weakify(self)
UIBarButtonItem *xButton = [UIBarButtonItem wmf_buttonType:WMFButtonTypeX
handler:^(id sender) {
@strongify(self)
[self dismissViewControllerAnimated:YES
completion:nil];
@strongify(self)
[self dismissViewControllerAnimated:YES
completion:nil];
}];
self.navigationItem.leftBarButtonItem = xButton;

@@ -77,21 +77,21 @@ - (void)getPageHistoryData {

@weakify(self);
[self.pageHistoryFetcher fetchRevisionInfo:self.article.url requestParams:self.historyFetcherParams].then(^(HistoryFetchResults *historyFetchResults) {
@strongify(self);
[self.pageHistoryDataArray addObjectsFromArray:historyFetchResults.items];
self.historyFetcherParams = [historyFetchResults getPageHistoryRequestParameters:self.article.url];
self.batchComplete = historyFetchResults.batchComplete;
[[WMFAlertManager sharedInstance] dismissAlert];
[self.tableView reloadData];
@strongify(self);
[self.pageHistoryDataArray addObjectsFromArray:historyFetchResults.items];
self.historyFetcherParams = [historyFetchResults getPageHistoryRequestParameters:self.article.url];
self.batchComplete = historyFetchResults.batchComplete;
[[WMFAlertManager sharedInstance] dismissAlert];
[self.tableView reloadData];
})
.catch(^(NSError *error) {
@strongify(self);
DDLogError(@"Failed to fetch items for section %@. %@", self, error);
[[WMFAlertManager sharedInstance] showErrorAlert:error sticky:YES dismissPreviousAlerts:NO tapCallBack:NULL];
@strongify(self);
DDLogError(@"Failed to fetch items for section %@. %@", self, error);
[[WMFAlertManager sharedInstance] showErrorAlert:error sticky:YES dismissPreviousAlerts:NO tapCallBack:NULL];
})
.finally(^{
@strongify(self);
self.isLoadingData = NO;
@strongify(self);
self.isLoadingData = NO;
});
}

@@ -208,28 +208,28 @@ - (void)viewDidLoad {
@weakify(self)
self.buttonX = [UIBarButtonItem wmf_buttonType:WMFButtonTypeX
handler:^(id sender) {
@strongify(self)
[self goBack];
@strongify(self)
[self goBack];
}];

self.buttonLeftCaret = [UIBarButtonItem wmf_buttonType:WMFButtonTypeCaretLeft
handler:^(id sender) {
@strongify(self)
[self goBack];
@strongify(self)
[self goBack];
}];

self.buttonSave = [[UIBarButtonItem alloc] bk_initWithTitle:MWLocalizedString(@"button-save", nil)
style:UIBarButtonItemStylePlain
handler:^(id sender) {
@strongify(self)
[self goForward];
@strongify(self)
[self goForward];
}];

self.buttonNext = [[UIBarButtonItem alloc] bk_initWithTitle:MWLocalizedString(@"button-next", nil)
style:UIBarButtonItemStylePlain
handler:^(id sender) {
@strongify(self)
[self goForward];
@strongify(self)
[self goForward];
}];

self.mode = PREVIEW_MODE_EDIT_WIKITEXT_PREVIEW;
@@ -511,17 +511,17 @@ - (void)fetchFinished:(id)sender
EditTokenFetcher *tokenFetcher = (EditTokenFetcher *)sender;

void (^upload)() = ^void() {
NSMutableDictionary *editTokens = self.keychainCredentials.editTokens;
NSString *editToken = editTokens[tokenFetcher.articleURL.wmf_language];
(void)[[WikiTextSectionUploader alloc] initAndUploadWikiText:tokenFetcher.wikiText
forArticleURL:tokenFetcher.articleURL
section:tokenFetcher.section
summary:tokenFetcher.summary
captchaId:tokenFetcher.captchaId
captchaWord:tokenFetcher.captchaWord
token:editToken
withManager:[QueuesSingleton sharedInstance].sectionWikiTextUploadManager
thenNotifyDelegate:self];
NSMutableDictionary *editTokens = self.keychainCredentials.editTokens;
NSString *editToken = editTokens[tokenFetcher.articleURL.wmf_language];
(void)[[WikiTextSectionUploader alloc] initAndUploadWikiText:tokenFetcher.wikiText
forArticleURL:tokenFetcher.articleURL
section:tokenFetcher.section
summary:tokenFetcher.summary
captchaId:tokenFetcher.captchaId
captchaWord:tokenFetcher.captchaWord
token:editToken
withManager:[QueuesSingleton sharedInstance].sectionWikiTextUploadManager
thenNotifyDelegate:self];
};

switch (status) {
@@ -554,7 +554,7 @@ - (void)fetchFinished:(id)sender
case FETCH_FINAL_STATUS_SUCCEEDED: {
[self.funnel logSavedRevision:[fetchedData[@"newrevid"] intValue]];
dispatchOnMainQueue(^{
[self.delegate previewViewControllerDidSave:self];
[self.delegate previewViewControllerDidSave:self];
});
} break;

@@ -639,26 +639,26 @@ - (void)showImageForCaptcha {
MWKArticle *article = self.section.article;
[UIView animateWithDuration:0.2f
animations:^{
[self revealCaptcha];
[self revealCaptcha];

[self.captchaViewController.captchaTextBox performSelector:@selector(becomeFirstResponder)
withObject:nil
afterDelay:0.4f];
[self.captchaViewController.captchaTextBox performSelector:@selector(becomeFirstResponder)
withObject:nil
afterDelay:0.4f];

self.captchaViewController.captchaImageView.image = nil;
self.captchaViewController.captchaImageView.image = nil;

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
NSURL *captchaImageUrl = [NSURL URLWithString:
[NSString stringWithFormat:@"https://%@.m.%@%@", article.url.wmf_language, article.url.wmf_domain, self.captchaUrl]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
NSURL *captchaImageUrl = [NSURL URLWithString:
[NSString stringWithFormat:@"https://%@.m.%@%@", article.url.wmf_language, article.url.wmf_domain, self.captchaUrl]];

UIImage *captchaImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:captchaImageUrl]];
UIImage *captchaImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:captchaImageUrl]];

dispatch_async(dispatch_get_main_queue(), ^(void) {
self.captchaViewController.captchaTextBox.text = @"";
self.captchaViewController.captchaImageView.image = captchaImage;
[self.view layoutIfNeeded];
dispatch_async(dispatch_get_main_queue(), ^(void) {
self.captchaViewController.captchaTextBox.text = @"";
self.captchaViewController.captchaImageView.image = captchaImage;
[self.view layoutIfNeeded];
});
});
});
}
completion:^(BOOL done){
}];
@@ -668,10 +668,10 @@ - (void)preview {
[[WMFAlertManager sharedInstance] showAlert:MWLocalizedString(@"wikitext-preview-changes", nil) sticky:YES dismissPreviousAlerts:YES tapCallBack:NULL];

[[QueuesSingleton sharedInstance].sectionPreviewHtmlFetchManager wmf_cancelAllTasksWithCompletionHandler:^{
(void)[[PreviewHtmlFetcher alloc] initAndFetchHtmlForWikiText:self.wikiText
articleURL:self.section.url
withManager:[QueuesSingleton sharedInstance].sectionPreviewHtmlFetchManager
thenNotifyDelegate:self];
(void)[[PreviewHtmlFetcher alloc] initAndFetchHtmlForWikiText:self.wikiText
articleURL:self.section.url
withManager:[QueuesSingleton sharedInstance].sectionPreviewHtmlFetchManager
thenNotifyDelegate:self];
}];
}

@@ -689,22 +689,22 @@ - (void)save {
}

[[QueuesSingleton sharedInstance].sectionWikiTextUploadManager wmf_cancelAllTasksWithCompletionHandler:^{
// If fromTitle was set, the section was transcluded, so use the title of the page
// it was transcluded from.
NSURL *editURL = self.section.fromURL ? self.section.fromURL : self.section.article.url;

// First try to get an edit token for the page's domain before trying to upload the changes.
// Only the domain is used to actually fetch the token, the other values are
// parked in EditTokenFetcher so the actual uploader can have quick read-only
// access to the exact params which kicked off the token request.
(void)[[EditTokenFetcher alloc] initAndFetchEditTokenForWikiText:self.wikiText
articleURL:editURL
section:[NSString stringWithFormat:@"%d", self.section.sectionId]
summary:[self getSummary]
captchaId:self.captchaId
captchaWord:self.captchaViewController.captchaTextBox.text
withManager:[QueuesSingleton sharedInstance].sectionWikiTextUploadManager
thenNotifyDelegate:self];
// If fromTitle was set, the section was transcluded, so use the title of the page
// it was transcluded from.
NSURL *editURL = self.section.fromURL ? self.section.fromURL : self.section.article.url;

// First try to get an edit token for the page's domain before trying to upload the changes.
// Only the domain is used to actually fetch the token, the other values are
// parked in EditTokenFetcher so the actual uploader can have quick read-only
// access to the exact params which kicked off the token request.
(void)[[EditTokenFetcher alloc] initAndFetchEditTokenForWikiText:self.wikiText
articleURL:editURL
section:[NSString stringWithFormat:@"%d", self.section.sectionId]
summary:[self getSummary]
captchaId:self.captchaId
captchaWord:self.captchaViewController.captchaTextBox.text
withManager:[QueuesSingleton sharedInstance].sectionWikiTextUploadManager
thenNotifyDelegate:self];
}];
}

@@ -737,9 +737,9 @@ - (void)reloadCaptchaPushed:(id)sender {
self.captchaViewController.captchaTextBox.text = @"";
[[WMFAlertManager sharedInstance] showAlert:MWLocalizedString(@"account-creation-captcha-obtaining", nil) sticky:NO dismissPreviousAlerts:YES tapCallBack:NULL];
[[QueuesSingleton sharedInstance].sectionWikiTextUploadManager wmf_cancelAllTasksWithCompletionHandler:^{
(void)[[CaptchaResetter alloc] initAndResetCaptchaForDomain:[SessionSingleton sharedInstance].currentArticleSiteURL.wmf_language
withManager:[QueuesSingleton sharedInstance].sectionWikiTextUploadManager
thenNotifyDelegate:self];
(void)[[CaptchaResetter alloc] initAndResetCaptchaForDomain:[SessionSingleton sharedInstance].currentArticleSiteURL.wmf_language
withManager:[QueuesSingleton sharedInstance].sectionWikiTextUploadManager
thenNotifyDelegate:self];
}];
}

@@ -780,12 +780,12 @@ - (void)previewLicenseViewTermsLicenseLabelWasTapped:(PreviewLicenseView *)previ
[sheet addAction:[UIAlertAction actionWithTitle:MWLocalizedString(@"wikitext-upload-save-terms-name", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) {
[self wmf_openExternalUrl:[NSURL URLWithString:TERMS_LINK]];
[self wmf_openExternalUrl:[NSURL URLWithString:TERMS_LINK]];
}]];
[sheet addAction:[UIAlertAction actionWithTitle:MWLocalizedString(@"wikitext-upload-save-license-name", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) {
[self wmf_openExternalUrl:[NSURL URLWithString:LICENSE_LINK]];
[self wmf_openExternalUrl:[NSURL URLWithString:LICENSE_LINK]];
}]];
[sheet addAction:[UIAlertAction actionWithTitle:MWLocalizedString(@"open-link-cancel", nil) style:UIAlertActionStyleCancel handler:NULL]];
[self presentViewController:sheet animated:YES completion:NULL];
@@ -34,39 +34,39 @@ - (void)fetchPreviewForWikiText:(NSString *)wikiText
parameters:params
progress:NULL
success:^(NSURLSessionDataTask *operation, id responseObject) {
//NSLog(@"JSON: %@", responseObject);
[[MWNetworkActivityIndicatorManager sharedManager] pop];

// Fake out an error if non-dictionary response received.
if (![responseObject isDict]) {
responseObject = @{ @"error" : @{@"info" : @"Preview not found."} };
}

//NSLog(@"PREVIEW HTML DATA RETRIEVED = %@", responseObject);

// Handle case where response is received, but API reports error.
NSError *error = nil;
if (responseObject[@"error"]) {
NSMutableDictionary *errorDict = [responseObject[@"error"] mutableCopy];
errorDict[NSLocalizedDescriptionKey] = errorDict[@"info"];
error = [NSError errorWithDomain:@"Preview HTML Fetcher" code:001 userInfo:errorDict];
}

NSString *output = @"";
if (!error) {
output = [self getSanitizedResponse:responseObject];
}

[self finishWithError:error
fetchedData:output];
//NSLog(@"JSON: %@", responseObject);
[[MWNetworkActivityIndicatorManager sharedManager] pop];

// Fake out an error if non-dictionary response received.
if (![responseObject isDict]) {
responseObject = @{ @"error" : @{@"info" : @"Preview not found."} };
}

//NSLog(@"PREVIEW HTML DATA RETRIEVED = %@", responseObject);

// Handle case where response is received, but API reports error.
NSError *error = nil;
if (responseObject[@"error"]) {
NSMutableDictionary *errorDict = [responseObject[@"error"] mutableCopy];
errorDict[NSLocalizedDescriptionKey] = errorDict[@"info"];
error = [NSError errorWithDomain:@"Preview HTML Fetcher" code:001 userInfo:errorDict];
}

NSString *output = @"";
if (!error) {
output = [self getSanitizedResponse:responseObject];
}

[self finishWithError:error
fetchedData:output];
}
failure:^(NSURLSessionDataTask *operation, NSError *error) {
//NSLog(@"PREVIEW HTML FAIL = %@", error);
//NSLog(@"PREVIEW HTML FAIL = %@", error);

[[MWNetworkActivityIndicatorManager sharedManager] pop];
[[MWNetworkActivityIndicatorManager sharedManager] pop];

[self finishWithError:error
fetchedData:nil];
[self finishWithError:error
fetchedData:nil];
}];
}

@@ -60,7 +60,7 @@ - (void)awakeFromNib {
self.webView.navigationDelegate = self;
self.userInteractionEnabled = YES;
[self.webView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.bottom.leading.and.trailing.equalTo(self.webView.superview);
make.top.bottom.leading.and.trailing.equalTo(self.webView.superview);
}];
}

@@ -12,7 +12,7 @@ + (QueuesSingleton *)sharedInstance {
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
@@ -70,15 +70,15 @@ - (void)setupTrashButton {
self.trashButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.trashButton setImage:[UIImage imageNamed:@"clear-mini"] forState:UIControlStateNormal];
[self.trashButton bk_addEventHandler:^(UIButton *sender) {
@strongify(self)
[self showDeleteAllDialog];
@strongify(self)
[self showDeleteAllDialog];
}
forControlEvents:UIControlEventTouchUpInside];
self.trashButton.tintColor = [UIColor wmf_lightGrayColor];
[self.trashButtonContainer addSubview:self.trashButton];

[self.trashButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.trailing.top.and.bottom.equalTo(self.trashButtonContainer);
make.leading.trailing.top.and.bottom.equalTo(self.trashButtonContainer);
}];

self.trashButton.accessibilityLabel = MWLocalizedString(@"menu-trash-accessibility-label", nil);
@@ -357,17 +357,17 @@ - (void)setViewControllers:(NSArray *)viewControllers
direction:direction
animated:animated
completion:^(BOOL finished) {
if (!weakSelf.pageController) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.pageController setViewControllers:viewControllers
direction:direction
animated:NO
completion:^(BOOL done) {
[weakSelf refViewDidAppear:weakSelf.topPageControl.currentPage];
}];
});
if (!weakSelf.pageController) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.pageController setViewControllers:viewControllers
direction:direction
animated:NO
completion:^(BOOL done) {
[weakSelf refViewDidAppear:weakSelf.topPageControl.currentPage];
}];
});
}];
}

@@ -18,8 +18,8 @@ - (void)wmf_removeImageForURL:(NSURL *__nullable)URL fromDisk:(BOOL)fromDisk {
withCompletion:
#if LOG_POST_REMOVAL_CACHE_SIZE
^{
@strongify(self)
[self wmf_calculateAndLogCacheSize];
@strongify(self)
[self wmf_calculateAndLogCacheSize];
}
#else
nil
@@ -43,11 +43,11 @@ - (void)wmf_removeImageURLs:(NSArray *__nonnull)URLs fromDisk:(BOOL)fromDisk {
#if LOG_POST_REMOVAL_CACHE_SIZE
- (void)wmf_calculateAndLogCacheSize {
[self.imageCache calculateSizeWithCompletionBlock:^(NSUInteger fileCount, NSUInteger totalSize) {
DDLogInfo(@"Current cache size:\n"
"\t- files: %lu\n"
"\t- totalSize: %lu\n",
(unsigned long)fileCount,
(unsigned long)totalSize);
DDLogInfo(@"Current cache size:\n"
"\t- files: %lu\n"
"\t- totalSize: %lu\n",
(unsigned long)fileCount,
(unsigned long)totalSize);
}];
}

@@ -131,7 +131,7 @@ - (void)fetchUncachedEntries:(NSArray<MWKHistoryEntry *> *)insertedEntries {

- (void)fetchUncachedArticlesInSavedPages {
dispatch_block_t didFinishLegacyMigration = ^{
[[NSUserDefaults standardUserDefaults] wmf_setDidFinishLegacySavedArticleImageMigration:YES];
[[NSUserDefaults standardUserDefaults] wmf_setDidFinishLegacySavedArticleImageMigration:YES];
};
if ([self.savedPageList numberOfItems] == 0) {
didFinishLegacyMigration();
@@ -140,18 +140,18 @@ - (void)fetchUncachedArticlesInSavedPages {

WMFTaskGroup *group = [WMFTaskGroup new];
[self.savedPageList enumerateItemsWithBlock:^(MWKHistoryEntry *_Nonnull entry, BOOL *_Nonnull stop) {
[group enter];
dispatch_async(self.accessQueue, ^{
@autoreleasepool {
[self fetchArticleURL:entry.url
failure:^(NSError *error) {
[group leave];
}
success:^{
[group leave];
}];
}
});
[group enter];
dispatch_async(self.accessQueue, ^{
@autoreleasepool {
[self fetchArticleURL:entry.url
failure:^(NSError *error) {
[group leave];
}
success:^{
[group leave];
}];
}
});
}];
[group waitInBackgroundWithCompletion:didFinishLegacyMigration];
}
@@ -162,11 +162,11 @@ - (void)fetchUncachedArticleURLs:(NSArray<NSURL *> *)urls {
}
for (NSURL *url in urls) {
dispatch_async(self.accessQueue, ^{
[self fetchArticleURL:url
failure:^(NSError *error) {
}
success:^{
}];
[self fetchArticleURL:url
failure:^(NSError *error) {
}
success:^{
}];
});
}
}
@@ -187,28 +187,28 @@ - (void)fetchArticleURL:(NSURL *)articleURL failure:(WMFErrorHandler)failure suc
[self.articleFetcher fetchArticleForURL:articleURL
progress:NULL]
.thenOn(self.accessQueue, ^(MWKArticle *article) {
@strongify(self);
[self downloadImageDataForArticle:article
failure:^(NSError *error) {
dispatch_async(self.accessQueue, ^{
[self didFetchArticle:article url:articleURL error:error];
failure(error);
});
}
success:^{
dispatch_async(self.accessQueue, ^{
[self didFetchArticle:article url:articleURL error:nil];
success();
});
}];
@strongify(self);
[self downloadImageDataForArticle:article
failure:^(NSError *error) {
dispatch_async(self.accessQueue, ^{
[self didFetchArticle:article url:articleURL error:error];
failure(error);
});
}
success:^{
dispatch_async(self.accessQueue, ^{
[self didFetchArticle:article url:articleURL error:nil];
success();
});
}];
})
.catch(^(NSError *error) {
if (!self) {
return;
}
dispatch_async(self.accessQueue, ^{
[self didFetchArticle:nil url:articleURL error:error];
});
if (!self) {
return;
}
dispatch_async(self.accessQueue, ^{
[self didFetchArticle:nil url:articleURL error:error];
});
});
}
}
@@ -220,14 +220,14 @@ - (void)downloadImageDataForArticle:(MWKArticle *)article failure:(WMFErrorHandl
}
[self fetchAllImagesInArticle:article
failure:^(NSError *error) {
failure([NSError wmf_savedPageImageDownloadError]);
failure([NSError wmf_savedPageImageDownloadError]);
}
success:^{
//NOTE: turning off gallery image fetching as users are potentially downloading large amounts of data up front when upgrading to a new version of the app.
// [self fetchGalleryDataForArticle:article failure:failure success:success];
if (success) {
success();
}
//NOTE: turning off gallery image fetching as users are potentially downloading large amounts of data up front when upgrading to a new version of the app.
// [self fetchGalleryDataForArticle:article failure:failure success:success];
if (success) {
success();
}
}];
}

@@ -284,22 +284,22 @@ - (void)fetchGalleryDataForArticle:(MWKArticle *)article failure:(WMFErrorHandle

[self fetchImageInfoForImagesInArticle:article
failure:^(NSError *error) {
failure(error);
failure(error);
}
success:^(NSArray *info) {
@strongify(self);
if (!self) {
failure([NSError cancelledError]);
return;
}
if (info.count == 0) {
DDLogVerbose(@"No gallery images to fetch.");
success();
return;
}

NSArray *URLs = [info valueForKey:@"imageThumbURL"];
[self cacheImagesWithURLsInBackground:URLs failure:failure success:success];
@strongify(self);
if (!self) {
failure([NSError cancelledError]);
return;
}
if (info.count == 0) {
DDLogVerbose(@"No gallery images to fetch.");
success();
return;
}

NSArray *URLs = [info valueForKey:@"imageThumbURL"];
[self cacheImagesWithURLsInBackground:URLs failure:failure success:success];
}];
}

@@ -319,23 +319,23 @@ - (void)fetchImageInfoForImagesInArticle:(MWKArticle *)article failure:(WMFError
}

PMKJoin([[imageFileTitles bk_map:^AnyPromise *(NSString *canonicalFilename) {
return [self.imageInfoFetcher fetchGalleryInfoForImage:canonicalFilename fromSiteURL:article.url];
return [self.imageInfoFetcher fetchGalleryInfoForImage:canonicalFilename fromSiteURL:article.url];
}] bk_reject:^BOOL(id obj) {
return [obj isEqual:[NSNull null]];
return [obj isEqual:[NSNull null]];
}]).thenInBackground(^id(NSArray *infoObjects) {
@strongify(self);
if (!self) {
return [NSError cancelledError];
}
[self.dataStore saveImageInfo:infoObjects forArticleURL:article.url];
success(infoObjects);
return infoObjects;
@strongify(self);
if (!self) {
return [NSError cancelledError];
}
[self.dataStore saveImageInfo:infoObjects forArticleURL:article.url];
success(infoObjects);
return infoObjects;
});
}

- (void)cacheImagesWithURLsInBackground:(NSArray<NSURL *> *)imageURLs failure:(void (^_Nonnull)(NSError *_Nonnull error))failure success:(void (^_Nonnull)(void))success {
imageURLs = [imageURLs bk_select:^BOOL(id obj) {
return [obj isKindOfClass:[NSURL class]];
return [obj isKindOfClass:[NSURL class]];
}];

if ([imageURLs count] == 0) {
@@ -351,9 +351,9 @@ - (void)cacheImagesWithURLsInBackground:(NSArray<NSURL *> *)imageURLs failure:(v
- (void)cancelFetchForSavedPages {
BOOL wasFetching = self.fetchOperationsByArticleTitle.count > 0;
[self.savedPageList enumerateItemsWithBlock:^(MWKHistoryEntry *_Nonnull entry, BOOL *_Nonnull stop) {
dispatch_async(self.accessQueue, ^{
[self cancelFetchForArticleURL:entry.url];
});
dispatch_async(self.accessQueue, ^{
[self cancelFetchForArticleURL:entry.url];
});
}];
if (wasFetching) {
/*
@@ -368,7 +368,7 @@ - (void)cancelFetchForArticleURL:(NSURL *)URL {
DDLogVerbose(@"Canceling saved page download for title: %@", URL);
[self.articleFetcher cancelFetchForArticleURL:URL];
[[[self.dataStore existingArticleWithURL:URL] allImageURLs] bk_each:^(NSURL *imageURL) {
[self.imageController cancelFetchForURL:imageURL];
[self.imageController cancelFetchForURL:imageURL];
}];
WMF_TECH_DEBT_TODO(cancel image info & high - res image requests)
[self.fetchOperationsByArticleTitle removeObjectForKey:URL];
@@ -378,11 +378,11 @@ - (void)cancelFetchForArticleURL:(NSURL *)URL {

- (void)getProgress:(WMFProgressHandler)progressBlock {
dispatch_async(self.accessQueue, ^{
CGFloat progress = [self progress];
CGFloat progress = [self progress];

dispatch_async(dispatch_get_main_queue(), ^{
progressBlock(progress);
});
dispatch_async(dispatch_get_main_queue(), ^{
progressBlock(progress);
});
});
}

@@ -418,11 +418,11 @@ - (void)didFetchArticle:(MWKArticle *__nullable)fetchedArticle

CGFloat progress = [self progress];
dispatch_async(dispatch_get_main_queue(), ^{
[self.fetchFinishedDelegate savedArticlesFetcher:self
didFetchURL:url
article:fetchedArticle
progress:progress
error:error];
[self.fetchFinishedDelegate savedArticlesFetcher:self
didFetchURL:url
article:fetchedArticle
progress:progress
error:error];
});

[self notifyDelegateIfFinished];
@@ -43,23 +43,23 @@ - (void)viewDidLoad {
@weakify(self)
UIBarButtonItem *buttonX = [UIBarButtonItem wmf_buttonType:WMFButtonTypeX
handler:^(id sender) {
@strongify(self)
[self.delegate sectionEditorFinishedEditing:self];
@strongify(self)
[self.delegate sectionEditorFinishedEditing:self];
}];
buttonX.accessibilityLabel = MWLocalizedString(@"back-button-accessibility-label", nil);
self.navigationItem.leftBarButtonItem = buttonX;

self.rightButton = [[UIBarButtonItem alloc] bk_initWithTitle:MWLocalizedString(@"button-next", nil)
style:UIBarButtonItemStylePlain
handler:^(id sender) {
@strongify(self)

if (![self changesMade]) {
[[WMFAlertManager sharedInstance] showAlert:MWLocalizedString(@"wikitext-preview-changes-none", nil) sticky:NO dismissPreviousAlerts:YES tapCallBack:NULL];
}
else {
[self preview];
}
@strongify(self)

if (![self changesMade]) {
[[WMFAlertManager sharedInstance] showAlert:MWLocalizedString(@"wikitext-preview-changes-none", nil) sticky:NO dismissPreviousAlerts:YES tapCallBack:NULL];
}
else {
[self preview];
}
}];
self.navigationItem.rightBarButtonItem = self.rightButton;

@@ -172,9 +172,9 @@ - (void)loadLatestWikiTextForSectionFromServer {
[[WMFAlertManager sharedInstance] showAlert:MWLocalizedString(@"wikitext-downloading", nil) sticky:YES dismissPreviousAlerts:YES tapCallBack:NULL];

[[QueuesSingleton sharedInstance].sectionWikiTextDownloadManager wmf_cancelAllTasksWithCompletionHandler:^{
(void)[[WikiTextSectionFetcher alloc] initAndFetchWikiTextForSection:self.section
withManager:[QueuesSingleton sharedInstance].sectionWikiTextDownloadManager
thenNotifyDelegate:self];
(void)[[WikiTextSectionFetcher alloc] initAndFetchWikiTextForSection:self.section
withManager:[QueuesSingleton sharedInstance].sectionWikiTextDownloadManager
thenNotifyDelegate:self];
}];
}

@@ -27,7 +27,7 @@ + (SessionSingleton *)sharedInstance {
static dispatch_once_t onceToken;
static SessionSingleton *sharedInstance;
dispatch_once(&onceToken, ^{
sharedInstance = [self new];
sharedInstance = [self new];
});
return sharedInstance;
}