ReadViewSDK/Doc/WXRead/decompiled/WRBookmark.m

220 lines
7.2 KiB
Objective-C

//
// WRBookmark.m
// WeRead (读书)
// Reverse-engineered implementation reconstruction
//
// Detailed pseudo-code based on binary analysis (many NSString ivars,
// WRBook, WRMPReview, NSArray, NSDictionary, NSMutableSet) and
// contextual knowledge of WeRead's bookmark/annotation system.
//
#import "WRBookmark.h"
// ---------------------------------------------------------------------------
// Color mapping
// ---------------------------------------------------------------------------
static NSDictionary<NSString *, UIColor *> *sColorMap = nil;
@implementation WRBookmark
#pragma mark - Class Initialization
+ (void)initialize
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sColorMap = @{
@"yellow" : [UIColor colorWithRed:1.0 green:0.9 blue:0.3 alpha:0.4],
@"blue" : [UIColor colorWithRed:0.3 green:0.6 blue:1.0 alpha:0.4],
@"red" : [UIColor colorWithRed:1.0 green:0.3 blue:0.3 alpha:0.4],
@"green" : [UIColor colorWithRed:0.3 green:0.9 blue:0.4 alpha:0.4],
@"purple" : [UIColor colorWithRed:0.7 green:0.3 blue:0.9 alpha:0.4],
};
});
}
#pragma mark - Factory Methods
+ (instancetype)bookmarkWithBookId:(NSString *)bookId
chapterUid:(NSString *)chapterUid
offset:(NSInteger)offset
text:(NSString *)text
type:(WRBookmarkType)type
{
WRBookmark *bm = [[WRBookmark alloc] init];
bm.bookId = bookId;
bm.chapterUid = chapterUid;
bm.chapterOffset = offset;
bm.markText = text;
bm.type = type;
bm.createTime = [[NSDate date] timeIntervalSince1970];
bm.updateTime = bm.createTime;
bm.isSynced = NO;
// Generate a unique bookmark ID
bm.bookmarkId = [[NSUUID UUID] UUIDString];
return bm;
}
+ (instancetype)highlightWithBookId:(NSString *)bookId
chapterUid:(NSString *)chapterUid
startPos:(NSInteger)startPos
endPos:(NSInteger)endPos
text:(NSString *)text
colorStyle:(NSString *)colorStyle
{
WRBookmark *bm = [self bookmarkWithBookId:bookId
chapterUid:chapterUid
offset:startPos
text:text
type:WRBookmarkTypeHighlight];
bm.startPos = startPos;
bm.endPos = endPos;
bm.rangeLength = endPos - startPos;
bm.colorStyle = colorStyle ?: @"yellow";
return bm;
}
#pragma mark - Serialization
- (NSDictionary *)toDictionary
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
if (_bookmarkId) dict[@"bookmarkId"] = _bookmarkId;
if (_bookId) dict[@"bookId"] = _bookId;
if (_chapterUid) dict[@"chapterUid"] = _chapterUid;
dict[@"chapterOffset"] = @(_chapterOffset);
dict[@"chapterIndex"] = @(_chapterIndex);
if (_markText) dict[@"markText"] = _markText;
if (_noteContent) dict[@"noteContent"] = _noteContent;
dict[@"type"] = @(_type);
if (_colorStyle) dict[@"colorStyle"] = _colorStyle;
dict[@"startPos"] = @(_startPos);
dict[@"endPos"] = @(_endPos);
dict[@"rangeLength"] = @(_rangeLength);
if (_anchorId) dict[@"anchorId"] = _anchorId;
if (_rangeKey) dict[@"rangeKey"] = _rangeKey;
dict[@"createTime"] = @(_createTime);
dict[@"updateTime"] = @(_updateTime);
dict[@"isSynced"] = @(_isSynced);
if (_syncKey) dict[@"syncKey"] = _syncKey;
if (_rangeInfo) dict[@"rangeInfo"] = _rangeInfo;
if (_extraMetadata) dict[@"extraMetadata"] = _extraMetadata;
if (_tags.count > 0) {
dict[@"tags"] = [_tags allObjects];
}
return [dict copy];
}
+ (nullable instancetype)fromDictionary:(NSDictionary *)dict
{
if (!dict) return nil;
WRBookmark *bm = [[WRBookmark alloc] init];
bm.bookmarkId = dict[@"bookmarkId"];
bm.bookId = dict[@"bookId"];
bm.chapterUid = dict[@"chapterUid"];
bm.chapterOffset = [dict[@"chapterOffset"] integerValue];
bm.chapterIndex = [dict[@"chapterIndex"] integerValue];
bm.markText = dict[@"markText"];
bm.noteContent = dict[@"noteContent"];
bm.type = [dict[@"type"] integerValue];
bm.colorStyle = dict[@"colorStyle"];
bm.startPos = [dict[@"startPos"] integerValue];
bm.endPos = [dict[@"endPos"] integerValue];
bm.rangeLength = [dict[@"rangeLength"] integerValue];
bm.anchorId = dict[@"anchorId"];
bm.rangeKey = dict[@"rangeKey"];
bm.createTime = [dict[@"createTime"] doubleValue];
bm.updateTime = [dict[@"updateTime"] doubleValue];
bm.isSynced = [dict[@"isSynced"] boolValue];
bm.syncKey = dict[@"syncKey"];
bm.rangeInfo = dict[@"rangeInfo"];
bm.extraMetadata = dict[@"extraMetadata"];
NSArray *tags = dict[@"tags"];
if (tags) {
bm.tags = [NSMutableSet setWithArray:tags];
}
return bm;
}
- (nullable NSData *)toJSONData
{
NSDictionary *dict = [self toDictionary];
return [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil];
}
+ (nullable instancetype)fromJSONData:(NSData *)data
{
if (!data) return nil;
NSError *error = nil;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data
options:0
error:&error];
if (!dict || ![dict isKindOfClass:[NSDictionary class]]) return nil;
return [self fromDictionary:dict];
}
#pragma mark - Display
- (NSString *)displaySummary
{
switch (_type) {
case WRBookmarkTypeHighlight:
return [NSString stringWithFormat:@"[Highlight] %@", _markText ?: @""];
case WRBookmarkTypeUnderline:
return [NSString stringWithFormat:@"[Underline] %@", _markText ?: @""];
case WRBookmarkTypeMark:
return [NSString stringWithFormat:@"[Bookmark] Chapter %@", _chapterUid ?: @""];
case WRBookmarkTypeNote:
return [NSString stringWithFormat:@"[Note] %@", _noteContent ?: _markText ?: @""];
case WRBookmarkTypePencil:
return @"[Pencil Note]";
default:
return _markText ?: @"";
}
}
- (UIColor *)highlightUIColor
{
if (!_colorStyle) {
return sColorMap[@"yellow"] ?: [UIColor yellowColor];
}
return sColorMap[_colorStyle] ?: [UIColor yellowColor];
}
#pragma mark - NSObject
- (NSString *)description
{
return [NSString stringWithFormat:@"<WRBookmark: %@ type=%ld book=%@ ch=%@ '%@'>",
_bookmarkId, (long)_type, _bookId, _chapterUid,
[_markText substringToIndex:MIN(30, _markText.length)]];
}
- (BOOL)isEqual:(id)object
{
if (self == object) return YES;
if (![object isKindOfClass:[WRBookmark class]]) return NO;
WRBookmark *other = (WRBookmark *)object;
return [self.bookmarkId isEqualToString:other.bookmarkId];
}
- (NSUInteger)hash
{
return self.bookmarkId.hash;
}
@end