From 4a0cca22b8530b4e57b31b2f30b4acd14afc6ec6 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 26 Feb 2025 01:11:55 -0800 Subject: [PATCH] Feature: Replaced Crashlytics with Sentry Crash logging is now handled by the Sentry service. Signed-off-by: Christopher Snowhill --- .gitignore | 1 + Application/AppController.m | 64 +++++++--- Application/PlaybackController.m | 24 ++-- Audio/PluginController.mm | 2 - Base.lproj/Feedback.xib | 93 +++++++------- Cog.xcodeproj/project.pbxproj | 63 ++++----- .../xcshareddata/swiftpm/Package.resolved | 120 +----------------- Feedback/FeedbackController.h | 13 +- Feedback/FeedbackController.m | 85 +++++++------ Info.plist.template | 2 - Playlist/PlaylistLoader.m | 6 +- Playlist/PlaylistView.m | 4 +- .../Preferences/Base.lproj/Preferences.xib | 2 +- Window/MainWindow.m | 14 +- Window/MiniWindow.m | 4 +- Xcode-config/Shared.xcconfig | 5 + en.lproj/Feedback.strings | 12 +- en.lproj/Localizable.strings | 4 +- es.lproj/Feedback.strings | 14 +- es.lproj/Localizable.strings | 4 +- 20 files changed, 232 insertions(+), 304 deletions(-) diff --git a/.gitignore b/.gitignore index a372da51a..58da8c0c3 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ xcuserdata # User-specific xcconfig files Xcode-config/DEVELOPMENT_TEAM.xcconfig +Xcode-config/SENTRY_SETTINGS.xcconfig # Plist derived from template at build time /Info.plist diff --git a/Application/AppController.m b/Application/AppController.m index e2dff470a..1eeb18750 100644 --- a/Application/AppController.m +++ b/Application/AppController.m @@ -31,7 +31,9 @@ #import "PreferencesController.h" -@import Firebase; +#import "FeedbackController.h" + +@import Sentry; void *kAppControllerContext = &kAppControllerContext; @@ -166,19 +168,14 @@ static AppController *kAppController = nil; return [key isEqualToString:@"currentEntry"]; } -- (void)awakeFromNib { -#if DEBUG - [[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"NSApplicationCrashOnExceptions": @(NO) }]; -#else - [[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"NSApplicationCrashOnExceptions": @(YES) }]; -#endif - [[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"crashlyticsConsented": @(NO), - @"crashlyticsAskedConsent": @(NO) }]; +static BOOL consentLastEnabled = NO; - [FIRApp configure]; - - [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.crashlyticsConsented" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kAppControllerContext]; +- (void)awakeFromNib { + [[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"sentryConsented": @(NO), + @"sentryAskedConsent": @(NO) }]; + [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.sentryConsented" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kAppControllerContext]; + [[totalTimeField cell] setBackgroundStyle:NSBackgroundStyleRaised]; [self.infoButton setToolTip:NSLocalizedString(@"InfoButtonTooltip", @"")]; @@ -323,10 +320,45 @@ static AppController *kAppController = nil; return; } - if([keyPath isEqualToString:@"values.crashlyticsConsented"]) { - BOOL enabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"crashlyticsConsented"]; - [[FIRCrashlytics crashlytics] setCrashlyticsCollectionEnabled:enabled]; - [FIRAnalytics setAnalyticsCollectionEnabled:enabled]; + if([keyPath isEqualToString:@"values.sentryConsented"]) { + BOOL enabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"sentryConsented"]; + if(enabled != consentLastEnabled) { + if(enabled) { + [SentrySDK startWithConfigureOptions:^(SentryOptions *options) { + options.dsn = @"https://b5eda1c2390eb965a74dd735413b6392@cog-analytics.losno.co/3"; + options.debug = YES; // Enabled debug when first installing is always helpful + + // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring. + // We recommend adjusting this value in production. + options.tracesSampleRate = @1.0; + + // Adds IP for users. + // For more information, visit: https://docs.sentry.io/platforms/apple/data-management/data-collected/ + options.sendDefaultPii = YES; + + // And now to set up user feedback prompting + options.onCrashedLastRun = ^void(SentryEvent * _Nonnull event) { + // capture user feedback + FeedbackController *fbcon = [[FeedbackController alloc] init]; + [fbcon performSelectorOnMainThread:@selector(showWindow:) withObject:nil waitUntilDone:YES]; + if([fbcon waitForCompletion]) { + SentryUserFeedback *userFeedback = [[SentryUserFeedback alloc] initWithEventId:[event eventId]]; + + userFeedback.comments = [fbcon comments]; + userFeedback.email = [fbcon email]; + userFeedback.name = [fbcon name]; + + [SentrySDK captureUserFeedback:userFeedback]; + } + }; + }]; + } else { + if([SentrySDK isEnabled]) { + [SentrySDK close]; + } + } + consentLastEnabled = enabled; + } } else if([keyPath isEqualToString:@"playlistController.currentEntry"]) { PlaylistEntry *entry = playlistController.currentEntry; NSString *appTitle = NSLocalizedString(@"CogTitle", @""); diff --git a/Application/PlaybackController.m b/Application/PlaybackController.m index 425858097..4b674c434 100644 --- a/Application/PlaybackController.m +++ b/Application/PlaybackController.m @@ -19,7 +19,7 @@ #import "Logging.h" -@import Firebase; +//@import Sentry; extern BOOL kAppControllerShuttingDown; @@ -284,11 +284,11 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) { if(!pe.url) { pe.error = YES; pe.errorMessage = NSLocalizedStringFromTableInBundle(@"ErrorMessageBadFile", nil, [NSBundle bundleForClass:[self class]], @""); - [[FIRCrashlytics crashlytics] log:@"Attempting to play bad file."]; + //[[FIRCrashlytics crashlytics] log:@"Attempting to play bad file."]; return; } - [[FIRCrashlytics crashlytics] logWithFormat:@"Playing track: %@", pe.url]; + //[[FIRCrashlytics crashlytics] logWithFormat:@"Playing track: %@", pe.url]; DLog(@"PLAYLIST CONTROLLER: %@", [playlistController class]); [playlistController setCurrentEntry:pe]; @@ -767,15 +767,15 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) { } if(pe && pe.url) { - [[FIRCrashlytics crashlytics] logWithFormat:@"Beginning decoding track: %@", pe.url]; + //[[FIRCrashlytics crashlytics] logWithFormat:@"Beginning decoding track: %@", pe.url]; [player setNextStream:pe.url withUserInfo:pe withRGInfo:makeRGInfo(pe)]; } else if(pe) { - [[FIRCrashlytics crashlytics] log:@"Invalid playlist entry reached."]; + //[[FIRCrashlytics crashlytics] log:@"Invalid playlist entry reached."]; [player setNextStream:nil]; pe.error = YES; pe.errorMessage = NSLocalizedStringFromTableInBundle(@"ErrorMessageBadFile", nil, [NSBundle bundleForClass:[self class]], @""); } else { - [[FIRCrashlytics crashlytics] log:@"End of playlist reached."]; + //[[FIRCrashlytics crashlytics] log:@"End of playlist reached."]; [player setNextStream:nil]; } } @@ -786,7 +786,7 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) { // Delay the action until this function has returned to the audio thread dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ if(pe) { - [[FIRCrashlytics crashlytics] logWithFormat:@"Updating UI with track: %@", pe.url]; + //[[FIRCrashlytics crashlytics] logWithFormat:@"Updating UI with track: %@", pe.url]; } [self->playlistController setCurrentEntry:pe]; @@ -817,7 +817,7 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) { } if(status == CogStatusStopped) { - [[FIRCrashlytics crashlytics] log:@"Stopped."]; + //[[FIRCrashlytics crashlytics] log:@"Stopped."]; [self setPosition:0]; [self setSeekable:NO]; // the player stopped, disable the slider @@ -825,11 +825,11 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) { [[NSNotificationCenter defaultCenter] postNotificationName:CogPlaybackDidStopNotificiation object:nil]; } else // paused { - [[FIRCrashlytics crashlytics] log:@"Paused."]; + //[[FIRCrashlytics crashlytics] log:@"Paused."]; [[NSNotificationCenter defaultCenter] postNotificationName:CogPlaybackDidPauseNotificiation object:nil]; } } else if(status == CogStatusPlaying) { - [[FIRCrashlytics crashlytics] log:@"Started playing."]; + //[[FIRCrashlytics crashlytics] log:@"Started playing."]; if(!positionTimer) { positionTimer = [NSTimer timerWithTimeInterval:0.2 target:self selector:@selector(updatePosition:) userInfo:nil repeats:YES]; @@ -865,7 +865,7 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) { - (void)audioPlayer:(AudioPlayer *)player didStopNaturally:(id)userInfo { if([[NSUserDefaults standardUserDefaults] boolForKey:@"quitOnNaturalStop"]) { - [[FIRCrashlytics crashlytics] log:@"Terminating due to natural stop."]; + //[[FIRCrashlytics crashlytics] log:@"Terminating due to natural stop."]; [NSApp terminate:nil]; } } @@ -880,7 +880,7 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) { - (void)audioPlayer:(AudioPlayer *)player restartPlaybackAtCurrentPosition:(id)userInfo { PlaylistEntry *pe = [playlistController currentEntry]; BOOL paused = playbackStatus == CogStatusPaused; - [[FIRCrashlytics crashlytics] logWithFormat:@"Restarting playback of track: %@", pe.url]; + //[[FIRCrashlytics crashlytics] logWithFormat:@"Restarting playback of track: %@", pe.url]; [player performSelectorOnMainThread:@selector(playBG:withUserInfo:withRGInfo:startPaused:andSeekTo:) withObjects:pe.url, pe, makeRGInfo(pe), @(paused), @(pe.seekable ? pe.currentPosition : 0.0), nil]; } diff --git a/Audio/PluginController.mm b/Audio/PluginController.mm index e4a77728c..bb4b149cd 100644 --- a/Audio/PluginController.mm +++ b/Audio/PluginController.mm @@ -389,8 +389,6 @@ static NSString *xmlEscapeString(NSString * string) { \n\ \n\ \n\ -\tFirebaseCrashlyticsCollectionEnabled\n\ -\t\n\ \tSUEnableInstallerLauncherService\n\ \t\n\ \tCFBundleDevelopmentRegion\n\ diff --git a/Base.lproj/Feedback.xib b/Base.lproj/Feedback.xib index 11d89a9ea..beba9f061 100644 --- a/Base.lproj/Feedback.xib +++ b/Base.lproj/Feedback.xib @@ -1,23 +1,22 @@ - + - + - - - - + + + - + @@ -27,7 +26,37 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -36,22 +65,13 @@ - + - - - - - - - - - - + - + @@ -61,15 +81,15 @@ - + - + - + @@ -80,7 +100,7 @@ - + @@ -111,31 +131,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Cog.xcodeproj/project.pbxproj b/Cog.xcodeproj/project.pbxproj index 3f9adc5c8..0ae4a735e 100644 --- a/Cog.xcodeproj/project.pbxproj +++ b/Cog.xcodeproj/project.pbxproj @@ -143,6 +143,7 @@ 837DC931285B3F790005C58A /* DataModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 837DC92F285B3F790005C58A /* DataModel.xcdatamodeld */; }; 8381A09227C5F72F00A1C530 /* SHA256Digest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8381A09127C5F72F00A1C530 /* SHA256Digest.m */; }; 8384914018083E4E00E7332D /* filetype.icns in Resources */ = {isa = PBXBuildFile; fileRef = 8384913D18083E4E00E7332D /* filetype.icns */; }; + 838770FA2D6F0996001455A0 /* FeedbackController.m in Sources */ = {isa = PBXBuildFile; fileRef = 838770F42D6F0996001455A0 /* FeedbackController.m */; }; 838A33742D06A9B100D0D770 /* librubberband.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 838A33732D06A9B100D0D770 /* librubberband.3.dylib */; }; 838A33752D06A9CE00D0D770 /* librubberband.3.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 838A33732D06A9B100D0D770 /* librubberband.3.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 838A337D2D06C14200D0D770 /* TempoSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A337C2D06C14200D0D770 /* TempoSlider.m */; }; @@ -153,9 +154,6 @@ 83922FBA286B1AA900A0B039 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83922FB6286B1AA900A0B039 /* WebKit.framework */; }; 839614A2286ED97200D3EEDB /* AboutWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 839614A0286ED97200D3EEDB /* AboutWindowController.xib */; }; 839614AD286EDA5C00D3EEDB /* SpectrumWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 839614AB286EDA5C00D3EEDB /* SpectrumWindow.xib */; }; - 83978E16285C58190076ED21 /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = 83978E15285C58190076ED21 /* FirebaseCrashlytics */; }; - 83978E26285C596F0076ED21 /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = 83978E25285C596F0076ED21 /* FirebaseAnalytics */; }; - 83978E29285C5C0A0076ED21 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 83978E28285C5C0A0076ED21 /* GoogleService-Info.plist */; }; 83988F0E27BE0A5900A0E89A /* RedundantPlaylistDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 83988F0D27BE0A5900A0E89A /* RedundantPlaylistDataStore.m */; }; 8399D4E21805A55000B503B1 /* XmlContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8399D4E01805A55000B503B1 /* XmlContainer.m */; }; 839B837F286D7F8D00F529EE /* NumberHertzToStringTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 839B837E286D7F8D00F529EE /* NumberHertzToStringTransformer.swift */; }; @@ -182,6 +180,7 @@ 83BCB8DE17FC971300760340 /* FFMPEG.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = B09E94350D747F7B0064F138 /* FFMPEG.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 83D0380F24A40DFB004CF90F /* CogAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 83D0380E24A40DF2004CF90F /* CogAssets.xcassets */; }; 83F9D8071A884C54007ABEC2 /* SilenceDecoder.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83F9D7F61A884B46007ABEC2 /* SilenceDecoder.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 83F9FFEF2D6EB75B00026576 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 83F9FFEE2D6EB75B00026576 /* Sentry */; }; 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; @@ -196,7 +195,7 @@ 8E8D41C80CBB0DA900135C1B /* Pls.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8E8D41C70CBB0DA000135C1B /* Pls.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 8E9A30160BA792DC0091081B /* NSFileHandle+CreateFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E9A30140BA792DC0091081B /* NSFileHandle+CreateFile.m */; }; 99EAACA80DD1BB7A00423C38 /* APL.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 566D321B0D538550004466A5 /* APL.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - ED69CA3B25BE2A390090B90D /* Preferences.preferencePane in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17F5622E0C3BD8FB0019975C /* Preferences.preferencePane */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + ED69CA3B25BE2A390090B90D /* Preferences.preferencePane in Copy Files */ = {isa = PBXBuildFile; fileRef = 17F5622E0C3BD8FB0019975C /* Preferences.preferencePane */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; ED69CBC725BE32C00090B90D /* MASShortcut.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED69CBBF25BE328C0090B90D /* MASShortcut.framework */; }; ED69CBCA25BE32E80090B90D /* MASShortcut.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = ED69CBBF25BE328C0090B90D /* MASShortcut.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; EDAAA41F25A665C000731773 /* PositionSliderToolbarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDAAA41E25A665C000731773 /* PositionSliderToolbarItem.swift */; }; @@ -662,14 +661,15 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - 07DFC3930ECDF80100DA400D /* CopyFiles */ = { + 07DFC3930ECDF80100DA400D /* Copy Files */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 7; files = ( - ED69CA3B25BE2A390090B90D /* Preferences.preferencePane in CopyFiles */, + ED69CA3B25BE2A390090B90D /* Preferences.preferencePane in Copy Files */, ); + name = "Copy Files"; runOnlyForDeploymentPostprocessing = 0; }; 177FD1000B90CB570011C3B5 /* CopyFiles */ = { @@ -969,6 +969,8 @@ 8384912518080F2D00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = ""; }; 8384913D18083E4E00E7332D /* filetype.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = filetype.icns; sourceTree = ""; }; 83859520234FEB35004E9946 /* Cog.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Cog.entitlements; sourceTree = ""; }; + 838770F32D6F0996001455A0 /* FeedbackController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FeedbackController.h; sourceTree = ""; }; + 838770F42D6F0996001455A0 /* FeedbackController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FeedbackController.m; sourceTree = ""; }; 838A33732D06A9B100D0D770 /* librubberband.3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = librubberband.3.dylib; path = ThirdParty/rubberband/lib/librubberband.3.dylib; sourceTree = ""; }; 838A33792D06C14200D0D770 /* PitchSlider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PitchSlider.h; path = Window/PitchSlider.h; sourceTree = ""; }; 838A337A2D06C14200D0D770 /* PitchSlider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = PitchSlider.m; path = Window/PitchSlider.m; sourceTree = ""; }; @@ -992,7 +994,6 @@ 839614AC286EDA5C00D3EEDB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/SpectrumWindow.xib; sourceTree = ""; }; 839614AF286EDA6800D3EEDB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/SpectrumWindow.strings; sourceTree = ""; }; 839614B1286EDA6E00D3EEDB /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/SpectrumWindow.strings; sourceTree = ""; }; - 83978E28285C5C0A0076ED21 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 83988F0C27BE0A5900A0E89A /* RedundantPlaylistDataStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RedundantPlaylistDataStore.h; sourceTree = ""; }; 83988F0D27BE0A5900A0E89A /* RedundantPlaylistDataStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RedundantPlaylistDataStore.m; sourceTree = ""; }; 8399D4E01805A55000B503B1 /* XmlContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XmlContainer.m; sourceTree = ""; }; @@ -1081,11 +1082,10 @@ 835FAC7E27BCDF5B00BA8562 /* libaom.a in Frameworks */, 837DC92B285B05710005C58A /* CoreData.framework in Frameworks */, 838A33742D06A9B100D0D770 /* librubberband.3.dylib in Frameworks */, - 83978E26285C596F0076ED21 /* FirebaseAnalytics in Frameworks */, 17BB5CF90B8A86350009ACB1 /* AudioUnit.framework in Frameworks */, 17BB5CFA0B8A86350009ACB1 /* CoreAudio.framework in Frameworks */, + 83F9FFEF2D6EB75B00026576 /* Sentry in Frameworks */, 17BB5CFB0B8A86350009ACB1 /* CoreAudioKit.framework in Frameworks */, - 83978E16285C58190076ED21 /* FirebaseCrashlytics in Frameworks */, 17BB5EA60B8A87850009ACB1 /* IOKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1105,6 +1105,7 @@ 080E96DDFE201D6D7F000001 /* Classes */ = { isa = PBXGroup; children = ( + 838770F72D6F0996001455A0 /* Feedback */, 835F00B3279BD1CD00055FCF /* Formatters */, 177042960B8BC53600B86321 /* Application */, 17E0D5D20F520E75005B6FED /* Window */, @@ -1462,7 +1463,6 @@ isa = PBXGroup; children = ( 0A1B412E286F6301008A6A44 /* Localizable.stringsdict */, - 83978E28285C5C0A0076ED21 /* GoogleService-Info.plist */, 83D0380E24A40DF2004CF90F /* CogAssets.xcassets */, 83859520234FEB35004E9946 /* Cog.entitlements */, 080E96DDFE201D6D7F000001 /* Classes */, @@ -1798,6 +1798,15 @@ name = Visualization; sourceTree = ""; }; + 838770F72D6F0996001455A0 /* Feedback */ = { + isa = PBXGroup; + children = ( + 838770F32D6F0996001455A0 /* FeedbackController.h */, + 838770F42D6F0996001455A0 /* FeedbackController.m */, + ); + path = Feedback; + sourceTree = ""; + }; 83B0669D180D5668008E3612 /* Products */ = { isa = PBXGroup; children = ( @@ -1969,8 +1978,8 @@ 8D1107290486CEB800E47090 /* Resources */, 8E757AEC09F3265E0080F1EE /* CopyFiles */, 177FD1000B90CB570011C3B5 /* CopyFiles */, - 07DFC3930ECDF80100DA400D /* CopyFiles */, - 83978E27285C5A4C0076ED21 /* Run Crashlytics symbol upload */, + 07DFC3930ECDF80100DA400D /* Copy Files */, + 83978E27285C5A4C0076ED21 /* Run Sentry symbol upload */, ); buildRules = ( ); @@ -2009,8 +2018,7 @@ ); name = Cog; packageProductDependencies = ( - 83978E15285C58190076ED21 /* FirebaseCrashlytics */, - 83978E25285C596F0076ED21 /* FirebaseAnalytics */, + 83F9FFEE2D6EB75B00026576 /* Sentry */, ); productInstallPath = /Applications; productName = Cog; @@ -2054,7 +2062,7 @@ ); mainGroup = 29B97314FDCFA39411CA2CEA /* Cog */; packageReferences = ( - 83978E14285C58190076ED21 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + 83F9FFED2D6EB75B00026576 /* XCRemoteSwiftPackageReference "sentry-cocoa" */, ); projectDirPath = ""; projectReferences = ( @@ -2477,7 +2485,6 @@ 832923AF279FAC400048201E /* Cog.q1.json in Resources */, 17D1B2820CF8B2830028F5B5 /* vg.icns in Resources */, 17D1B2830CF8B2830028F5B5 /* xm.icns in Resources */, - 83978E29285C5C0A0076ED21 /* GoogleService-Info.plist in Resources */, 836DF61E298F7F6E00CD0580 /* Scenes.scnassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2504,7 +2511,7 @@ shellPath = /bin/sh; shellScript = "$SCRIPT_INPUT_FILE_0 $SCRIPT_INPUT_FILE_1 $SCRIPT_OUTPUT_FILE_0\n"; }; - 83978E27285C5A4C0076ED21 /* Run Crashlytics symbol upload */ = { + 83978E27285C5A4C0076ED21 /* Run Sentry symbol upload */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; buildActionMask = 12; @@ -2516,14 +2523,14 @@ "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}", "$(SRCROOT)/$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", ); - name = "Run Crashlytics symbol upload"; + name = "Run Sentry symbol upload"; outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [[ \"${CONFIGURATION}\" = \"Release\" ]] || [[ \"${CONFIGURATION}\" = \"Adhoc\" ]]; then\n echo \"Uploading dSYMs...\"\n find \"${DWARF_DSYM_FOLDER_PATH}\" -name \"*.dSYM\" -print0 | xargs -0 -I \\{\\} \"${BUILD_DIR%/Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/upload-symbols\" -gsp \"${SRCROOT}/GoogleService-Info.plist\" -p mac \\{\\}\nelse\n echo \"Skip dSYMs upload\"\nfi\n\nexit 0\n"; + shellScript = "if [[ \"x${SENTRY_URL}\" != \"x\" ]] && [[ \"x${SENTRY_PROJECT}\" != \"x\" ]] && [[ \"x${SENTRY_AUTH_TOKEN}\" != \"x\" ]]; then\n if [[ \"${CONFIGURATION}\" = \"Release\" ]] || [[ \"${CONFIGURATION}\" = \"Adhoc\" ]]; then\n echo \"Uploading dSYMs...\"\n export PATH=$PATH:/usr/local/bin:/opt/homebrew/bin\n sentry-cli --url \"${SENTRY_URL}\" debug-files upload -p \"${SENTRY_PROJECT}\" --auth-token \"${SENTRY_AUTH_TOKEN}\" --include-sources \"${DWARF_DSYM_FOLDER_PATH}\"\n else\n echo \"Skip dSYMs upload\"\n fi\nelse\n echo \"Sentry settings not configured\"\nfi\n\nexit 0\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -2552,6 +2559,7 @@ 1755E1F90BA0D2B600CA3560 /* PlaylistLoader.m in Sources */, 8E9A30160BA792DC0091081B /* NSFileHandle+CreateFile.m in Sources */, 179790E10C087AB7001D6996 /* OpenURLPanel.m in Sources */, + 838770FA2D6F0996001455A0 /* FeedbackController.m in Sources */, 837DC931285B3F790005C58A /* DataModel.xcdatamodeld in Sources */, 835FAC7927BCDF2A00BA8562 /* AVIFDecoder.m in Sources */, EDAAA41F25A665C000731773 /* PositionSliderToolbarItem.swift in Sources */, @@ -3157,26 +3165,21 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 83978E14285C58190076ED21 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + 83F9FFED2D6EB75B00026576 /* XCRemoteSwiftPackageReference "sentry-cocoa" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + repositoryURL = "https://github.com/getsentry/sentry-cocoa.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 10.0.0; + minimumVersion = 8.45.0; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 83978E15285C58190076ED21 /* FirebaseCrashlytics */ = { + 83F9FFEE2D6EB75B00026576 /* Sentry */ = { isa = XCSwiftPackageProductDependency; - package = 83978E14285C58190076ED21 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; - productName = FirebaseCrashlytics; - }; - 83978E25285C596F0076ED21 /* FirebaseAnalytics */ = { - isa = XCSwiftPackageProductDependency; - package = 83978E14285C58190076ED21 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; - productName = FirebaseAnalytics; + package = 83F9FFED2D6EB75B00026576 /* XCRemoteSwiftPackageReference "sentry-cocoa" */; + productName = Sentry; }; /* End XCSwiftPackageProductDependency section */ diff --git a/Cog.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Cog.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index d5ec6b911..129e24f4b 100644 --- a/Cog.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Cog.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,123 +1,15 @@ { - "originHash" : "c63c63846d9c539229e96de38d6af51417e28c0ee9a0bc48bd0f0f19d923c329", + "originHash" : "7b5e54f81ac1ebaa640945691cb38c371b637198701f04fba811702fc8e7067e", "pins" : [ { - "identity" : "abseil-cpp-binary", + "identity" : "sentry-cocoa", "kind" : "remoteSourceControl", - "location" : "https://github.com/google/abseil-cpp-binary.git", + "location" : "https://github.com/getsentry/sentry-cocoa.git", "state" : { - "revision" : "194a6706acbd25e4ef639bcaddea16e8758a3e27", - "version" : "1.2024011602.0" - } - }, - { - "identity" : "app-check", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/app-check.git", - "state" : { - "revision" : "3b62f154d00019ae29a71e9738800bb6f18b236d", - "version" : "10.19.2" - } - }, - { - "identity" : "firebase-ios-sdk", - "kind" : "remoteSourceControl", - "location" : "https://github.com/firebase/firebase-ios-sdk", - "state" : { - "revision" : "eca84fd638116dd6adb633b5a3f31cc7befcbb7d", - "version" : "10.29.0" - } - }, - { - "identity" : "googleappmeasurement", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleAppMeasurement.git", - "state" : { - "revision" : "fe727587518729046fc1465625b9afd80b5ab361", - "version" : "10.28.0" - } - }, - { - "identity" : "googledatatransport", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleDataTransport.git", - "state" : { - "revision" : "a637d318ae7ae246b02d7305121275bc75ed5565", - "version" : "9.4.0" - } - }, - { - "identity" : "googleutilities", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleUtilities.git", - "state" : { - "revision" : "57a1d307f42df690fdef2637f3e5b776da02aad6", - "version" : "7.13.3" - } - }, - { - "identity" : "grpc-binary", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/grpc-binary.git", - "state" : { - "revision" : "e9fad491d0673bdda7063a0341fb6b47a30c5359", - "version" : "1.62.2" - } - }, - { - "identity" : "gtm-session-fetcher", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/gtm-session-fetcher.git", - "state" : { - "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", - "version" : "3.5.0" - } - }, - { - "identity" : "interop-ios-for-google-sdks", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/interop-ios-for-google-sdks.git", - "state" : { - "revision" : "2d12673670417654f08f5f90fdd62926dc3a2648", - "version" : "100.0.0" - } - }, - { - "identity" : "leveldb", - "kind" : "remoteSourceControl", - "location" : "https://github.com/firebase/leveldb.git", - "state" : { - "revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1", - "version" : "1.22.5" - } - }, - { - "identity" : "nanopb", - "kind" : "remoteSourceControl", - "location" : "https://github.com/firebase/nanopb.git", - "state" : { - "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", - "version" : "2.30910.0" - } - }, - { - "identity" : "promises", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/promises.git", - "state" : { - "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", - "version" : "2.4.0" - } - }, - { - "identity" : "swift-protobuf", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-protobuf.git", - "state" : { - "revision" : "e17d61f26df0f0e06f58f6977ba05a097a720106", - "version" : "1.27.1" + "revision" : "3640aaf7849a9fc39996d777e6df866251bb98d6", + "version" : "8.45.0" } } ], - "version" : 2 + "version" : 3 } diff --git a/Feedback/FeedbackController.h b/Feedback/FeedbackController.h index b9d7a3b13..68e16f59e 100644 --- a/Feedback/FeedbackController.h +++ b/Feedback/FeedbackController.h @@ -10,15 +10,18 @@ #import @interface FeedbackController : NSWindowController { - IBOutlet NSTextField* fromView; - IBOutlet NSTextField* subjectView; + IBOutlet NSTextField* nameView; + IBOutlet NSTextField* emailView; IBOutlet NSTextView* messageView; - IBOutlet NSProgressIndicator* sendingIndicator; - - FeedbackSocket* feedbackSocket; } - (IBAction)sendFeedback:(id)sender; - (IBAction)cancel:(id)sender; +- (BOOL)waitForCompletion; + +- (NSString *)name; +- (NSString *)email; +- (NSString *)comments; + @end diff --git a/Feedback/FeedbackController.m b/Feedback/FeedbackController.m index 4a13a3e7c..e0960a381 100644 --- a/Feedback/FeedbackController.m +++ b/Feedback/FeedbackController.m @@ -10,62 +10,67 @@ #import "Logging.h" -@implementation FeedbackController +@implementation FeedbackController { + BOOL showing; + BOOL sent; + + NSString *name; + NSString *email; + NSString *comments; +} - (id)init { - return [super initWithWindowNibName:@"Feedback"]; + self = [super initWithWindowNibName:@"Feedback"]; + if(self) { + showing = NO; + sent = NO; + } + return self; } - (IBAction)showWindow:(id)sender { - [fromView setStringValue:@""]; - [subjectView setStringValue:@""]; + [nameView setStringValue:@""]; + [emailView setStringValue:@""]; [messageView setString:@""]; [super showWindow:sender]; -} - -- (void)alertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo { - if([(NSNumber *)CFBridgingRelease(contextInfo) boolValue] == YES) { - [[self window] close]; - } -} - -- (void)feedbackDidNotSend:(FeedbackSocket *)feedback { - ALog(@"Error sending feedback"); - - [sendingIndicator stopAnimation:self]; - - NSAlert *alert = [[NSAlert alloc] init]; - [alert setMessageText:NSLocalizedString(@"FeedbackFailedMessageText", @"")]; - [alert setInformativeText:NSLocalizedString(@"FeedbackFailedInformativeText", @"")]; - - [alert beginSheetModalForWindow:[self window] modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:(void *)CFBridgingRetain([NSNumber numberWithBool:NO])]; -} - -- (void)feedbackDidSend:(FeedbackSocket *)feedback { - [sendingIndicator stopAnimation:self]; - - NSAlert *alert = [[NSAlert alloc] init]; - [alert setMessageText:NSLocalizedString(@"FeedbackSuccessMessageText", @"")]; - [alert setInformativeText:NSLocalizedString(@"FeedbackSuccessInformativeText", @"")]; - - [alert beginSheetModalForWindow:[self window] modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:(void *)CFBridgingRetain([NSNumber numberWithBool:YES])]; + + showing = YES; } - (IBAction)sendFeedback:(id)sender { - [sendingIndicator startAnimation:self]; + name = [nameView stringValue]; + email = [emailView stringValue]; + comments = [messageView string]; - // Using this so that if its a bad connection, it doesnt sit there looking stupid..or should it - feedbackSocket = [[FeedbackSocket alloc] init]; - [feedbackSocket setDelegate:self]; - - NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]; - - [feedbackSocket sendFeedback:[fromView stringValue] subject:[subjectView stringValue] message:[messageView string] version:version]; + [[self window] close]; + sent = YES; + showing = NO; } - (IBAction)cancel:(id)sender { [[self window] close]; + sent = NO; + showing = NO; +} + +- (BOOL)waitForCompletion { + while(showing) { + usleep(2000); + } + return sent; +} + +- (NSString *)name { + return name; +} + +- (NSString *)email { + return email; +} + +- (NSString *)comments { + return comments; } @end diff --git a/Info.plist.template b/Info.plist.template index a9145e825..3d223fa0d 100644 --- a/Info.plist.template +++ b/Info.plist.template @@ -2,8 +2,6 @@ - FirebaseCrashlyticsCollectionEnabled - SUEnableInstallerLauncherService CFBundleDevelopmentRegion diff --git a/Playlist/PlaylistLoader.m b/Playlist/PlaylistLoader.m index 1945d2f90..20cbbbde9 100644 --- a/Playlist/PlaylistLoader.m +++ b/Playlist/PlaylistLoader.m @@ -41,8 +41,6 @@ #import "SandboxBroker.h" -@import Firebase; - extern NSMutableDictionary *kArtworkDictionary; @implementation PlaylistLoader @@ -748,7 +746,7 @@ NSURL *_Nullable urlForPath(NSString *_Nullable path); [op addExecutionBlock:^{ @autoreleasepool { DLog(@"Loading metadata for %@", url); - [[FIRCrashlytics crashlytics] logWithFormat:@"Loading metadata for %@", url]; + //[[FIRCrashlytics crashlytics] logWithFormat:@"Loading metadata for %@", url]; NSDictionary *entryProperties = [AudioPropertiesReader propertiesForURL:url]; if(entryProperties == nil) @@ -883,7 +881,7 @@ NSURL *_Nullable urlForPath(NSString *_Nullable path); PlaylistEntry *pe = [entries objectAtIndex:idx]; DLog(@"Loading metadata for %@", pe.url); - [[FIRCrashlytics crashlytics] logWithFormat:@"Loading metadata for %@", pe.url]; + //[[FIRCrashlytics crashlytics] logWithFormat:@"Loading metadata for %@", pe.url]; NSDictionary *entryProperties = [AudioPropertiesReader propertiesForURL:pe.url]; if(entryProperties == nil) diff --git a/Playlist/PlaylistView.m b/Playlist/PlaylistView.m index 5b080dabd..1a5fe6188 100644 --- a/Playlist/PlaylistView.m +++ b/Playlist/PlaylistView.m @@ -16,8 +16,6 @@ #import "Logging.h" -@import Firebase; - static NSString *playlistSavedColumnsID = @"Playlist Saved Columns v0"; @implementation PlaylistView @@ -121,7 +119,7 @@ static NSString *playlistSavedColumnsID = @"Playlist Saved Columns v0"; // Reset to defaults NSString *message = @"Reset playlist columns to default"; DLog(@"%@", message); - [[FIRCrashlytics crashlytics] logWithFormat:@"%@", message]; + //[[FIRCrashlytics crashlytics] logWithFormat:@"%@", message]; for(NSTableColumn *col in columns) { [self removeTableColumn:col]; } diff --git a/Preferences/Preferences/Base.lproj/Preferences.xib b/Preferences/Preferences/Base.lproj/Preferences.xib index fbbe9cac6..40fd8b483 100644 --- a/Preferences/Preferences/Base.lproj/Preferences.xib +++ b/Preferences/Preferences/Base.lproj/Preferences.xib @@ -443,7 +443,7 @@ - + diff --git a/Window/MainWindow.m b/Window/MainWindow.m index 9ebea956c..f3bd68d70 100644 --- a/Window/MainWindow.m +++ b/Window/MainWindow.m @@ -16,26 +16,26 @@ // For instance, for the first option to get this treatment, we want time stretching to stay enabled // for existing installations, but disable itself by default on new installs, to spare processing. -void showCrashlyticsConsent(NSWindow *window) { - BOOL askedConsent = [[NSUserDefaults standardUserDefaults] boolForKey:@"crashlyticsAskedConsent"]; +void showSentryConsent(NSWindow *window) { + BOOL askedConsent = [[NSUserDefaults standardUserDefaults] boolForKey:@"sentryAskedConsent"]; if(!askedConsent) { [[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"rubberbandEngine": @"disabled" }]; [window orderFront:window]; NSAlert *alert = [[NSAlert alloc] init]; - [alert setMessageText:NSLocalizedString(@"CrashlyticsConsentTitle", @"")]; - [alert setInformativeText:NSLocalizedString(@"CrashlyticsConsentText", @"")]; + [alert setMessageText:NSLocalizedString(@"SentryConsentTitle", @"")]; + [alert setInformativeText:NSLocalizedString(@"SentryConsentText", @"")]; [alert addButtonWithTitle:NSLocalizedString(@"ConsentYes",@"")]; [alert addButtonWithTitle:NSLocalizedString(@"ConsentNo", @"")]; [alert beginSheetModalForWindow:window completionHandler:^(NSModalResponse returnCode) { if(returnCode == NSAlertFirstButtonReturn) { - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"crashlyticsConsented"]; + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"sentryConsented"]; } }]; - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"crashlyticsAskedConsent"]; + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"sentryAskedConsent"]; } else { [[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"rubberbandEngine": @"faster" }]; } @@ -62,7 +62,7 @@ void showCrashlyticsConsent(NSWindow *window) { [self showHDCDLogo:NO]; if(![[NSUserDefaults standardUserDefaults] boolForKey:@"miniMode"]) { - showCrashlyticsConsent(self); + showSentryConsent(self); } } diff --git a/Window/MiniWindow.m b/Window/MiniWindow.m index d596e91f3..5f2682014 100644 --- a/Window/MiniWindow.m +++ b/Window/MiniWindow.m @@ -12,7 +12,7 @@ extern NSString *iTunesDropType; -extern void showCrashlyticsConsent(NSWindow *window); +extern void showSentryConsent(NSWindow *window); @implementation MiniWindow @@ -44,7 +44,7 @@ extern void showCrashlyticsConsent(NSWindow *window); [self registerForDraggedTypes:@[NSPasteboardTypeFileURL, iTunesDropType]]; if([[NSUserDefaults standardUserDefaults] boolForKey:@"miniMode"]) { - showCrashlyticsConsent(self); + showSentryConsent(self); } } diff --git a/Xcode-config/Shared.xcconfig b/Xcode-config/Shared.xcconfig index d0704be39..a0420fed7 100644 --- a/Xcode-config/Shared.xcconfig +++ b/Xcode-config/Shared.xcconfig @@ -47,3 +47,8 @@ // CODE_SIGN_IDENTITY = …; // CODE_SIGN_STYLE = Manual/Automatic; // Please make sure not to commit them to source control. + +#include? "SENTRY_SETTINGS.xcconfig" +// Also, create this file if you wish to upload symbols to your server on build. +// It should define SENTRY_URL, SENTRY_PROJECT to the ID number, and finally +// SENTRY_AUTH_TOKEN to the developer authorization token for your instance. diff --git a/en.lproj/Feedback.strings b/en.lproj/Feedback.strings index 7849c8d7e..030fefb69 100644 --- a/en.lproj/Feedback.strings +++ b/en.lproj/Feedback.strings @@ -1,15 +1,15 @@ -/* Class = "NSWindow"; title = "Send Feedback"; ObjectID = "1"; */ -"1.title" = "Send Feedback"; +/* Class = "NSWindow"; title = "Send Crash Feedback"; ObjectID = "1"; */ +"1.title" = "Send Crash Feedback"; /* Class = "NSButtonCell"; title = "Send"; ObjectID = "17"; */ "17.title" = "Send"; -/* Class = "NSTextFieldCell"; title = "Subject:"; ObjectID = "18"; */ -"18.title" = "Subject:"; +/* Class = "NSTextFieldCell"; title = "Name:"; ObjectID = "18"; */ +"18.title" = "Name:"; -/* Class = "NSTextFieldCell"; title = "Message:"; ObjectID = "19"; */ -"19.title" = "Message:"; +/* Class = "NSTextFieldCell"; title = "Describe what you were doing:"; ObjectID = "19"; */ +"19.title" = "Describe what you were doing:"; /* Class = "NSButtonCell"; title = "Cancel"; ObjectID = "20"; */ "20.title" = "Cancel"; diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index 8dd94b207..dd7e04acb 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -2,8 +2,8 @@ "CogTitle" = "Cog"; "ConsentNo" = "No"; "ConsentYes" = "Yes"; -"CrashlyticsConsentText" = "Would you like to allow Crashlytics to submit crash reports? You may turn this off again in Preferences. We won't ask you again."; -"CrashlyticsConsentTitle" = "Crashlytics crash collection"; +"SentryConsentText" = "Would you like to allow Sentry to submit crash reports? You may turn this off again in Preferences. We won't ask you again."; +"SentryConsentTitle" = "Sentry crash collection"; "ErrorInvalidTrackId" = "Invalid track ID sent to SQLite request."; "ErrorMessageBadFile" = "Unable to parse metadata for bad file."; "ErrorMetadata" = "Unable to retrieve metadata."; diff --git a/es.lproj/Feedback.strings b/es.lproj/Feedback.strings index 0cbf727e2..acc191b1b 100644 --- a/es.lproj/Feedback.strings +++ b/es.lproj/Feedback.strings @@ -1,18 +1,18 @@ -/* Class = "NSWindow"; title = "Send Feedback"; ObjectID = "1"; */ -"1.title" = "Enviar comentarios"; +/* Class = "NSWindow"; title = "Send Crash Feedback"; ObjectID = "1"; */ +"1.title" = "Enviar comentarios por fallo"; /* Class = "NSButtonCell"; title = "Send"; ObjectID = "17"; */ "17.title" = "Enviar"; -/* Class = "NSTextFieldCell"; title = "Subject:"; ObjectID = "18"; */ -"18.title" = "Asunto:"; +/* Class = "NSTextFieldCell"; title = "Name:"; ObjectID = "18"; */ +"18.title" = "Nombre:"; -/* Class = "NSTextFieldCell"; title = "Message:"; ObjectID = "19"; */ -"19.title" = "Mensaje:"; +/* Class = "NSTextFieldCell"; title = "Describe what you were doing:"; ObjectID = "19"; */ +"19.title" = "Describe lo que estabas haciendo:"; /* Class = "NSButtonCell"; title = "Cancel"; ObjectID = "20"; */ "20.title" = "Cancelar"; /* Class = "NSTextFieldCell"; title = "Email:"; ObjectID = "22"; */ -"22.title" = "Correo electrónico:"; +"22.title" = "Email:"; diff --git a/es.lproj/Localizable.strings b/es.lproj/Localizable.strings index 1f0584e36..2f08d9cff 100644 --- a/es.lproj/Localizable.strings +++ b/es.lproj/Localizable.strings @@ -2,8 +2,8 @@ "CogTitle" = "Cog"; "ConsentNo" = "No"; "ConsentYes" = "Sí"; -"CrashlyticsConsentText" = "¿Te gustaría permitir que Crashlytics envíe informes de fallos? Puedes volver a desactivarlo en Preferencias. No te volveremos a preguntar."; -"CrashlyticsConsentTitle" = "Colección de fallos de Crashlytics"; +"SentryConsentText" = "¿Te gustaría permitir que Sentry envíe informes de fallos? Puedes volver a desactivarlo en Preferencias. No te volveremos a preguntar."; +"CrashlyticsConsentTitle" = "Colección de fallos de Sentry"; "ErrorInvalidTrackId" = "ID de pista inválida enviada a la solicitud de SQLite."; "ErrorMessageBadFile" = "No se han podido analizar los metadatos de un archivo defectuoso."; "ErrorMetadata" = "No se pueden recuperar los metadatos.";