diff --git a/.github/workflows/beta.build-push.yml b/.github/workflows/beta.build-push.yml index 2ef6806c0e..5a9a93f77e 100644 --- a/.github/workflows/beta.build-push.yml +++ b/.github/workflows/beta.build-push.yml @@ -51,11 +51,6 @@ jobs: run: ./scripts/build.sh - name: validate ios app run: xcrun altool --validate-app --file ./Monal/build/ipa/Monal.ipa --type ios --asc-provider S8D843U34Y -u "$(cat /Users/ci/apple_connect_upload_mail.txt)" -p "$(cat /Users/ci/apple_connect_upload_secret.txt)" - - name: Update translations - run: | - chmod +x ./scripts/updateLocalization.sh - chmod +x ./scripts/xliff_extractor.py - ./scripts/updateLocalization.sh BUILDSERVER - name: push tag to beta repo run: | buildNumber=$(git tag --sort="v:refname" |grep "Build_iOS" | tail -n1 | sed 's/Build_iOS_//g') @@ -75,6 +70,11 @@ jobs: run: ./scripts/uploadNonAlpha.sh beta - name: Publish catalyst to appstore connect run: xcrun altool --upload-app --file ./Monal/build/app/Monal.pkg --type macos --asc-provider S8D843U34Y -u "$(cat /Users/ci/apple_connect_upload_mail.txt)" -p "$(cat /Users/ci/apple_connect_upload_secret.txt)" --primary-bundle-id org.monal-im.prod.catalyst.monal + - name: Update translations + run: | + chmod +x ./scripts/updateLocalization.sh + chmod +x ./scripts/xliff_extractor.py + ./scripts/updateLocalization.sh BUILDSERVER - uses: actions/upload-artifact@v3 with: name: monal-catalyst diff --git a/.github/workflows/develop-push.yml b/.github/workflows/develop-push.yml index 16492bc84a..b88e0a5692 100644 --- a/.github/workflows/develop-push.yml +++ b/.github/workflows/develop-push.yml @@ -37,6 +37,10 @@ jobs: if [[ -e "/Users/ci/secrets.monal_alpha" ]]; then echo "#import \"/Users/ci/secrets.monal_alpha\"" > Monal/Classes/secrets.h fi + - name: Write git hash include + run: | + echo "Current commit hash: $(git show-ref --hash --abbrev refs/heads/develop)" + echo "#define ALPHA_COMMIT_HASH \"$(git show-ref --hash --abbrev refs/heads/develop)\"" > Monal/Classes/commitHash.h - name: Make our build scripts executable run: chmod +x ./scripts/build.sh - name: Run build diff --git a/Monal/Classes/AddContactMenu.swift b/Monal/Classes/AddContactMenu.swift index 73dc76921f..4f6d13ad29 100644 --- a/Monal/Classes/AddContactMenu.swift +++ b/Monal/Classes/AddContactMenu.swift @@ -181,7 +181,7 @@ struct AddContactMenu: View { } .pickerStyle(.menu) } - TextField("Contact or Group/Channel Jid", text: $toAdd) + TextField(NSLocalizedString("Contact or Group/Channel Jid", comment: "placeholder when adding jid"), text: $toAdd) //ios15: .textInputAutocapitalization(.never) .autocapitalization(.none) .autocorrectionDisabled() diff --git a/Monal/Classes/ContactDetails.swift b/Monal/Classes/ContactDetails.swift index a35ade2ad7..a8aea957b3 100644 --- a/Monal/Classes/ContactDetails.swift +++ b/Monal/Classes/ContactDetails.swift @@ -112,7 +112,7 @@ struct ContactDetails: View { #endif if(!contact.isGroup && !contact.isSelfChat) { - TextField("Rename Contact", text: $contact.nickNameView) + TextField(NSLocalizedString("Rename Contact", comment: "placeholder text in contact details"), text: $contact.nickNameView) .textFieldStyle(RoundedBorderTextFieldStyle()) .addClearButton(text:$contact.nickNameView) } diff --git a/Monal/Classes/ContactPicker.swift b/Monal/Classes/ContactPicker.swift index 769c7bba81..f7877f8cf6 100644 --- a/Monal/Classes/ContactPicker.swift +++ b/Monal/Classes/ContactPicker.swift @@ -77,7 +77,7 @@ struct ContactPicker: View { } else { List { Section { - TextField("Search contacts", text: $searchFieldInput) + TextField(NSLocalizedString("Search contacts", comment: "placeholder in contact picker"), text: $searchFieldInput) } ForEach(Array(contacts.enumerated()), id: \.element) { idx, contact in if matchesSearch(contact: contact) { diff --git a/Monal/Classes/ContactsViewController.m b/Monal/Classes/ContactsViewController.m index 3c46c7430e..c582966a14 100644 --- a/Monal/Classes/ContactsViewController.m +++ b/Monal/Classes/ContactsViewController.m @@ -68,7 +68,7 @@ -(void) configureContactRequestsImage hasNotification:[[DataLayer sharedInstance] allContactRequests].count > 0 withTapHandler:requestsTapRecoginzer]; [self.navigationItem.rightBarButtonItems[1] setIsAccessibilityElement:YES]; - [self.navigationItem.rightBarButtonItems[1] setAccessibilityLabel:@"Open contacts list"]; + [self.navigationItem.rightBarButtonItems[1] setAccessibilityLabel:NSLocalizedString(@"Open list of pending contact requests", @"")]; } diff --git a/Monal/Classes/CreateGroupMenu.swift b/Monal/Classes/CreateGroupMenu.swift index 587d505092..881d61c1ad 100644 --- a/Monal/Classes/CreateGroupMenu.swift +++ b/Monal/Classes/CreateGroupMenu.swift @@ -78,7 +78,7 @@ struct CreateGroupMenu: View { } .pickerStyle(.menu) } - TextField("Group Name (optional)", text: $groupName) + TextField(NSLocalizedString("Group Name (optional)", comment: "placeholder when creating new group"), text: $groupName) .autocorrectionDisabled() .autocapitalization(.none) .addClearButton(text:$groupName) diff --git a/Monal/Classes/DataLayer.m b/Monal/Classes/DataLayer.m index c7910f15dc..bb162046e7 100644 --- a/Monal/Classes/DataLayer.m +++ b/Monal/Classes/DataLayer.m @@ -255,8 +255,7 @@ -(BOOL) updateAccounWithDictionary:(NSDictionary*) dictionary [dictionary objectForKey:kAccountID], ]; BOOL retval = [self.db executeNonQuery:query andArguments:params]; - //add self-chat - [self addContact:[NSString stringWithFormat:@"%@@%@", dictionary[kUsername], dictionary[kDomain]] forAccount:dictionary[kAccountID] nickname:nil]; + [self addSelfChatForAccount:dictionary[kAccountID]]; return retval; }]; } @@ -283,8 +282,7 @@ -(NSNumber*) addAccountWithDictionary:(NSDictionary*) dictionary if(result == YES) { NSNumber* accountID = [self.db lastInsertId]; DDLogInfo(@"Added account %@ to account table with accountNo %@", [dictionary objectForKey:kUsername], accountID); - //add self-chat - [self addContact:[NSString stringWithFormat:@"%@@%@", dictionary[kUsername], dictionary[kDomain]] forAccount:accountID nickname:nil]; + [self addSelfChatForAccount:accountID]; return accountID; } else { return (NSNumber*)nil; @@ -374,6 +372,16 @@ -(void) persistState:(NSDictionary*) state forAccount:(NSNumber*) accountNo #pragma mark contact Commands +-(BOOL) addSelfChatForAccount:(NSNumber*) accountNo +{ + BOOL encrypt = NO; +#ifndef DISABLE_OMEMO + encrypt = [[HelperTools defaultsDB] boolForKey:@"OMEMODefaultOn"]; +#endif// DISABLE_OMEMO + NSDictionary* accountDetails = [self detailsForAccount:accountNo]; + return [self.db executeNonQuery:@"INSERT INTO buddylist ('account_id', 'buddy_name', 'full_name', 'nick_name', 'muc', 'muc_nick', 'encrypt') VALUES(?, ?, ?, ?, ?, ?, ?) ON CONFLICT(account_id, buddy_name) DO UPDATE SET subscription='both';" andArguments:@[accountNo, [NSString stringWithFormat:@"%@@%@", accountDetails[kUsername], accountDetails[kDomain]], @"", @"", @0, @"", @(encrypt)]]; +} + -(BOOL) addContact:(NSString*) contact forAccount:(NSNumber*) accountNo nickname:(NSString*) nickName { if(accountNo == nil || !contact) diff --git a/Monal/Classes/HelperTools.h b/Monal/Classes/HelperTools.h index 6db98f842c..277c5d126e 100644 --- a/Monal/Classes/HelperTools.h +++ b/Monal/Classes/HelperTools.h @@ -32,6 +32,11 @@ NS_ASSUME_NONNULL_BEGIN @class UIView; @class UITapGestureRecognizer; +typedef NS_ENUM(NSUInteger, MLVersionType) { + MLVersionTypeIQ, + MLVersionTypeLog, +}; + void logException(NSException* exception); void swizzle(Class c, SEL orig, SEL new); @@ -49,7 +54,7 @@ void swizzle(Class c, SEL orig, SEL new); +(void) postError:(NSString*) description withNode:(XMPPStanza* _Nullable) node andAccount:(xmpp*) account andIsSevere:(BOOL) isSevere andDisableAccount:(BOOL) disableAccount; +(void) postError:(NSString*) description withNode:(XMPPStanza* _Nullable) node andAccount:(xmpp*) account andIsSevere:(BOOL) isSevere; +(NSString*) extractXMPPError:(XMPPStanza*) stanza withDescription:(NSString* _Nullable) description; -+(void) showErrorOnAlpha:(NSString*) description withNode:(XMPPStanza* _Nullable) node andAccount:(xmpp*) account andFile:(char*) file andLine:(int) line andFunc:(char*) func; ++(void) showErrorOnAlpha:(NSString*) description withNode:(XMPPStanza* _Nullable) node andAccount:(xmpp* _Nullable) account andFile:(char*) file andLine:(int) line andFunc:(char*) func; +(NSDictionary*) getInvalidPushServers; +(NSString*) getSelectedPushServerBasedOnLocale; @@ -139,7 +144,7 @@ void swizzle(Class c, SEL orig, SEL new); //don't use these four directly, but via createTimer() makro +(monal_void_block_t) startQueuedTimer:(double) timeout withHandler:(monal_void_block_t) handler andCancelHandler:(monal_void_block_t _Nullable) cancelHandler andFile:(char*) file andLine:(int) line andFunc:(char*) func onQueue:(dispatch_queue_t _Nullable) queue; -+(NSString*) appBuildVersionInfo; ++(NSString*) appBuildVersionInfoFor:(MLVersionType) type; +(BOOL) deviceUsesSplitView; diff --git a/Monal/Classes/HelperTools.m b/Monal/Classes/HelperTools.m index bf398a7f58..787b9eca16 100644 --- a/Monal/Classes/HelperTools.m +++ b/Monal/Classes/HelperTools.m @@ -49,6 +49,7 @@ #import "OmemoState.h" #import "MLUDPLogger.h" #import "MLStreamRedirect.h" +#import "commithash.h" @import UserNotifications; @import CoreImage; @@ -71,6 +72,7 @@ @interface KSCrash() static char _origLogfilePath[1024] = ""; static char _logfilePath[1024] = ""; static NSObject* _isAppExtensionLock = nil; +static NSMutableDictionary* _versionInfoCache; static MLStreamRedirect* _stdoutRedirector = nil; static MLStreamRedirect* _stderrRedirector = nil; static volatile void (*_oldExceptionHandler)(NSException*) = NULL; @@ -172,8 +174,10 @@ static void addFilePathWithSize(const KSCrashReportWriter* writer, char* name, c static void crash_callback(const KSCrashReportWriter* writer) { int copyRetval = asyncSafeCopyFile(_origLogfilePath, _logfilePath); + int errnoCopy = errno; writer->addStringElement(writer, "logfileCopied", "YES"); writer->addIntegerElement(writer, "logfileCopyResult", copyRetval); + writer->addIntegerElement(writer, "logfileCopyErrno", errnoCopy); addFilePathWithSize(writer, "logfileCopy", _logfilePath); //this comes last to make sure we see size differences if the logfile got written during crash data collection (could be other processes) addFilePathWithSize(writer, "currentLogfile", _origLogfilePath); @@ -229,6 +233,7 @@ @implementation HelperTools +(void) initialize { _isAppExtensionLock = [NSObject new]; + _versionInfoCache = [NSMutableDictionary new]; u_int32_t i = arc4random(); _processID = [self hexadecimalString:[NSData dataWithBytes:&i length:sizeof(i)]]; @@ -280,7 +285,6 @@ +(void) __attribute__((noreturn)) handleRustPanicWithText:(NSString*) text andBa _crash_info.message = abort_msg.UTF8String; _crash_info.signature = abort_msg.UTF8String; //use signature for apple crash reporter which does not handle message field _crash_info.backtrace = backtrace.UTF8String; - _crash_info.message2 = backtrace.UTF8String; //use message2 for kscrash which does not handle backtrace field //log error and flush all logs [DDLog flushLog]; @@ -314,7 +318,7 @@ +(void) postError:(NSString*) description withNode:(XMPPStanza* _Nullable) node [[MLNotificationQueue currentQueue] postNotificationName:kXMPPError object:account userInfo:@{@"message": message, @"isSevere":@(isSevere)}]; } -+(void) showErrorOnAlpha:(NSString*) description withNode:(XMPPStanza* _Nullable) node andAccount:(xmpp*) account andFile:(char*) file andLine:(int) line andFunc:(char*) func ++(void) showErrorOnAlpha:(NSString*) description withNode:(XMPPStanza* _Nullable) node andAccount:(xmpp* _Nullable) account andFile:(char*) file andLine:(int) line andFunc:(char*) func { NSString* fileStr = [NSString stringWithFormat:@"%s", file]; NSArray* filePathComponents = [fileStr pathComponents]; @@ -322,10 +326,22 @@ +(void) showErrorOnAlpha:(NSString*) description withNode:(XMPPStanza* _Nullable fileStr = [NSString stringWithFormat:@"%@/%@", filePathComponents[[filePathComponents count]-2], filePathComponents[[filePathComponents count]-1]]; NSString* message = description; if(node) - message = [HelperTools extractXMPPError:node withDescription:description]; + message = [self extractXMPPError:node withDescription:description]; #ifdef IS_ALPHA DDLogError(@"Notifying alpha user about error at %@:%d in %s: %@", fileStr, line, func, message); - [[MLNotificationQueue currentQueue] postNotificationName:kXMPPError object:account userInfo:@{@"message": message, @"isSevere":@YES}]; + if(account != nil) + [[MLNotificationQueue currentQueue] postNotificationName:kXMPPError object:account userInfo:@{@"message": message, @"isSevere":@YES}]; + else + { + UNMutableNotificationContent* content = [UNMutableNotificationContent new]; + content.title = @"Global Error"; + content.body = message; + content.sound = [UNNotificationSound defaultSound]; + UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:[[NSUUID UUID] UUIDString] content:content trigger:nil]; + NSError* error = [self postUserNotificationRequest:request]; + if(error) + DDLogError(@"Error posting global alpha xmppError notification: %@", error); + } #else DDLogWarn(@"Ignoring alpha-only error at %@:%d in %s: %@", fileStr, line, func, message); #endif @@ -1643,11 +1659,7 @@ +(void) configureLogging DDLogDebug(@"File attributes: %@", attrs); //log version info as early as possible - NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary]; - NSString* version = [infoDict objectForKey:@"CFBundleShortVersionString"]; - NSString* buildDate = [NSString stringWithUTF8String:__DATE__]; - NSString* buildTime = [NSString stringWithUTF8String:__TIME__]; - DDLogInfo(@"Starting: Version %@ (%@ %@ UTC, %@) on iOS/macOS %@", version, buildDate, buildTime, [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"], [UIDevice currentDevice].systemVersion); + DDLogInfo(@"Starting: %@", [self appBuildVersionInfoFor:MLVersionTypeLog]); //[SCRAM SSDPXepOutput]; [DDLog flushLog]; @@ -1730,15 +1742,11 @@ +(void) installCrashHandler handler.demangleLanguages = KSCrashDemangleLanguageAll; handler.maxReportCount = 4; handler.deadlockWatchdogInterval = 0; // no main thread watchdog - NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary]; - NSString* version = [infoDict objectForKey:@"CFBundleShortVersionString"]; - NSString* buildDate = [NSString stringWithUTF8String:__DATE__]; - NSString* buildTime = [NSString stringWithUTF8String:__TIME__]; handler.userInfo = @{ @"isAppex": @([self isAppExtension]), @"processName": [[[NSBundle mainBundle] executablePath] lastPathComponent], @"bundleName": nilWrapper([[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]), - @"appVersion": [NSString stringWithFormat:NSLocalizedString(@"Version %@ (%@ %@ UTC)", @""), version, buildDate, buildTime], + @"appVersion": [self appBuildVersionInfoFor:MLVersionTypeLog], }; //we can not use [KSCrash install] because this uses the bundle names to store our crash reports which are different //in appex and mainapp use the lowlevel C api with dummy bundle name "UnifiedReport" instead @@ -2068,15 +2076,24 @@ +(NSString*) encodeRandomResource return resource; } -+(NSString*) appBuildVersionInfo ++(NSString*) appBuildVersionInfoFor:(MLVersionType) type { - NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary]; + @synchronized(_versionInfoCache) { + if(_versionInfoCache[@(type)] != nil) + return _versionInfoCache[@(type)]; + #ifdef IS_ALPHA - NSString* versionTxt = [NSString stringWithFormat:@"Alpha %@ (%s: %s UTC)", [infoDict objectForKey:@"CFBundleShortVersionString"], __DATE__, __TIME__]; + NSString* rawVersionString = [NSString stringWithFormat:@"Alpha %s (%s %s UTC)", ALPHA_COMMIT_HASH, __DATE__, __TIME__]; #else - NSString* versionTxt = [NSString stringWithFormat:@"%@ (%@)", [infoDict objectForKey:@"CFBundleShortVersionString"], [infoDict objectForKey:@"CFBundleVersion"]]; + NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary]; + NSString* rawVersionString = [NSString stringWithFormat:@"%@ (%@)", [infoDict objectForKey:@"CFBundleShortVersionString"], [infoDict objectForKey:@"CFBundleVersion"]]; #endif - return versionTxt; + if(type == MLVersionTypeIQ) + return _versionInfoCache[@(type)] = rawVersionString; + else if(type == MLVersionTypeLog) + return _versionInfoCache[@(type)] = [NSString stringWithFormat:@"Version %@, %@ on iOS/macOS %@", rawVersionString, [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"], [UIDevice currentDevice].systemVersion]; + unreachable(@"unknown version type!"); + } } +(NSNumber*) currentTimestampInSeconds diff --git a/Monal/Classes/MLContact.h b/Monal/Classes/MLContact.h index 6217902cc5..ee4bce8cc3 100644 --- a/Monal/Classes/MLContact.h +++ b/Monal/Classes/MLContact.h @@ -37,6 +37,7 @@ FOUNDATION_EXPORT NSString* const kAskSubscribe; @property (nonatomic, readonly) BOOL isSubscribedFrom; @property (nonatomic, readonly) BOOL isSubscribedBoth; @property (nonatomic, readonly) BOOL hasIncomingContactRequest; +@property (nonatomic, readonly) BOOL hasOutgoingContactRequest; -(BOOL) isEqualToContact:(MLContact*) contact; -(BOOL) isEqualToMessage:(MLMessage*) message; diff --git a/Monal/Classes/MLContact.m b/Monal/Classes/MLContact.m index 3be0fb15ca..afc77b9a22 100644 --- a/Monal/Classes/MLContact.m +++ b/Monal/Classes/MLContact.m @@ -442,6 +442,11 @@ -(BOOL) hasIncomingContactRequest return self.isGroup == NO && [[DataLayer sharedInstance] hasContactRequestForContact:self]; } +-(BOOL) hasOutgoingContactRequest +{ + return self.isGroup == NO && [self.ask isEqualToString:kAskSubscribe]; +} + // this will cache the unread count on first access -(NSInteger) unreadCount { @@ -676,7 +681,7 @@ -(NSString*) id -(NSString*) description { - return [NSString stringWithFormat:@"%@: %@ (%@) %@%@", self.accountId, self.contactJid, self.isGroup ? self.mucType : @"1:1", self.isInRoster ? @"inRoster" : @"not(inRoster)", self.hasIncomingContactRequest ? @"[incomingContactRequest]" : @""]; + return [NSString stringWithFormat:@"%@: %@ (%@) %@%@%@, kSub=%@", self.accountId, self.contactJid, self.isGroup ? self.mucType : @"1:1", self.isInRoster ? @"inRoster" : @"not(inRoster)", self.hasIncomingContactRequest ? @"[incomingContactRequest]" : @"", self.hasOutgoingContactRequest ? @"[outgoingContactRequest]" : @"", self.subscription]; } +(MLContact*) contactFromDictionary:(NSDictionary*) dic diff --git a/Monal/Classes/MLHandler.h b/Monal/Classes/MLHandler.h index d4f58226f4..1cf4b8c911 100644 --- a/Monal/Classes/MLHandler.h +++ b/Monal/Classes/MLHandler.h @@ -119,16 +119,15 @@ MLHandler* h = $newHandlerWithInvalidation(ClassName, myHandlerName, myInvalidat //declare handler, the order of provided arguments does not matter because we use named arguments #define $$class_handler(name, ...) +(void) MLHandler_##name##_withArguments:(NSDictionary*) _callerArgs andBoundArguments:(NSDictionary*) _boundArgs { metamacro_if_eq(0, metamacro_argcount(__VA_ARGS__))( )( metamacro_foreach(_expand_import, ;, __VA_ARGS__) ); #define $$instance_handler(name, instance, ...) +(void) MLHandler_##name##_withArguments:(NSDictionary*) _callerArgs andBoundArguments:(NSDictionary*) _boundArgs { metamacro_if_eq(0, metamacro_argcount(__VA_ARGS__))( )( metamacro_foreach(_expand_import, ;, __VA_ARGS__) ); [instance MLInstanceHandler_##name##_withArguments:_callerArgs andBoundArguments:_boundArgs]; } -(void) MLInstanceHandler_##name##_withArguments:(NSDictionary*) _callerArgs andBoundArguments:(NSDictionary*) _boundArgs { metamacro_if_eq(0, metamacro_argcount(__VA_ARGS__))( )( metamacro_foreach(_expand_import, ;, __VA_ARGS__) ); -#define $_ID(type, var) (STRIP_PARENTHESES(type) var __unused = _callerArgs[@#var] ? _callerArgs[@#var] : _boundArgs[@#var]) -//#define $$ID(type, var) (STRIP_PARENTHESES(type) var __unused = _callerArgs[@#var] ? _callerArgs[@#var] : _boundArgs[@#var]) -#define $$ID(type, var) (if(_callerArgs[@#var]==nil && _boundArgs[@#var]==nil) [MLHandler throwDynamicExceptionForType:@#type andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; STRIP_PARENTHESES(type) var __unused = _callerArgs[@#var] ? _callerArgs[@#var] : _boundArgs[@#var]) -#define $_HANDLER(var) (MLHandler* var __unused = _callerArgs[@#var] ? _callerArgs[@#var] : _boundArgs[@#var]) -#define $$HANDLER(var) (if(_callerArgs[@#var]==nil && _boundArgs[@#var]==nil) [MLHandler throwDynamicExceptionForType:@"MLHandler" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; MLHandler* var __unused = _callerArgs[@#var] ? _callerArgs[@#var] : _boundArgs[@#var]) -#define $$BOOL(var) (if(_callerArgs[@#var]==nil && _boundArgs[@#var]==nil) [MLHandler throwDynamicExceptionForType:@"BOOL" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; BOOL var __unused = _callerArgs[@#var] ? [_callerArgs[@#var] boolValue] : [_boundArgs[@#var] boolValue]) -#define $$INT(var) (if(_callerArgs[@#var]==nil && _boundArgs[@#var]==nil) [MLHandler throwDynamicExceptionForType:@"int" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; int var __unused = _callerArgs[@#var] ? [_callerArgs[@#var] intValue] : [_boundArgs[@#var] intValue]) -#define $$DOUBLE(var) (if(_callerArgs[@#var]==nil && _boundArgs[@#var]==nil) [MLHandler throwDynamicExceptionForType:@"double" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; double var __unused = _callerArgs[@#var] ? [_callerArgs[@#var] doubleValue] : [_boundArgs[@#var] doubleValue]) -#define $$INTEGER(var) (if(_callerArgs[@#var]==nil && _boundArgs[@#var]==nil) [MLHandler throwDynamicExceptionForType:@"NSInteger" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; NSInteger var __unused = _callerArgs[@#var] ? [_callerArgs[@#var] integerValue] : [_boundArgs[@#var] integerValue]) -#define $$UINTEGER(var) (if(_callerArgs[@#var]==nil && _boundArgs[@#var]==nil) [MLHandler throwDynamicExceptionForType:@"NSUInteger" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; NSInteger var __unused = _callerArgs[@#var] ? [_callerArgs[@#var] integerValue] : [_boundArgs[@#var] unsignedIntegerValue]) +#define $_ID(type, var) (STRIP_PARENTHESES(type) var __unused = _callerArgs[@#var] ? _callerArgs[@#var] : _boundArgs[@#var]; if(var != nil && NSClassFromString(type_to_classname(@metamacro_foreach(_foreach_stringify, ",", STRIP_PARENTHESES(type)))) != nil && ![var isKindOfClass:NSClassFromString(type_to_classname(@metamacro_foreach(_foreach_stringify, ",", STRIP_PARENTHESES(type))))]) [MLHandler throwDynamicExceptionForType:@"!"#type andVar:@#var andUserData:(@{@"actualType": NSStringFromClass([var class]), @"expectedType": @#type, @"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__];) +#define $$ID(type, var) (STRIP_PARENTHESES(type) var __unused = _callerArgs[@#var] ? _callerArgs[@#var] : _boundArgs[@#var]; if(var == nil) [MLHandler throwDynamicExceptionForType:@"$$"#type andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; else if(NSClassFromString(type_to_classname(@metamacro_foreach(_foreach_stringify, ",", STRIP_PARENTHESES(type)))) != nil && ![var isKindOfClass:NSClassFromString(type_to_classname(@metamacro_foreach(_foreach_stringify, ",", STRIP_PARENTHESES(type))))]) [MLHandler throwDynamicExceptionForType:@"!"#type andVar:@#var andUserData:(@{@"actualType": NSStringFromClass([var class]), @"expectedType": @#type, @"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__];) +#define $_HANDLER(var) (MLHandler* var __unused = _callerArgs[@#var] ? _callerArgs[@#var] : _boundArgs[@#var]; if(var != nil &&![var isKindOfClass:[MLHandler class]]) [MLHandler throwDynamicExceptionForType:@"!MLHandler" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]) +#define $$HANDLER(var) (MLHandler* var __unused = _callerArgs[@#var] ? _callerArgs[@#var] : _boundArgs[@#var]; if(var == nil) [MLHandler throwDynamicExceptionForType:@"$$MLHandler" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; if(![var isKindOfClass:[MLHandler class]]) [MLHandler throwDynamicExceptionForType:@"!MLHandler" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]) +#define $$BOOL(var) (if(_callerArgs[@#var]==nil && _boundArgs[@#var]==nil) [MLHandler throwDynamicExceptionForType:@"$$BOOL" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; if(![(_callerArgs[@#var] ? _callerArgs[@#var] : _boundArgs[@#var]) isKindOfClass:[NSNumber class]]) [MLHandler throwDynamicExceptionForType:@"!NSNumber(BOOL)" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; BOOL var __unused = _callerArgs[@#var] ? [_callerArgs[@#var] boolValue] : [_boundArgs[@#var] boolValue]) +#define $$INT(var) (if(_callerArgs[@#var]==nil && _boundArgs[@#var]==nil) [MLHandler throwDynamicExceptionForType:@"$$int" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; if(![(_callerArgs[@#var] ? _callerArgs[@#var] : _boundArgs[@#var]) isKindOfClass:[NSNumber class]]) [MLHandler throwDynamicExceptionForType:@"!NSNumber(int)" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; int var __unused = _callerArgs[@#var] ? [_callerArgs[@#var] intValue] : [_boundArgs[@#var] intValue]) +#define $$DOUBLE(var) (if(_callerArgs[@#var]==nil && _boundArgs[@#var]==nil) [MLHandler throwDynamicExceptionForType:@"$$double" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; if(![(_callerArgs[@#var] ? _callerArgs[@#var] : _boundArgs[@#var]) isKindOfClass:[NSNumber class]]) [MLHandler throwDynamicExceptionForType:@"!NSNumber(double)" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; double var __unused = _callerArgs[@#var] ? [_callerArgs[@#var] doubleValue] : [_boundArgs[@#var] doubleValue]) +#define $$INTEGER(var) (if(_callerArgs[@#var]==nil && _boundArgs[@#var]==nil) [MLHandler throwDynamicExceptionForType:@"$$NSInteger" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; if(![(_callerArgs[@#var] ? _callerArgs[@#var] : _boundArgs[@#var]) isKindOfClass:[NSNumber class]]) [MLHandler throwDynamicExceptionForType:@"!NSNumber(NSInteger)" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; NSInteger var __unused = _callerArgs[@#var] ? [_callerArgs[@#var] integerValue] : [_boundArgs[@#var] integerValue]) +#define $$UINTEGER(var) (if(_callerArgs[@#var]==nil && _boundArgs[@#var]==nil) [MLHandler throwDynamicExceptionForType:@"$$NSUInteger" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; if(![(_callerArgs[@#var] ? _callerArgs[@#var] : _boundArgs[@#var]) isKindOfClass:[NSNumber class]]) [MLHandler throwDynamicExceptionForType:@"!NSNumber(NSUInteger)" andVar:@#var andUserData:(@{@"_boundArgs": _boundArgs, @"_callerArgs": _callerArgs}) andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__]; NSUInteger var __unused = _callerArgs[@#var] ? [_callerArgs[@#var] unsignedIntegerValue] : [_boundArgs[@#var] unsignedIntegerValue]) #define $$ } //call handler/invalidation @@ -147,9 +146,13 @@ MLHandler* h = $newHandlerWithInvalidation(ClassName, myHandlerName, myInvalidat #define _packDOUBLE(name, value, ...) @#name : [NSNumber numberWithDouble: value ] #define _packINTEGER(name, value, ...) @#name : [NSNumber numberWithInteger: value ] #define _packUINTEGER(name, value, ...) @#name : [NSNumber numberWithUnsignedInteger: value ] +#define _foreach_stringify(_, var) metamacro_stringify(var) + NS_ASSUME_NONNULL_BEGIN +NSString* type_to_classname(NSString* type); + @interface MLHandler : NSObject { } diff --git a/Monal/Classes/MLHandler.m b/Monal/Classes/MLHandler.m index 4c0729eec5..2a55fc6206 100644 --- a/Monal/Classes/MLHandler.m +++ b/Monal/Classes/MLHandler.m @@ -20,6 +20,11 @@ @interface MLHandler () } @end +NSString* type_to_classname(NSString* type) +{ + return [type componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"*< "]][0]; +} + @implementation MLHandler -(instancetype) init diff --git a/Monal/Classes/MLPubSub.m b/Monal/Classes/MLPubSub.m index 50794d2660..5d8a0874f6 100644 --- a/Monal/Classes/MLPubSub.m +++ b/Monal/Classes/MLPubSub.m @@ -98,7 +98,7 @@ -(void) fetchNode:(NSString*) node from:(NSString*) jid withItemsList:(NSArray* if(!account.connectionProperties.accountDiscoDone) { DDLogWarn(@"Queueing pubsub call until account disco is resolved..."); - [_queue addObject:$newHandlerWithInvalidation(self, queuedFetchNodeHandler, handleFetchInvalidation, $ID(node), $ID(jid), $ID(itemsList), $ID(handler))]; + [_queue addObject:$newHandlerWithInvalidation(self, queuedFetchNodeHandler, handleFetchInvalidation, $ID(node), $ID(jid), $ID(itemsList), $HANDLER(handler))]; return; } @@ -146,7 +146,7 @@ -(void) subscribeToNode:(NSString*) node onJid:(NSString*) jid withHandler:(MLHa if(!account.connectionProperties.accountDiscoDone) { DDLogWarn(@"Queueing pubsub call until account disco is resolved..."); - [_queue addObject:$newHandlerWithInvalidation(self, queuedSubscribeToNodeHandler, handleSubscribeInvalidation, $ID(node), $ID(jid), $ID(handler))]; + [_queue addObject:$newHandlerWithInvalidation(self, queuedSubscribeToNodeHandler, handleSubscribeInvalidation, $ID(node), $ID(jid), $HANDLER(handler))]; return; } @@ -187,7 +187,7 @@ -(void) unsubscribeFromNode:(NSString*) node forJid:(NSString*) jid withHandler: if(!_account.connectionProperties.accountDiscoDone) { DDLogWarn(@"Queueing pubsub call until account disco is resolved..."); - [_queue addObject:$newHandlerWithInvalidation(self, queuedUnsubscribeFromNodeHandler, handleUnsubscribeInvalidation, $ID(node), $ID(jid), $ID(handler))]; + [_queue addObject:$newHandlerWithInvalidation(self, queuedUnsubscribeFromNodeHandler, handleUnsubscribeInvalidation, $ID(node), $ID(jid), $HANDLER(handler))]; return; } @@ -229,7 +229,7 @@ -(void) configureNode:(NSString*) node withConfigOptions:(NSDictionary*) configO if(!account.connectionProperties.accountDiscoDone) { DDLogWarn(@"Queueing pubsub call until account disco is resolved..."); - [_queue addObject:$newHandlerWithInvalidation(self, queuedConfigureNodeHandler, handleConfigFormResultInvalidation, $ID(node), $ID(configOptions), $ID(handler))]; + [_queue addObject:$newHandlerWithInvalidation(self, queuedConfigureNodeHandler, handleConfigFormResultInvalidation, $ID(node), $ID(configOptions), $HANDLER(handler))]; return; } @@ -273,7 +273,7 @@ -(void) publishItem:(MLXMLNode*) item onNode:(NSString*) node withConfigOptions: if(!_account.connectionProperties.accountDiscoDone) { DDLogWarn(@"Queueing pubsub call until account disco is resolved..."); - [_queue addObject:$newHandlerWithInvalidation(self, queuedPublishItemHandler, handlePublishResultInvalidation, $ID(item), $ID(node), $ID(configOptions), $ID(handler))]; + [_queue addObject:$newHandlerWithInvalidation(self, queuedPublishItemHandler, handlePublishResultInvalidation, $ID(item), $ID(node), $ID(configOptions), $HANDLER(handler))]; return; } @@ -306,7 +306,7 @@ -(void) retractItemWithId:(NSString*) itemId onNode:(NSString*) node andHandler: if(!account.connectionProperties.accountDiscoDone) { DDLogWarn(@"Queueing pubsub call until account disco is resolved..."); - [_queue addObject:$newHandlerWithInvalidation(self, queuedRetractItemWithIdHandler, handleRetractResultInvalidation, $ID(itemId), $ID(node), $ID(handler))]; + [_queue addObject:$newHandlerWithInvalidation(self, queuedRetractItemWithIdHandler, handleRetractResultInvalidation, $ID(itemId), $ID(node), $HANDLER(handler))]; return; } @@ -343,7 +343,7 @@ -(void) purgeNode:(NSString*) node andHandler:(MLHandler* _Nullable) handler if(!account.connectionProperties.accountDiscoDone) { DDLogWarn(@"Queueing pubsub call until account disco is resolved..."); - [_queue addObject:$newHandlerWithInvalidation(self, queuedPurgeNodeNodeHandler, handlePurgeOrDeleteResultInvalidation, $ID(node), $ID(handler))]; + [_queue addObject:$newHandlerWithInvalidation(self, queuedPurgeNodeNodeHandler, handlePurgeOrDeleteResultInvalidation, $ID(node), $HANDLER(handler))]; return; } @@ -377,7 +377,7 @@ -(void) deleteNode:(NSString*) node andHandler:(MLHandler* _Nullable) handler if(!account.connectionProperties.accountDiscoDone) { DDLogWarn(@"Queueing pubsub call until account disco is resolved..."); - [_queue addObject:$newHandlerWithInvalidation(self, queuedDeleteNodeHandler, handlePurgeOrDeleteResultInvalidation, $ID(node), $ID(handler))]; + [_queue addObject:$newHandlerWithInvalidation(self, queuedDeleteNodeHandler, handlePurgeOrDeleteResultInvalidation, $ID(node), $HANDLER(handler))]; return; } @@ -772,8 +772,8 @@ -(NSDictionary*) copyDefaultNodeOptions:(NSDictionary*) defaultOptions forConfig } $$ -$$instance_handler(handleInternalFetch, account.pubsub, $$ID(xmpp*, account), $$ID(NSString*, node), $$BOOL(success), $$ID(NSString*, jid), $$ID(NSDictionary*, data)) - if(success == NO) //ignore errors (--> ignore invalidations, too) +$$instance_handler(handleInternalFetch, account.pubsub, $$ID(xmpp*, account), $$ID(NSString*, node), $$BOOL(success), $$ID(NSString*, jid), $_ID(NSDictionary*, data)) + if(success != NO && data != nil) //ignore errors (--> ignore invalidations, too) [self callHandlersForNode:node andJid:jid withType:@"publish" andData:data]; $$ diff --git a/Monal/Classes/MLSQLite.m b/Monal/Classes/MLSQLite.m index 14f914cb6c..a53f110f2b 100644 --- a/Monal/Classes/MLSQLite.m +++ b/Monal/Classes/MLSQLite.m @@ -381,8 +381,12 @@ -(BOOL) boolWriteTransaction:(monal_sqlite_bool_operations_t) operations -(id) idWriteTransaction:(monal_sqlite_operations_t) operations { [self beginWriteTransaction]; + NSDate* startTime = [NSDate date]; id retval = operations(); + NSDate* endTime = [NSDate date]; [self endWriteTransaction]; + if([endTime timeIntervalSinceDate:startTime] > 0.5) + showErrorOnAlpha(nil, @"Write transaction took %fs (longer than 0.5s): %@", (double)[endTime timeIntervalSinceDate:startTime], [NSThread callStackSymbols]); return retval; } diff --git a/Monal/Classes/MLSettingsAboutViewController.m b/Monal/Classes/MLSettingsAboutViewController.m index 28400519ac..066b94cfcb 100644 --- a/Monal/Classes/MLSettingsAboutViewController.m +++ b/Monal/Classes/MLSettingsAboutViewController.m @@ -20,8 +20,7 @@ - (void) viewDidLoad { [super viewDidLoad]; - NSString* versionTxt = [HelperTools appBuildVersionInfo]; - [self.aboutVersion setText:versionTxt]; + [self.aboutVersion setText: [HelperTools appBuildVersionInfoFor:MLVersionTypeIQ]]; UIBarButtonItem* leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemClose target:self action:@selector(close:)]; self.navigationItem.leftBarButtonItem = leftBarButtonItem; diff --git a/Monal/Classes/MLSettingsTableViewController.m b/Monal/Classes/MLSettingsTableViewController.m index 3db3e56852..7ad6b9800d 100644 --- a/Monal/Classes/MLSettingsTableViewController.m +++ b/Monal/Classes/MLSettingsTableViewController.m @@ -253,7 +253,7 @@ -(UITableViewCell*) tableView:(UITableView*) tableView cellForRowAtIndexPath:(NS break; } case VersionRow: { - [cell initCell:NSLocalizedString(@"Version", @"") withLabel:[HelperTools appBuildVersionInfo]]; + [cell initCell:NSLocalizedString(@"Version", @"") withLabel:[HelperTools appBuildVersionInfoFor:MLVersionTypeIQ]]; break; } #ifdef DEBUG @@ -386,7 +386,7 @@ -(void)tableView:(UITableView*) tableView didSelectRowAtIndexPath:(NSIndexPath*) _tappedVersionInfo++; #endif UIPasteboard* pastboard = UIPasteboard.generalPasteboard; - pastboard.string = [HelperTools appBuildVersionInfo]; + pastboard.string = [HelperTools appBuildVersionInfoFor:MLVersionTypeIQ]; break; } default: diff --git a/Monal/Classes/MonalAppDelegate.m b/Monal/Classes/MonalAppDelegate.m index c1214f9297..b7a51dcead 100644 --- a/Monal/Classes/MonalAppDelegate.m +++ b/Monal/Classes/MonalAppDelegate.m @@ -287,6 +287,28 @@ -(void) runSDPTests a=max-message-size:262144\n" withInitiator:YES]); } +$$class_handler(handlerTest01, $$ID(NSObject*, dummyObj)) + DDLogError(@"HandlerTest01 completed"); +$$ + +$$class_handler(handlerTest02, $$ID(monal_void_block_t, dummyCallback)) + DDLogError(@"HandlerTest02 completed"); +$$ + +-(void) runHandlerTests +{ + DDLogError(@"NSClassFromString: '%@'", NSClassFromString(@"monal_void_block_t")); + + if([^{} isKindOfClass:[NSObject class]]) + DDLogError(@"isKindOfClass"); + + MLHandler* handler01 = $newHandler([self class], handlerTest01); + $call(handler01, $ID(dummyObj, [NSString new])); + + MLHandler* handler02 = $newHandler([self class], handlerTest02); + $call(handler02, $ID(dummyCallback, ^{})); +} + -(id) init { //someone (suspect: AppKit) resets our exception handler between the call to [MonalAppDelegate initialize] and [MonalAppDelegate init] @@ -302,6 +324,7 @@ -(id) init //[self runParserTests]; //[self runSDPTests]; //[HelperTools flushLogsWithTimeout:0.250]; + //[self runHandlerTests]; return self; } @@ -536,11 +559,7 @@ -(BOOL) application:(UIApplication*) application didFinishLaunchingWithOptions:( [HelperTools configureDefaultAudioSession]; self.audioState = MLAudioStateNormal; - NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary]; - NSString* version = [infoDict objectForKey:@"CFBundleShortVersionString"]; - NSString* buildDate = [NSString stringWithUTF8String:__DATE__]; - NSString* buildTime = [NSString stringWithUTF8String:__TIME__]; - DDLogInfo(@"App started: %@", [NSString stringWithFormat:NSLocalizedString(@"Version %@ (%@ %@ UTC)", @""), version, buildDate, buildTime]); + DDLogInfo(@"App started: %@", [HelperTools appBuildVersionInfoFor:MLVersionTypeLog]); //init background/foreground status //this has to be done here to make sure we have the correct state when he app got started through notification quick actions diff --git a/Monal/Classes/PasswordMigration.swift b/Monal/Classes/PasswordMigration.swift index 36c58a2066..20c9216f00 100644 --- a/Monal/Classes/PasswordMigration.swift +++ b/Monal/Classes/PasswordMigration.swift @@ -57,7 +57,7 @@ struct PasswordMigration: View { } )) - SecureField("Password", text:Binding( + SecureField(NSLocalizedString("Password", comment: "placeholder when migrating account"), text:Binding( get: { self.needingMigration[id]?["password"] as? String ?? "" }, set: { self.needingMigration[id]?["password"] = $0 as NSString diff --git a/Monal/Classes/RegisterAccount.swift b/Monal/Classes/RegisterAccount.swift index e184d67b09..dda5c9885a 100644 --- a/Monal/Classes/RegisterAccount.swift +++ b/Monal/Classes/RegisterAccount.swift @@ -321,7 +321,7 @@ struct RegisterAccount: View { Group { if(selectedServerIndex == 0) { - TextField("Provide XMPP-Server", text: Binding( + TextField(NSLocalizedString("Provide XMPP-Server", comment: "placeholder when creating account"), text: Binding( get: { self.providedServer }, set: { string in self.providedServer = string.lowercased().replacingOccurrences(of: " ", with: "") } )) @@ -332,7 +332,7 @@ struct RegisterAccount: View { .disabled(self.registerToken != nil) } - TextField("Username", text: Binding( + TextField(NSLocalizedString("Username", comment: "placeholder when creating account"), text: Binding( get: { self.username }, set: { string in self.username = string.lowercased().replacingOccurrences(of: " ", with: "") } )) @@ -340,8 +340,8 @@ struct RegisterAccount: View { .autocapitalization(.none) .autocorrectionDisabled() - SecureField("Password", text: $password) - SecureField("Password (repeated)", text: $repeatedPassword) + SecureField(NSLocalizedString("Password", comment: "placeholder when creating account"), text: $password) + SecureField(NSLocalizedString("Password (repeated)", comment: "placeholder when creating account"), text: $repeatedPassword) } if(self.captchaImg != nil) { @@ -355,7 +355,7 @@ struct RegisterAccount: View { }) .buttonStyle(.borderless) } - TextField("Captcha", text: $captchaText) + TextField(NSLocalizedString("Captcha", comment: "placeholder when creating account"), text: $captchaText) //ios15: .textInputAutocapitalization(.never) .autocapitalization(.none) .autocorrectionDisabled() diff --git a/Monal/Classes/SwiftuiHelpers.swift b/Monal/Classes/SwiftuiHelpers.swift index bd9bc8390d..951c9f8f82 100644 --- a/Monal/Classes/SwiftuiHelpers.swift +++ b/Monal/Classes/SwiftuiHelpers.swift @@ -21,8 +21,6 @@ extension MLContact : Identifiable {} //make MLContact be usable in swiftu let monalGreen = Color(UIColor(red:128.0/255, green:203.0/255, blue:182.0/255, alpha:1.0)); let monalDarkGreen = Color(UIColor(red:20.0/255, green:138.0/255, blue:103.0/255, alpha:1.0)); -let swiftuiTranslationDummyString = Text("Dummy string to test SwiftUI translation support.") - //see https://stackoverflow.com/a/62207329/3528174 public extension Color { #if os(macOS) diff --git a/Monal/Classes/WelcomeLogIn.swift b/Monal/Classes/WelcomeLogIn.swift index 6e171bf3bf..ee9d4d357b 100644 --- a/Monal/Classes/WelcomeLogIn.swift +++ b/Monal/Classes/WelcomeLogIn.swift @@ -129,7 +129,7 @@ struct WelcomeLogIn: View { //for ios >= 15.0 //.listRowSeparator(.hidden) - TextField("user@domain.tld", text: Binding( + TextField(NSLocalizedString("user@domain.tld", comment: "placeholder when adding account"), text: Binding( get: { self.jid }, set: { string in self.jid = string.lowercased().replacingOccurrences(of: " ", with: "") }) ) @@ -138,7 +138,7 @@ struct WelcomeLogIn: View { .autocorrectionDisabled() .keyboardType(.emailAddress) - SecureField("Password", text: $password) + SecureField(NSLocalizedString("Password", comment: "placeholder when adding account"), text: $password) HStack() { Button(action: { diff --git a/Monal/Classes/XMPPIQ.m b/Monal/Classes/XMPPIQ.m index ab1ad15627..93b6ef25b9 100644 --- a/Monal/Classes/XMPPIQ.m +++ b/Monal/Classes/XMPPIQ.m @@ -301,7 +301,7 @@ -(void) setVersion #else [[MLXMLNode alloc] initWithElement:@"os" andData:[NSString stringWithFormat:@"iOS %lu", osVersion.majorVersion]], #endif - [[MLXMLNode alloc] initWithElement:@"version" andData:[HelperTools appBuildVersionInfo]] + [[MLXMLNode alloc] initWithElement:@"version" andData:[HelperTools appBuildVersionInfoFor:MLVersionTypeIQ]] ] andData:nil]]; } diff --git a/Monal/Classes/chatViewController.m b/Monal/Classes/chatViewController.m index 603f69fcd0..3e56a3c039 100644 --- a/Monal/Classes/chatViewController.m +++ b/Monal/Classes/chatViewController.m @@ -494,12 +494,15 @@ -(void) handleForeGround -(void) openCallScreen:(id) sender { - if(![[DataLayer sharedInstance] checkCap:@"urn:xmpp:jingle-message:0" forUser:self.contact.contactJid onAccountNo:self.contact.accountId]) + MLAssert(sender != nil || self.callButton != nil, @"We need at least one ui source (e.g. button) to base the popover controller upon!"); + if(sender == nil) + sender = self.callButton; + + MonalAppDelegate* appDelegate = (MonalAppDelegate *)[[UIApplication sharedApplication] delegate]; + MLCall* activeCall = [appDelegate.voipProcessor getActiveCallWithContact:self.contact]; + if(activeCall == nil && ![[DataLayer sharedInstance] checkCap:@"urn:xmpp:jingle-message:0" forUser:self.contact.contactJid onAccountNo:self.contact.accountId]) { - NSInteger style = UIAlertControllerStyleActionSheet; - if([HelperTools deviceUsesSplitView]) - style = UIAlertControllerStyleAlert; - UIAlertController* alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Missing Call Support", @"") message:NSLocalizedString(@"Your contact may not support calls. Your call might never reach its destination.", @"") preferredStyle:style]; + UIAlertController* alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Missing Call Support", @"") message:NSLocalizedString(@"Your contact may not support calls. Your call might never reach its destination.", @"") preferredStyle:UIAlertControllerStyleActionSheet]; [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Try nevertheless", @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { [self dismissViewControllerAnimated:YES completion:nil]; @@ -510,6 +513,11 @@ -(void) openCallScreen:(id) sender [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { [self dismissViewControllerAnimated:YES completion:nil]; }]]; + UIPopoverPresentationController* popPresenter = [alert popoverPresentationController]; + if(@available(iOS 16.0, macCatalyst 16.0, *)) + popPresenter.sourceItem = sender; + else + popPresenter.barButtonItem = sender; [self presentViewController:alert animated:YES completion:nil]; } else diff --git a/Monal/Classes/commithash.h b/Monal/Classes/commithash.h new file mode 100644 index 0000000000..967e5cd7da --- /dev/null +++ b/Monal/Classes/commithash.h @@ -0,0 +1 @@ +#define ALPHA_COMMIT_HASH "HEAD" \ No newline at end of file diff --git a/Monal/Classes/xmpp.m b/Monal/Classes/xmpp.m index 6a0a442d23..49a2303b1d 100644 --- a/Monal/Classes/xmpp.m +++ b/Monal/Classes/xmpp.m @@ -4889,6 +4889,10 @@ -(BOOL) writeToStream:(NSString*) messageOut -(void) enablePush { +#if TARGET_OS_SIMULATOR + DDLogError(@"Not registering push on the simulator!"); + [self disablePush]; +#else NSString* pushToken = [MLXMPPManager sharedInstance].pushToken; NSString* selectedPushServer = [[HelperTools defaultsDB] objectForKey:@"selectedPushServer"]; if(pushToken == nil || [pushToken length] == 0 || selectedPushServer == nil || self.accountState < kStateBound) @@ -4929,6 +4933,7 @@ -(void) enablePush [self disablePush]; } } +#endif } -(void) disablePush @@ -4995,13 +5000,14 @@ -(void) updateIqHandlerTimeouts BOOL stateUpdated = NO; @synchronized(_iqHandlers) { //we are NOT mutating on iteration here, because we use dispatchAsyncOnReceiveQueue to handle timeouts + NSMutableArray* idsToRemove = [NSMutableArray new]; for(NSString* iqid in _iqHandlers) { //decrement handler timeout every second and check if it landed below zero --> trigger a fake iq error to handle timeout //this makes sure a freeze/killed app doesn't immediately trigger timeouts once the app is restarted, as it would be with timestamp based timeouts //doing it this way makes sure the incoming iq result has a chance to be processed even in a freeze/kill scenario _iqHandlers[iqid][@"timeout"] = @([_iqHandlers[iqid][@"timeout"] doubleValue] - 1.0); - if([_iqHandlers[iqid][@"timeout"] doubleValue] < 0) + if([_iqHandlers[iqid][@"timeout"] doubleValue] < 0.0) { DDLogWarn(@"Timeout of handler triggered: %@", _iqHandlers[iqid]); //only force save state after calling a handler @@ -5022,30 +5028,32 @@ -(void) updateIqHandlerTimeouts ] andData:nil]]; //make sure our fake error iq is handled inside the receiveQueue - [self dispatchAsyncOnReceiveQueue:^{ - //extract this from _iqHandlers to make sure we only handle iqs that didn't get handled in the meantime - NSMutableDictionary* iqHandler = nil; - @synchronized(self->_iqHandlers) { - iqHandler = self->_iqHandlers[iqid]; - } - if(iqHandler) - { - DDLogDebug(@"Calling iq handler with faked error iq: %@", errorIq); - if(iqHandler[@"handler"] != nil) - $call(iqHandler[@"handler"], $ID(account, self), $ID(iqNode, errorIq)); - else if(iqHandler[@"errorHandler"] != nil) - ((monal_iq_handler_t) iqHandler[@"errorHandler"])(errorIq); - - //remove handler after calling it - @synchronized(self->_iqHandlers) { - [self->_iqHandlers removeObjectForKey:iqid]; - } - } - else - DDLogWarn(@"iq handler for '%@' vanished while switching to receive queue", iqid); - }]; + //extract this from _iqHandlers to make sure we only handle iqs that didn't get handled in the meantime + NSMutableDictionary* iqHandler = self->_iqHandlers[iqid]; + [idsToRemove addObject:iqid]; + if(iqHandler) + { + //do a real async dispatch, not an automatic sync one because we are in the same queue + [_receiveQueue addOperations:@[[NSBlockOperation blockOperationWithBlock:^{ + //make sure these handlers are called inside a db write transaction just like receiving a real error iq + //--> don't create a deadlock with 2 threads waiting for db write transaction and synchronized iqhandlers + // in opposite order + [[DataLayer sharedInstance] createTransaction:^{ + DDLogDebug(@"Calling iq handler with faked error iq: %@", errorIq); + if(iqHandler[@"handler"] != nil) + $call(iqHandler[@"handler"], $ID(account, self), $ID(iqNode, errorIq)); + else if(iqHandler[@"errorHandler"] != nil) + ((monal_iq_handler_t) iqHandler[@"errorHandler"])(errorIq); + }]; + }]] waitUntilFinished:NO]; + } + else + DDLogWarn(@"iq handler for '%@' vanished while switching to receive queue", iqid); } } + //now delete iqs marked for deletion + for(NSString* iqid in idsToRemove) + [_iqHandlers removeObjectForKey:iqid]; } //make sure all state is persisted as soon as possible (we could have called handlers and we don't want to execute them twice!) diff --git a/Monal/Monal.xcodeproj/project.pbxproj b/Monal/Monal.xcodeproj/project.pbxproj index 116e32bcc7..ddf1e17867 100644 --- a/Monal/Monal.xcodeproj/project.pbxproj +++ b/Monal/Monal.xcodeproj/project.pbxproj @@ -2592,7 +2592,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 14.0; LLVM_LTO = YES; MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 6.0.2; + MARKETING_VERSION = 6.0.3; SDKROOT = iphoneos; SUPPORTS_MACCATALYST = YES; SWIFT_VERSION = 5.0; @@ -2894,7 +2894,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 14.0; LLVM_LTO = YES; MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 6.0.2; + MARKETING_VERSION = 6.0.3; SDKROOT = iphoneos; SUPPORTS_MACCATALYST = YES; SWIFT_VERSION = 5.0; @@ -3041,7 +3041,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 14.0; LLVM_LTO = YES; MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 6.0.2; + MARKETING_VERSION = 6.0.3; ONLY_ACTIVE_ARCH = YES; RUN_CLANG_STATIC_ANALYZER = YES; SDKROOT = iphoneos; @@ -3302,7 +3302,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 14.0; LLVM_LTO = YES; MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 6.0.2; + MARKETING_VERSION = 6.0.3; RUN_CLANG_STATIC_ANALYZER = YES; SDKROOT = iphoneos; SUPPORTS_MACCATALYST = YES; @@ -3657,7 +3657,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 14.0; LLVM_LTO = NO; MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 6.0.2; + MARKETING_VERSION = 6.0.3; RUN_CLANG_STATIC_ANALYZER = YES; SDKROOT = iphoneos; SUPPORTS_MACCATALYST = YES; diff --git a/Monal/NotificationService/NotificationService.m b/Monal/NotificationService/NotificationService.m index 8a753ac8d3..6bf00d4a4f 100644 --- a/Monal/NotificationService/NotificationService.m +++ b/Monal/NotificationService/NotificationService.m @@ -427,10 +427,8 @@ +(void) initialize [MLProcessLock initializeForProcess:@"NotificationServiceExtension"]; //log startup - NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary]; - NSString* version = [infoDict objectForKey:@"CFBundleShortVersionString"]; - NSString* buildDate = [NSString stringWithUTF8String:__DATE__]; - NSString* buildTime = [NSString stringWithUTF8String:__TIME__]; + DDLogInfo(@"Notification Service Extension started: %@", [HelperTools appBuildVersionInfoFor:MLVersionTypeLog]); + [DDLog flushLog]; warnUnclean = ![NotificationService getAppexCleanShutdownStatus]; if(warnUnclean) @@ -438,9 +436,6 @@ +(void) initialize //mark this appex as unclean (will be cleared directly before calling exit(0)) [NotificationService setAppexCleanShutdownStatus:NO]; - - DDLogInfo(@"Notification Service Extension started: %@", [NSString stringWithFormat:NSLocalizedString(@"Version %@ (%@ %@ UTC)", @ ""), version, buildDate, buildTime]); - [DDLog flushLog]; } +(BOOL) getAppexCleanShutdownStatus diff --git a/Monal/shareSheet-iOS/ShareViewController.m b/Monal/shareSheet-iOS/ShareViewController.m index 2998004e18..e30d38febe 100644 --- a/Monal/shareSheet-iOS/ShareViewController.m +++ b/Monal/shareSheet-iOS/ShareViewController.m @@ -44,11 +44,7 @@ +(void) initialize [IPC initializeForProcess:@"ShareSheetExtension"]; //log startup - NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary]; - NSString* version = [infoDict objectForKey:@"CFBundleShortVersionString"]; - NSString* buildDate = [NSString stringWithUTF8String:__DATE__]; - NSString* buildTime = [NSString stringWithUTF8String:__TIME__]; - DDLogInfo(@"Share Sheet Extension started: %@", [NSString stringWithFormat:NSLocalizedString(@"Version %@ (%@ %@ UTC)", @ ""), version, buildDate, buildTime]); + DDLogInfo(@"Share Sheet Extension started: %@", [HelperTools appBuildVersionInfoFor:MLVersionTypeLog]); [DDLog flushLog]; } diff --git a/scripts/updateLocalization.sh b/scripts/updateLocalization.sh index a595bcd2f8..d5ad34b588 100755 --- a/scripts/updateLocalization.sh +++ b/scripts/updateLocalization.sh @@ -5,64 +5,42 @@ set -e cd "$(dirname "$0")" cd ../Monal -git submodule deinit --all -f -git submodule update --init --recursive --remote - -#subshell to not leak from "cd $folder" -( - cd "localization/external" - if [[ $1 == "BUILDSERVER" ]]; then - git remote set-url origin git@main.translation.repo:monal-im/Monal-localization-main.git - else - git remote set-url origin git@github.com:monal-im/Monal-localization-main.git - fi - echo "Git remote is now:" - git remote --verbose - git checkout main - git reset --hard origin/main -) -#subshell to not leak from "cd $folder" -( - cd "shareSheet-iOS/localization/external" - if [[ $1 == "BUILDSERVER" ]]; then - git remote set-url origin git@sharesheet.translation.repo:monal-im/Monal-localization-shareSheet.git - else - git remote set-url origin git@github.com:monal-im/Monal-localization-shareSheet.git - fi - echo "Git remote is now:" - git remote --verbose - git checkout main - git reset --hard origin/main -) - -# extract xliff file (has to be run multiple times, even if no error occured, don't ask me why) -# we use grep here to test for a dummy string to detect if our run succeeded -if [ -e localization.tmp ]; then - rm -rf localization.tmp -fi -x=$((1)) -while [[ $x < 8 ]]; do - echo "RUN $x..." - while ! xcrun xcodebuild -exportLocalizations -localizationPath localization.tmp -exportLanguage base SWIFT_EMIT_LOC_STRINGS=NO; do - echo "ERROR, TRYING AGAIN..." - done - if grep -q 'Dummy string to test SwiftUI translation support.' "localization.tmp/base.xcloc/Localized Contents/base.xliff"; then - echo "SUCCESS!" - break - fi - x=$((x+1)) -done -rm -rf *A\ Document\ Being\ Saved\ By\ xcodebuild* -if [[ $x == 8 ]]; then - echo "Could not extract dummy string!" +if ! which bartycrouch > /dev/null; then + echo "ERROR: BartyCrouch not installed, download it from https://github.com/Flinesoft/BartyCrouch" exit 1 fi + +function pullCurrentState { + #subshell to not leak from "cd $folder" + ( + cd "localization/external" + if [[ $1 == "BUILDSERVER" ]]; then + git remote set-url origin git@main.translation.repo:monal-im/Monal-localization-main.git + else + git remote set-url origin git@github.com:monal-im/Monal-localization-main.git + fi + echo "Git remote is now:" + git remote --verbose + git checkout main + git reset --hard origin/main + ) + #subshell to not leak from "cd $folder" + ( + cd "shareSheet-iOS/localization/external" + if [[ $1 == "BUILDSERVER" ]]; then + git remote set-url origin git@sharesheet.translation.repo:monal-im/Monal-localization-shareSheet.git + else + git remote set-url origin git@github.com:monal-im/Monal-localization-shareSheet.git + fi + echo "Git remote is now:" + git remote --verbose + git checkout main + git reset --hard origin/main + ) +} -# Run bartycrouch -# https://github.com/Flinesoft/BartyCrouch#exclude-specific-views--nslocalizedstrings-from-localization -if which bartycrouch > /dev/null; then - # extract additional strings from xliff file and add them to our strings file (bartycrouch will remove duplicates later on) - ../scripts/xliff_extractor.py -x "localization.tmp/base.xcloc/Localized Contents/base.xliff" +function runBartycrouch { + # https://github.com/Flinesoft/BartyCrouch#exclude-specific-views--nslocalizedstrings-from-localization # update normally using bartycrouch and use it to sync our SwiftUI translations from base language to all other languages bartycrouch update -x # clean up all files @@ -78,10 +56,51 @@ if which bartycrouch > /dev/null; then done # lint everything now bartycrouch lint -x -w -else - echo "warning: BartyCrouch not installed, download it from https://github.com/Flinesoft/BartyCrouch" +} + +git submodule deinit --all -f +git submodule update --init --recursive --remote +pullCurrentState "$@" + +# update strings to remove everything that's now unused (that includes swiftui strings we'll readd below) +cp .bartycrouch.toml .bartycrouch.toml.orig +sed 's/additive = true/additive = false/g' .bartycrouch.toml > .bartycrouch.toml.new +mv .bartycrouch.toml.new .bartycrouch.toml +runBartycrouch +mv .bartycrouch.toml.orig .bartycrouch.toml + +# now restore original state for all languages but our base one (otherwise every swiftui translation will be deleted) +mv "localization/external/Base.lproj/Localizable.strings" "localization/external/Base.lproj/Localizable.strings.updated" +pullCurrentState "$@" +mv "localization/external/Base.lproj/Localizable.strings.updated" "localization/external/Base.lproj/Localizable.strings" + +# extract xliff file (has to be run multiple times, even if no error occured, don't ask me why) +# we use grep here to test for a dummy string to detect if our run succeeded +if [ -e localization.tmp ]; then + rm -rf localization.tmp +fi +dummy="DON'T TRANSLATE: $(head /dev/urandom | LC_ALL=C tr -dc A-Za-z0-9 | head -c 8)" +echo "\nlet swiftuiTranslationRandomDummyString = Text(\"$dummy\")" >> Classes/SwiftuiHelpers.swift +x=$((1)) +while [[ $x -lt 16 ]]; do + echo "STARTING RUN $x..." + while ! xcrun xcodebuild -exportLocalizations -localizationPath localization.tmp -exportLanguage base SWIFT_EMIT_LOC_STRINGS=NO; do + echo "ERROR, TRYING AGAIN..." + done + echo "RUN $x SUCCEEDED, EXTRACTING STRINGS FROM XLIFF!" + # extract additional strings from xliff file and add them to our strings file (bartycrouch will remove duplicates later on) + ../scripts/xliff_extractor.py -x "localization.tmp/base.xcloc/Localized Contents/base.xliff" + x=$((x+1)) +done +rm -rf *A\ Document\ Being\ Saved\ By\ xcodebuild* +if ! grep -q "$dummy" "localization/external/Base.lproj/Localizable.strings"; then + echo "Could not extract dummy string after $x runs!" exit 1 fi +awk "!/$dummy/" "localization/external/Base.lproj/Localizable.strings" > "localization/external/Base.lproj/Localizable.strings.new" +mv "localization/external/Base.lproj/Localizable.strings.new" "localization/external/Base.lproj/Localizable.strings" + +runBartycrouch if [ -e localization.tmp ]; then rm -rf localization.tmp @@ -106,3 +125,4 @@ done git submodule deinit --all -f git submodule update --init --recursive +exit 0