Add recalled message tag UI enhancement

This commit is contained in:
Sunnyyoung
2019-08-30 00:35:47 +08:00
parent ec6823a564
commit fdd4e8ddfe
7 changed files with 167 additions and 60 deletions
+23
View File
@@ -0,0 +1,23 @@
//
// RecallCacheManager.h
// WeChatTweak
//
// Created by Sunny Young on 2019/8/29.
// Copyright © 2019 Sunnyyoung. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <MMKV/MMKV.h>
NS_ASSUME_NONNULL_BEGIN
@interface RecallCacheManager : NSObject
+ (instancetype)sharedInstance;
+ (void)insertRevokedMessageID:(long long)messageID;
+ (BOOL)containsRevokedMessageID:(long long)messageID;
@end
NS_ASSUME_NONNULL_END
+44
View File
@@ -0,0 +1,44 @@
//
// RecallCacheManager.m
// WeChatTweak
//
// Created by Sunny Young on 2019/8/29.
// Copyright © 2019 Sunnyyoung. All rights reserved.
//
#import "RecallCacheManager.h"
@interface RecallCacheManager()
@property (nonatomic, strong) MMKV *kv;
@end
@implementation RecallCacheManager
- (instancetype)init {
if (self = [super init]) {
[MMKV setLogLevel:MMKVLogNone];
_kv = [MMKV mmkvWithID:@"Recall.cache"];
}
return self;
}
+ (instancetype)sharedInstance {
static RecallCacheManager *shared;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shared = [[RecallCacheManager alloc] init];
});
return shared;
}
+ (void)insertRevokedMessageID:(long long)messageID {
[RecallCacheManager.sharedInstance.kv setBool:YES forKey:@(messageID).stringValue];
}
+ (BOOL)containsRevokedMessageID:(long long)messageID {
return [RecallCacheManager.sharedInstance.kv containsKey:@(messageID).stringValue];
}
@end
@@ -64,6 +64,7 @@ typedef NS_ENUM(unsigned int, MessageDataType) {
- (instancetype)initWithMsgType:(long long)arg1;
- (BOOL)isSendFromSelf;
- (id)getChatNameForCurMsg;
@end
@@ -111,7 +112,8 @@ typedef NS_ENUM(unsigned int, MessageDataType) {
- (id)GetMsgData:(id)arg1 svrId:(unsigned long long)arg2;
- (void)DelMsg:(id)arg1 msgList:(id)arg2 isDelAll:(BOOL)arg3 isManual:(BOOL)arg4;
- (void)AddLocalMsg:(id)arg1 msgData:(id)arg2;
- (void)notifyAddMsgOnMainThread:(id)arg1 msgData:(id)arg2;
- (void)notifyDelMsgOnMainThread:(id)arg1 msgData:(id)arg2;
- (void)notifyAddRevokePromptMsgOnMainThread:(id)arg1 msgData:(id)arg2;
@end
+82 -58
View File
@@ -14,6 +14,7 @@
#import "TweakPreferencesController.h"
#import "AlfredManager.h"
#import "WTConfigManager.h"
#import "RecallCacheManager.h"
// Global Function
static NSString *(*original_NSHomeDirectory)(void);
@@ -63,8 +64,12 @@ static void __attribute__((constructor)) tweak(void) {
[objc_getClass("CUtility") jr_swizzleClassMethod:NSSelectorFromString(@"FFSvrChatInfoMsgWithImgZZ") withClassMethod:@selector(tweak_HasWechatInstance) error:nil];
[objc_getClass("NSRunningApplication") jr_swizzleClassMethod:NSSelectorFromString(@"runningApplicationsWithBundleIdentifier:") withClassMethod:@selector(tweak_runningApplicationsWithBundleIdentifier:) error:nil];
[objc_getClass("MASPreferencesWindowController") jr_swizzleMethod:NSSelectorFromString(@"initWithViewControllers:") withMethod:@selector(tweak_initWithViewControllers:) error:nil];
[objc_getClass("MMMessageCellView") jr_swizzleMethod:NSSelectorFromString(@"contextMenu") withMethod:@selector(tweak_contextMenu) error:nil];
[objc_getClass("MMMessageCellView") jr_swizzleMethod:NSSelectorFromString(@"initWithFrame:") withMethod:@selector(tweak_initWithFrame:) error:nil];
[objc_getClass("MMMessageCellView") jr_swizzleMethod:NSSelectorFromString(@"populateWithMessage:") withMethod:@selector(tweak_populateWithMessage:) error:nil];
[objc_getClass("MMMessageCellView") jr_swizzleMethod:NSSelectorFromString(@"layout") withMethod:@selector(tweak_layout) error:nil];
objc_property_attribute_t type = { "T", "@\"NSString\"" }; // NSString
objc_property_attribute_t atom = { "N", "" }; // nonatomic
objc_property_attribute_t ownership = { "&", "" }; // C = copy & = strong
@@ -76,6 +81,59 @@ static void __attribute__((constructor)) tweak(void) {
class_addMethod(objc_getClass("WCContactData"), @selector(modelPropertyWhitelist), method_getImplementation(class_getClassMethod(objc_getClass("WCContactData"), @selector(modelPropertyWhitelist))), "v@:");
}
- (instancetype)tweak_initWithFrame:(NSRect)arg1 {
MMMessageCellView *view = (MMMessageCellView *)[self tweak_initWithFrame:arg1];
NSTextField *revokeTextField = [[NSTextField alloc] init];
revokeTextField.hidden = YES;
revokeTextField.editable = NO;
revokeTextField.selectable = NO;
revokeTextField.bordered = NO;
revokeTextField.drawsBackground = NO;
revokeTextField.usesSingleLineMode = YES;
revokeTextField.tag = 9527;
revokeTextField.stringValue = @"[已撤回]";
revokeTextField.font = [NSFont systemFontOfSize:10];
revokeTextField.textColor = [NSColor lightGrayColor];
[view addSubview:revokeTextField];
return view;
}
- (void)tweak_populateWithMessage:(MMMessageTableItem *)tableItem {
[self tweak_populateWithMessage:tableItem];
BOOL style = [RecallCacheManager containsRevokedMessageID:tableItem.message.mesSvrID] && tableItem.message.messageType != MessageDataTypePrompt;
[((MMMessageCellView *)self).subviews enumerateObjectsUsingBlock:^(__kindof NSView * _Nonnull view, NSUInteger index, BOOL * _Nonnull stop) {
if (view.tag != 9527) {
return ;
}
*stop = YES;
view.hidden = !style;
}];
((MMMessageCellView *)self).layer.backgroundColor = style ? [NSColor.yellowColor colorWithAlphaComponent:0.3].CGColor : ((MMMessageCellView *)self).layer.backgroundColor;
}
- (void)tweak_layout {
[self tweak_layout];
__block NSTextField *label = nil;
[((MMMessageCellView *)self).subviews enumerateObjectsUsingBlock:^(__kindof NSView * _Nonnull view, NSUInteger index, BOOL * _Nonnull stop) {
if (view.tag != 9527) {
return ;
}
*stop = YES;
label = view;
}];
if (label == nil) {
return;
}
label.frame = ({
NSView *avatarView = ((MMMessageCellView *)self).avatarImgView;
CGFloat x = CGRectGetMidX(avatarView.frame) - CGRectGetWidth(label.frame) / 2.0;
CGFloat y = CGRectGetMinY(avatarView.frame) - CGRectGetHeight(label.frame);
NSRect fuck = [label.stringValue boundingRectWithSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX) options:kNilOptions attributes:nil];
NSRect frame = NSMakeRect(x, y, CGRectGetWidth(fuck), CGRectGetHeight(fuck));
frame;
});
}
#pragma mark - No Revoke Message
- (void)tweak_onRevokeMsg:(MessageData *)message {
@@ -83,54 +141,10 @@ static void __attribute__((constructor)) tweak(void) {
NSString *session = [message.msgContent tweak_subStringFrom:@"<session>" to:@"</session>"];
NSUInteger newMessageID = [message.msgContent tweak_subStringFrom:@"<newmsgid>" to:@"</newmsgid>"].longLongValue;
NSString *replaceMessage = [message.msgContent tweak_subStringFrom:@"<replacemsg><![CDATA[" to:@"]]></replacemsg>"];
// Prepare message data
MessageData *localMessageData = [((MessageService *)self) GetMsgData:session svrId:newMessageID];
MessageData *promptMessageData = ({
MessageData *data = [[objc_getClass("MessageData") alloc] initWithMsgType:10000];
data.msgStatus = 4;
data.toUsrName = localMessageData.toUsrName;
data.fromUsrName = localMessageData.fromUsrName;
data.mesSvrID = localMessageData.mesSvrID;
data.mesLocalID = localMessageData.mesLocalID;
data.msgCreateTime = localMessageData.msgCreateTime;
if ([localMessageData isSendFromSelf]) {
data.msgContent = replaceMessage;
} else {
NSString *fromUserName = [replaceMessage componentsSeparatedByString:@" "].firstObject;
NSString *userRevoke = [NSString stringWithFormat:@"%@ %@ ", fromUserName, [NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.Recalled"]];
NSString *tips = [NSString stringWithFormat:[NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.InterceptedARecalledMessage"], userRevoke];
NSMutableString *msgContent = [NSMutableString stringWithString:tips];
switch (localMessageData.messageType) {
case MessageDataTypeText: {
if (localMessageData.msgContent.length) {
if ([session rangeOfString:@"@chatroom"].location == NSNotFound) {
[msgContent appendFormat:@"\"%@\"", localMessageData.msgContent];
} else {
[msgContent appendFormat:@"\"%@\"", [localMessageData.msgContent componentsSeparatedByString:@":\n"].lastObject];
}
} else {
[msgContent appendString:[NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.AMessage"]];
}
break;
}
case MessageDataTypeImage:
[msgContent appendFormat:@"<%@>", [NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.Image"]]; break;
case MessageDataTypeVoice:
[msgContent appendFormat:@"<%@>", [NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.Voice"]]; break;
case MessageDataTypeVideo:
[msgContent appendFormat:@"<%@>", [NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.Video"]]; break;
case MessageDataTypeSticker:
[msgContent appendFormat:@"<%@>", [NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.Sticker"]]; break;
case MessageDataTypeAppUrl:
[msgContent appendFormat:@"<%@>", [NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.Link"]]; break;
default:
[msgContent appendString:[NSBundle.tweakBundle localizedStringForKey:@"Tweak.Message.AMessage"]]; break;
}
data.msgContent = msgContent;
}
data;
});
// Get message data
MessageData *messageData = [((MessageService *)self) GetMsgData:session svrId:newMessageID];
[RecallCacheManager insertRevokedMessageID:messageData.mesSvrID];
// Prepare notification information
MMServiceCenter *serviceCenter = [objc_getClass("MMServiceCenter") defaultCenter];
@@ -149,21 +163,31 @@ static void __attribute__((constructor)) tweak(void) {
userNotification.informativeText = [NSString stringWithFormat:@"%@: %@", groupName, replaceMessage];
}
// Delete message if it is revoke from myself
if ([localMessageData isSendFromSelf]) {
[((MessageService *)self) DelMsg:session msgList:@[localMessageData] isDelAll:NO isManual:YES];
if ([messageData isSendFromSelf]) {
MessageData *promptMessageData = ({
MessageData *data = [[objc_getClass("MessageData") alloc] initWithMsgType:MessageDataTypePrompt];
data.msgStatus = 4;
data.toUsrName = messageData.toUsrName;
data.fromUsrName = messageData.fromUsrName;
data.mesSvrID = messageData.mesSvrID;
data.mesLocalID = messageData.mesLocalID;
data.msgCreateTime = messageData.msgCreateTime;
data.msgContent = replaceMessage;
data;
});
// Delete message if it is revoke from myself
[((MessageService *)self) DelMsg:session msgList:@[messageData] isDelAll:NO isManual:YES];
[((MessageService *)self) AddLocalMsg:session msgData:promptMessageData];
} else {
if (localMessageData.messageType == MessageDataTypeText) {
[((MessageService *)self) DelMsg:session msgList:@[localMessageData] isDelAll:NO isManual:YES];
}
[((MessageService *)self) AddLocalMsg:session msgData:promptMessageData];
// Invoke message reloading
[((MessageService *)self) notifyDelMsgOnMainThread:messageData.getChatNameForCurMsg msgData:messageData];
[((MessageService *)self) notifyAddRevokePromptMsgOnMainThread:messageData.getChatNameForCurMsg msgData:messageData];
}
// Dispatch notification
dispatch_async(dispatch_get_main_queue(), ^{
// Deliver notification
if (![localMessageData isSendFromSelf]) {
if (![messageData isSendFromSelf]) {
RevokeNotificationType notificationType = [[NSUserDefaults standardUserDefaults] integerForKey:WeChatTweakPreferenceRevokeNotificationTypeKey];
if (notificationType == RevokeNotificationTypeReceiveAll || (notificationType == RevokeNotificationTypeFollow && isChatStatusNotifyOpen)) {
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:userNotification];