Compare commits

...

3 Commits

27 changed files with 250 additions and 175 deletions

View File

@ -1,5 +1,20 @@
# Changelog # Changelog
## 2022.1 (24)
Features/Improvements:
- Local only posts (Glitch/Hometown)
- Show indicator for local only posts
- Add local only option to Compose screen
- Add extend selection button to asset picker when the limited library was used
- Improve profile directory UI
- Improve scrolling performance with large attachments on older devices
Bugfixes:
- Fix crash when closing Preferences
- Fix crash when posting attachment fails
- Fix scrolling through compose autocomplete suggestions dismissing keyboard
- Only show Mute action on your own posts
## 2021.1 (23) ## 2021.1 (23)
Features/Improvements: Features/Improvements:
- Synchronize GIF playback through animations and in gallery - Synchronize GIF playback through animations and in gallery

View File

@ -20,8 +20,9 @@ public final class Account: AccountProtocol, Decodable {
public let statusesCount: Int public let statusesCount: Int
public let note: String public let note: String
public let url: URL public let url: URL
public let avatar: URL // required on mastodon, but optional on gotosocial
public let avatarStatic: URL public let avatar: URL?
public let avatarStatic: URL?
public let header: URL? public let header: URL?
public let headerStatic: URL? public let headerStatic: URL?
public private(set) var emojis: [Emoji] public private(set) var emojis: [Emoji]
@ -44,8 +45,8 @@ public final class Account: AccountProtocol, Decodable {
self.statusesCount = try container.decode(Int.self, forKey: .statusesCount) self.statusesCount = try container.decode(Int.self, forKey: .statusesCount)
self.note = try container.decode(String.self, forKey: .note) self.note = try container.decode(String.self, forKey: .note)
self.url = try container.decode(URL.self, forKey: .url) self.url = try container.decode(URL.self, forKey: .url)
self.avatar = try container.decode(URL.self, forKey: .avatar) self.avatar = try? container.decode(URL.self, forKey: .avatar)
self.avatarStatic = try container.decode(URL.self, forKey: .avatarStatic) self.avatarStatic = try? container.decode(URL.self, forKey: .avatarStatic)
self.header = try? container.decode(URL.self, forKey: .header) self.header = try? container.decode(URL.self, forKey: .header)
self.headerStatic = try? container.decode(URL.self, forKey: .headerStatic) self.headerStatic = try? container.decode(URL.self, forKey: .headerStatic)
self.emojis = try container.decode([Emoji].self, forKey: .emojis) self.emojis = try container.decode([Emoji].self, forKey: .emojis)

View File

@ -56,6 +56,23 @@ extension Attachment {
case gifv case gifv
case audio case audio
case unknown case unknown
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
switch try container.decode(String.self) {
// gotosocial uses "gif" for gif images
case "image", "gif":
self = .image
case "video":
self = .video
case "gifv":
self = .gifv
case "audio":
self = .audio
default:
self = .unknown
}
}
} }
} }

View File

@ -22,7 +22,7 @@ public protocol AccountProtocol {
var statusesCount: Int { get } var statusesCount: Int { get }
var note: String { get } var note: String { get }
var url: URL { get } var url: URL { get }
var avatar: URL { get } var avatar: URL? { get }
var header: URL? { get } var header: URL? { get }
var moved: Bool? { get } var moved: Bool? { get }
var bot: Bool? { get } var bot: Bool? { get }

View File

@ -3,7 +3,7 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 52; objectVersion = 54;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
@ -126,7 +126,6 @@
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Helpers.swift */; }; D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Helpers.swift */; };
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */; }; D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */; };
D63569E023908A8D003DD353 /* StatusState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60A4FFB238B726A008AC647 /* StatusState.swift */; }; D63569E023908A8D003DD353 /* StatusState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60A4FFB238B726A008AC647 /* StatusState.swift */; };
D635ED5027ACD9260003635B /* WebURL in Frameworks */ = {isa = PBXBuildFile; productRef = D635ED4F27ACD9260003635B /* WebURL */; };
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */; }; D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */; };
D6370B9C24421FF30092A7FF /* Tusker.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = D6370B9A24421FF30092A7FF /* Tusker.xcdatamodeld */; }; D6370B9C24421FF30092A7FF /* Tusker.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = D6370B9A24421FF30092A7FF /* Tusker.xcdatamodeld */; };
D63A8D0B2561C27F00D9DFFF /* ProfileStatusesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63A8D0A2561C27F00D9DFFF /* ProfileStatusesViewController.swift */; }; D63A8D0B2561C27F00D9DFFF /* ProfileStatusesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63A8D0A2561C27F00D9DFFF /* ProfileStatusesViewController.swift */; };
@ -178,7 +177,6 @@
D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D663626B21361C6700C9CBA2 /* Account+Preferences.swift */; }; D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D663626B21361C6700C9CBA2 /* Account+Preferences.swift */; };
D66362752137068A00C9CBA2 /* Visibility+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D66362742137068A00C9CBA2 /* Visibility+Helpers.swift */; }; D66362752137068A00C9CBA2 /* Visibility+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D66362742137068A00C9CBA2 /* Visibility+Helpers.swift */; };
D6674AEA23341F7600E8DF94 /* AppShortcutItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6674AE923341F7600E8DF94 /* AppShortcutItems.swift */; }; D6674AEA23341F7600E8DF94 /* AppShortcutItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6674AE923341F7600E8DF94 /* AppShortcutItems.swift */; };
D6676CA327A8D0020052936B /* WebURL in Frameworks */ = {isa = PBXBuildFile; productRef = D6676CA227A8D0020052936B /* WebURL */; };
D6676CA527A8D0020052936B /* WebURLFoundationExtras in Frameworks */ = {isa = PBXBuildFile; productRef = D6676CA427A8D0020052936B /* WebURLFoundationExtras */; }; D6676CA527A8D0020052936B /* WebURLFoundationExtras in Frameworks */ = {isa = PBXBuildFile; productRef = D6676CA427A8D0020052936B /* WebURLFoundationExtras */; };
D667E5E12134937B0057A976 /* TimelineStatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D667E5E02134937B0057A976 /* TimelineStatusTableViewCell.xib */; }; D667E5E12134937B0057A976 /* TimelineStatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D667E5E02134937B0057A976 /* TimelineStatusTableViewCell.xib */; };
D667E5F12134D5050057A976 /* UIViewController+Delegates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F02134D5050057A976 /* UIViewController+Delegates.swift */; }; D667E5F12134D5050057A976 /* UIViewController+Delegates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F02134D5050057A976 /* UIViewController+Delegates.swift */; };
@ -341,6 +339,7 @@
D6EE63FB2551F7F60065485C /* StatusCollapseButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6EE63FA2551F7F60065485C /* StatusCollapseButton.swift */; }; D6EE63FB2551F7F60065485C /* StatusCollapseButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6EE63FA2551F7F60065485C /* StatusCollapseButton.swift */; };
D6F0B12B24A3071C001E48C3 /* MainSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F0B12A24A3071C001E48C3 /* MainSplitViewController.swift */; }; D6F0B12B24A3071C001E48C3 /* MainSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F0B12A24A3071C001E48C3 /* MainSplitViewController.swift */; };
D6F0B17524A3A1AA001E48C3 /* MainSidebarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F0B17424A3A1AA001E48C3 /* MainSidebarViewController.swift */; }; D6F0B17524A3A1AA001E48C3 /* MainSidebarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F0B17424A3A1AA001E48C3 /* MainSidebarViewController.swift */; };
D6F1F9DF27B0613300CB7D88 /* WebURL in Frameworks */ = {isa = PBXBuildFile; productRef = D6F1F9DE27B0613300CB7D88 /* WebURL */; settings = {ATTRIBUTES = (Required, ); }; };
D6F2E965249E8BFD005846BB /* CrashReporterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F2E963249E8BFD005846BB /* CrashReporterViewController.swift */; }; D6F2E965249E8BFD005846BB /* CrashReporterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F2E963249E8BFD005846BB /* CrashReporterViewController.swift */; };
D6F2E966249E8BFD005846BB /* CrashReporterViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6F2E964249E8BFD005846BB /* CrashReporterViewController.xib */; }; D6F2E966249E8BFD005846BB /* CrashReporterViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6F2E964249E8BFD005846BB /* CrashReporterViewController.xib */; };
D6F953F021251A2900CF0F2B /* MastodonController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F953EF21251A2900CF0F2B /* MastodonController.swift */; }; D6F953F021251A2900CF0F2B /* MastodonController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F953EF21251A2900CF0F2B /* MastodonController.swift */; };
@ -769,7 +768,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
D635ED5027ACD9260003635B /* WebURL in Frameworks */, D6F1F9DF27B0613300CB7D88 /* WebURL in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -785,7 +784,6 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
D6676CA327A8D0020052936B /* WebURL in Frameworks */,
D61099C02144B0CC00432DC2 /* Pachyderm.framework in Frameworks */, D61099C02144B0CC00432DC2 /* Pachyderm.framework in Frameworks */,
D6B0539F23BD2BA300A066FA /* SheetController in Frameworks */, D6B0539F23BD2BA300A066FA /* SheetController in Frameworks */,
D69CCBBF249E6EFD000AF167 /* CrashReporter in Frameworks */, D69CCBBF249E6EFD000AF167 /* CrashReporter in Frameworks */,
@ -1727,7 +1725,7 @@
); );
name = Pachyderm; name = Pachyderm;
packageProductDependencies = ( packageProductDependencies = (
D635ED4F27ACD9260003635B /* WebURL */, D6F1F9DE27B0613300CB7D88 /* WebURL */,
); );
productName = Pachyderm; productName = Pachyderm;
productReference = D61099AB2144B0CC00432DC2 /* Pachyderm.framework */; productReference = D61099AB2144B0CC00432DC2 /* Pachyderm.framework */;
@ -1762,6 +1760,7 @@
D6F953E52125197500CF0F2B /* Embed Frameworks */, D6F953E52125197500CF0F2B /* Embed Frameworks */,
D65F612C23AE957600F3CFD3 /* Embed debug-only frameworks */, D65F612C23AE957600F3CFD3 /* Embed debug-only frameworks */,
D6E3438F2659849800C4AA01 /* Embed App Extensions */, D6E3438F2659849800C4AA01 /* Embed App Extensions */,
D6F1F9E127B0677000CB7D88 /* ShellScript */,
); );
buildRules = ( buildRules = (
); );
@ -1774,7 +1773,6 @@
D6B0539E23BD2BA300A066FA /* SheetController */, D6B0539E23BD2BA300A066FA /* SheetController */,
D69CCBBE249E6EFD000AF167 /* CrashReporter */, D69CCBBE249E6EFD000AF167 /* CrashReporter */,
D60CFFDA24A290BA00D00083 /* SwiftSoup */, D60CFFDA24A290BA00D00083 /* SwiftSoup */,
D6676CA227A8D0020052936B /* WebURL */,
D6676CA427A8D0020052936B /* WebURLFoundationExtras */, D6676CA427A8D0020052936B /* WebURLFoundationExtras */,
); );
productName = Tusker; productName = Tusker;
@ -2008,6 +2006,24 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "#if [ \"${CONFIGURATION}\" == \"Debug\" ]; then\n# echo \"Embedding ${SCRIPT_INPUT_FILE_0}\"\n# cp -R $SCRIPT_INPUT_FILE_0 $SCRIPT_OUTPUT_FILE_0\n# codesign --force --verbose --sign $EXPANDED_CODE_SIGN_IDENTITY $SCRIPT_OUTPUT_FILE_0\n# \n# echo \"Embedding ${SCRIPT_INPUT_FILE_1}\"\n# cp -R $SCRIPT_INPUT_FILE_1 $SCRIPT_OUTPUT_FILE_1\n# codesign --force --verbose --sign $EXPANDED_CODE_SIGN_IDENTITY $SCRIPT_OUTPUT_FILE_1\n#else\n# echo \"Skipping embedding debug frameworks\"\n#fi\n"; shellScript = "#if [ \"${CONFIGURATION}\" == \"Debug\" ]; then\n# echo \"Embedding ${SCRIPT_INPUT_FILE_0}\"\n# cp -R $SCRIPT_INPUT_FILE_0 $SCRIPT_OUTPUT_FILE_0\n# codesign --force --verbose --sign $EXPANDED_CODE_SIGN_IDENTITY $SCRIPT_OUTPUT_FILE_0\n# \n# echo \"Embedding ${SCRIPT_INPUT_FILE_1}\"\n# cp -R $SCRIPT_INPUT_FILE_1 $SCRIPT_OUTPUT_FILE_1\n# codesign --force --verbose --sign $EXPANDED_CODE_SIGN_IDENTITY $SCRIPT_OUTPUT_FILE_1\n#else\n# echo \"Skipping embedding debug frameworks\"\n#fi\n";
}; };
D6F1F9E127B0677000CB7D88 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n\n# the nested framework doens't get signed automatically for some reason\n# so we sign it ourselves\n#codesign --force --verbose --sign $EXPANDED_CODE_SIGN_IDENTITY \"$BUILT_PRODUCTS_DIR/Tusker.app/Frameworks/Pachyderm.framework/Frameworks/WebURL.framework\"\n\n# xcode wants to include the weburl framework twice for some reason, but the app store doesn't like nested frameworks\n# we already have a copy in the app's Frameworks/ dir, so we can delete the nested one\nif [ \"$(ls \"$BUILT_PRODUCTS_DIR/Tusker.app/Frameworks/Pachyderm.framework/Frameworks/\")\" -ne \"WebURL.framework\" ]; then\n exit 1\nfi\nrm -rf \"$BUILT_PRODUCTS_DIR/Tusker.app/Frameworks/Pachyderm.framework/Frameworks/\"\n";
};
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
@ -2414,11 +2430,11 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = ""; CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = HGYVAQA9FW; DEVELOPMENT_TEAM = V4WK9KR9U2;
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
@ -2430,6 +2446,8 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@loader_path/Frameworks", "@loader_path/Frameworks",
); );
OTHER_CODE_SIGN_FLAGS = "--deep";
OTHER_SWIFT_FLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.Pachyderm; PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.Pachyderm;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -2445,11 +2463,11 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = ""; CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = HGYVAQA9FW; DEVELOPMENT_TEAM = V4WK9KR9U2;
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
@ -2461,6 +2479,8 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@loader_path/Frameworks", "@loader_path/Frameworks",
); );
OTHER_CODE_SIGN_FLAGS = "--deep";
OTHER_SWIFT_FLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.Pachyderm; PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.Pachyderm;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -2641,7 +2661,7 @@
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements; CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 23; CURRENT_PROJECT_VERSION = 24;
DEVELOPMENT_TEAM = V4WK9KR9U2; DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = Tusker/Info.plist; INFOPLIST_FILE = Tusker/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.1; IPHONEOS_DEPLOYMENT_TARGET = 14.1;
@ -2650,7 +2670,8 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 2021.1; MARKETING_VERSION = 2022.1;
OTHER_CODE_SIGN_FLAGS = "";
OTHER_LDFLAGS = ""; OTHER_LDFLAGS = "";
"OTHER_SWIFT_FLAGS[sdk=iphone*14*]" = "$(inherited) -D SDK_IOS_14"; "OTHER_SWIFT_FLAGS[sdk=iphone*14*]" = "$(inherited) -D SDK_IOS_14";
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker; PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker;
@ -2671,7 +2692,7 @@
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements; CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 23; CURRENT_PROJECT_VERSION = 24;
DEVELOPMENT_TEAM = V4WK9KR9U2; DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = Tusker/Info.plist; INFOPLIST_FILE = Tusker/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.1; IPHONEOS_DEPLOYMENT_TARGET = 14.1;
@ -2680,7 +2701,8 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 2021.1; MARKETING_VERSION = 2022.1;
OTHER_CODE_SIGN_FLAGS = "";
"OTHER_SWIFT_FLAGS[sdk=iphone*14*]" = "$(inherited) -D SDK_IOS_14"; "OTHER_SWIFT_FLAGS[sdk=iphone*14*]" = "$(inherited) -D SDK_IOS_14";
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker; PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -2780,7 +2802,7 @@
CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements; CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 23; CURRENT_PROJECT_VERSION = 24;
DEVELOPMENT_TEAM = V4WK9KR9U2; DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = OpenInTusker/Info.plist; INFOPLIST_FILE = OpenInTusker/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.1; IPHONEOS_DEPLOYMENT_TARGET = 14.1;
@ -2790,7 +2812,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 2021.1; MARKETING_VERSION = 2022.1;
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.OpenInTusker; PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.OpenInTusker;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -2807,7 +2829,7 @@
CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements; CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 23; CURRENT_PROJECT_VERSION = 24;
DEVELOPMENT_TEAM = V4WK9KR9U2; DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = OpenInTusker/Info.plist; INFOPLIST_FILE = OpenInTusker/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.1; IPHONEOS_DEPLOYMENT_TARGET = 14.1;
@ -2817,7 +2839,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 2021.1; MARKETING_VERSION = 2022.1;
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.OpenInTusker; PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.OpenInTusker;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -2936,16 +2958,6 @@
package = D60CFFD924A290BA00D00083 /* XCRemoteSwiftPackageReference "SwiftSoup" */; package = D60CFFD924A290BA00D00083 /* XCRemoteSwiftPackageReference "SwiftSoup" */;
productName = SwiftSoup; productName = SwiftSoup;
}; };
D635ED4F27ACD9260003635B /* WebURL */ = {
isa = XCSwiftPackageProductDependency;
package = D6676CA127A8D0020052936B /* XCRemoteSwiftPackageReference "swift-url" */;
productName = WebURL;
};
D6676CA227A8D0020052936B /* WebURL */ = {
isa = XCSwiftPackageProductDependency;
package = D6676CA127A8D0020052936B /* XCRemoteSwiftPackageReference "swift-url" */;
productName = WebURL;
};
D6676CA427A8D0020052936B /* WebURLFoundationExtras */ = { D6676CA427A8D0020052936B /* WebURLFoundationExtras */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = D6676CA127A8D0020052936B /* XCRemoteSwiftPackageReference "swift-url" */; package = D6676CA127A8D0020052936B /* XCRemoteSwiftPackageReference "swift-url" */;
@ -2961,6 +2973,11 @@
package = D6B0539D23BD2BA300A066FA /* XCRemoteSwiftPackageReference "SheetController" */; package = D6B0539D23BD2BA300A066FA /* XCRemoteSwiftPackageReference "SheetController" */;
productName = SheetController; productName = SheetController;
}; };
D6F1F9DE27B0613300CB7D88 /* WebURL */ = {
isa = XCSwiftPackageProductDependency;
package = D6676CA127A8D0020052936B /* XCRemoteSwiftPackageReference "swift-url" */;
productName = WebURL;
};
/* End XCSwiftPackageProductDependency section */ /* End XCSwiftPackageProductDependency section */
/* Begin XCVersionGroup section */ /* Begin XCVersionGroup section */

View File

@ -33,7 +33,7 @@
"repositoryURL": "https://github.com/karwa/swift-url", "repositoryURL": "https://github.com/karwa/swift-url",
"state": { "state": {
"branch": "main", "branch": "main",
"revision": "1519936d9813af86e57c06dc7f727cb281de9f16", "revision": "9d06f9f89397de16c8942aa123c425568654fd6a",
"version": null "version": null
} }
}, },

View File

@ -29,8 +29,9 @@ class AccountActivityItemSource: NSObject, UIActivityItemSource {
metadata.originalURL = account.url metadata.originalURL = account.url
metadata.url = account.url metadata.url = account.url
metadata.title = "\(account.displayName) (@\(account.username)@\(account.url.host!)" metadata.title = "\(account.displayName) (@\(account.username)@\(account.url.host!)"
if let data = ImageCache.avatars.getData(account.avatar), if let avatar = account.avatar,
let image = UIImage(data: data) { let data = ImageCache.avatars.getData(avatar),
let image = UIImage(data: data) {
metadata.iconProvider = NSItemProvider(object: image) metadata.iconProvider = NSItemProvider(object: image)
} }
return metadata return metadata

View File

@ -32,8 +32,9 @@ class StatusActivityItemSource: NSObject, UIActivityItemSource {
let doc = try! SwiftSoup.parse(status.content) let doc = try! SwiftSoup.parse(status.content)
let content = try! doc.text() let content = try! doc.text()
metadata.title = "\(status.account.displayName): \"\(content)\"" metadata.title = "\(status.account.displayName): \"\(content)\""
if let data = ImageCache.avatars.getData(status.account.avatar), if let avatar = status.account.avatar,
let image = UIImage(data: data) { let data = ImageCache.avatars.getData(avatar),
let image = UIImage(data: data) {
metadata.iconProvider = NSItemProvider(object: image) metadata.iconProvider = NSItemProvider(object: image)
} }
return metadata return metadata

View File

@ -19,7 +19,7 @@ public final class AccountMO: NSManagedObject, AccountProtocol {
} }
@NSManaged public var acct: String @NSManaged public var acct: String
@NSManaged public var avatar: URL @NSManaged public var avatar: URL?
@NSManaged public var botCD: Bool @NSManaged public var botCD: Bool
@NSManaged public var createdAt: Date @NSManaged public var createdAt: Date
@NSManaged public var displayName: String @NSManaged public var displayName: String

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21C52" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier=""> <model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21D49" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="Account" representedClassName="AccountMO" syncable="YES"> <entity name="Account" representedClassName="AccountMO" syncable="YES">
<attribute name="acct" attributeType="String"/> <attribute name="acct" attributeType="String"/>
<attribute name="avatar" attributeType="URI"/> <attribute name="avatar" optional="YES" attributeType="URI"/>
<attribute name="botCD" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/> <attribute name="botCD" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/> <attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="displayName" attributeType="String"/> <attribute name="displayName" attributeType="String"/>

View File

@ -56,7 +56,7 @@
<key>NSMicrophoneUsageDescription</key> <key>NSMicrophoneUsageDescription</key>
<string>Post videos from the camera.</string> <string>Post videos from the camera.</string>
<key>NSPhotoLibraryAddUsageDescription</key> <key>NSPhotoLibraryAddUsageDescription</key>
<string>Save photos directly from other people&apos;s posts.</string> <string>Save photos directly from other people's posts.</string>
<key>NSPhotoLibraryUsageDescription</key> <key>NSPhotoLibraryUsageDescription</key>
<string>Post photos from the photo library.</string> <string>Post photos from the photo library.</string>
<key>NSUserActivityTypes</key> <key>NSUserActivityTypes</key>

View File

@ -189,7 +189,7 @@ struct ComposeAutocompleteMentionsView: View {
} }
} }
var avatar: URL { var avatar: URL? {
switch self { switch self {
case let .pachyderm(account): case let .pachyderm(account):
return account.avatar return account.avatar

View File

@ -86,13 +86,15 @@ class ExpandThreadTableViewCell: UITableViewCell {
xConstraint xConstraint
]) ])
let req = ImageCache.avatars.get(account.avatar) { [weak accountImageView] (_, image) in if let avatar = account.avatar {
DispatchQueue.main.async { let req = ImageCache.avatars.get(avatar) { [weak accountImageView] (_, image) in
accountImageView?.image = image DispatchQueue.main.async {
accountImageView?.image = image
}
}
if let req = req {
avatarRequests.append(req)
} }
}
if let req = req {
avatarRequests.append(req)
} }
} }
} }

View File

@ -55,17 +55,19 @@ class FeaturedProfileCollectionViewCell: UICollectionViewCell {
noteTextView.setEmojis(account.emojis) noteTextView.setEmojis(account.emojis)
avatarImageView.image = nil avatarImageView.image = nil
avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (_, image) in if let avatar = account.avatar {
defer { avatarRequest = ImageCache.avatars.get(avatar) { [weak self] (_, image) in
self?.avatarRequest = nil defer {
} self?.avatarRequest = nil
guard let self = self, }
let image = image, guard let self = self,
self.account?.id == account.id else { let image = image,
return self.account?.id == account.id else {
} return
DispatchQueue.main.async { }
self.avatarImageView.image = image DispatchQueue.main.async {
self.avatarImageView.image = image
}
} }
} }

View File

@ -86,8 +86,10 @@ class FastSwitchingAccountView: UIView {
let controller = MastodonController.getForAccount(account) let controller = MastodonController.getForAccount(account)
controller.getOwnAccount { [weak self] (result) in controller.getOwnAccount { [weak self] (result) in
guard let self = self, case let .success(account) = result else { return } guard let self = self,
self.avatarRequest = ImageCache.avatars.get(account.avatar) { [weak avatarImageView] (_, image) in case let .success(account) = result,
let avatar = account.avatar else { return }
self.avatarRequest = ImageCache.avatars.get(avatar) { [weak avatarImageView] (_, image) in
guard let avatarImageView = avatarImageView, let image = image else { return } guard let avatarImageView = avatarImageView, let image = image else { return }
DispatchQueue.main.async { DispatchQueue.main.async {
avatarImageView.image = image avatarImageView.image = image

View File

@ -259,7 +259,8 @@ extension NotificationsTableViewController: UITableViewDataSourcePrefetching {
for indexPath in indexPaths { for indexPath in indexPaths {
guard let group = dataSource.itemIdentifier(for: indexPath) else { continue } guard let group = dataSource.itemIdentifier(for: indexPath) else { continue }
for notification in group.notifications { for notification in group.notifications {
ImageCache.avatars.fetchIfNotCached(notification.account.avatar) guard let avatar = notification.account.avatar else { continue }
ImageCache.avatars.fetchIfNotCached(avatar)
} }
} }
} }
@ -268,7 +269,8 @@ extension NotificationsTableViewController: UITableViewDataSourcePrefetching {
for indexPath in indexPaths { for indexPath in indexPaths {
guard let group = dataSource.itemIdentifier(for: indexPath) else { continue } guard let group = dataSource.itemIdentifier(for: indexPath) else { continue }
for notification in group.notifications { for notification in group.notifications {
ImageCache.avatars.cancelWithoutCallback(notification.account.avatar) guard let avatar = notification.account.avatar else { continue }
ImageCache.avatars.cancelWithoutCallback(avatar)
} }
} }
} }

View File

@ -37,8 +37,9 @@ struct LocalAccountAvatarView: View {
func loadImage() { func loadImage() {
let controller = MastodonController.getForAccount(localAccountInfo) let controller = MastodonController.getForAccount(localAccountInfo)
controller.getOwnAccount { (result) in controller.getOwnAccount { (result) in
guard case let .success(account) = result else { return } guard case let .success(account) = result,
_ = ImageCache.avatars.get(account.avatar) { (_, image) in let avatar = account.avatar else { return }
_ = ImageCache.avatars.get(avatar) { (_, image) in
DispatchQueue.main.async { DispatchQueue.main.async {
self.avatarImage = image self.avatarImage = image
} }

View File

@ -42,7 +42,7 @@ class MyProfileViewController: ProfileViewController {
} }
private func setAvatarTabBarImage<Account: AccountProtocol>(account: Account) { private func setAvatarTabBarImage<Account: AccountProtocol>(account: Account) {
let avatarURL = account.avatar guard let avatarURL = account.avatar else { return }
_ = ImageCache.avatars.get(avatarURL, completion: { [weak self] (_, image) in _ = ImageCache.avatars.get(avatarURL, completion: { [weak self] (_, image) in
guard let self = self, guard let self = self,
let image = image, let image = image,

View File

@ -21,7 +21,8 @@ extension StatusTablePrefetching {
return return
} }
for status in statuses { for status in statuses {
ImageCache.avatars.fetchIfNotCached(status.account.avatar) guard let avatar = status.account.avatar else { continue }
ImageCache.avatars.fetchIfNotCached(avatar)
for attachment in status.attachments where attachment.kind == .image { for attachment in status.attachments where attachment.kind == .image {
ImageCache.attachments.fetchIfNotCached(attachment.url) ImageCache.attachments.fetchIfNotCached(attachment.url)
} }
@ -36,7 +37,8 @@ extension StatusTablePrefetching {
return return
} }
for status in statuses { for status in statuses {
ImageCache.avatars.cancelWithoutCallback(status.account.avatar) guard let avatar = status.account.avatar else { continue }
ImageCache.avatars.cancelWithoutCallback(avatar)
for attachment in status.attachments where attachment.kind == .image { for attachment in status.attachments where attachment.kind == .image {
ImageCache.attachments.cancelWithoutCallback(attachment.url) ImageCache.attachments.cancelWithoutCallback(attachment.url)
} }

View File

@ -62,17 +62,18 @@ class AccountTableViewCell: UITableViewCell {
let accountID = self.accountID let accountID = self.accountID
let avatarURL = account.avatar if let avatarURL = account.avatar {
avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
guard let self = self else { return } guard let self = self else { return }
self.avatarRequest = nil self.avatarRequest = nil
guard let image = image, guard let image = image,
self.accountID == accountID, self.accountID == accountID,
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else { return } let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else { return }
DispatchQueue.main.async { DispatchQueue.main.async {
self.avatarImageView.image = transformedImage self.avatarImageView.image = transformedImage
}
} }
} }

View File

@ -69,11 +69,13 @@ class LargeAccountDetailView: UIView {
displayNameLabel.updateForAccountDisplayName(account: account) displayNameLabel.updateForAccountDisplayName(account: account)
usernameLabel.text = "@\(account.acct)" usernameLabel.text = "@\(account.acct)"
avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (_, image) in if let avatar = account.avatar {
guard let self = self, let image = image else { return } avatarRequest = ImageCache.avatars.get(avatar) { [weak self] (_, image) in
self.avatarRequest = nil guard let self = self, let image = image else { return }
DispatchQueue.main.async { self.avatarRequest = nil
self.avatarImageView.image = image DispatchQueue.main.async {
self.avatarImageView.image = image
}
} }
} }
} }

View File

@ -83,21 +83,22 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
imageView.translatesAutoresizingMaskIntoConstraints = false imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.masksToBounds = true imageView.layer.masksToBounds = true
imageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * 30 imageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * 30
let avatarURL = account.avatar if let avatarURL = account.avatar {
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
guard let self = self else { return } guard let self = self else { return }
guard let image = image, guard let image = image,
self.group.id == group.id, self.group.id == group.id,
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else { let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
DispatchQueue.main.async {
self.avatarRequests.removeValue(forKey: account.id)
}
return
}
DispatchQueue.main.async { DispatchQueue.main.async {
self.avatarRequests.removeValue(forKey: account.id) self.avatarRequests.removeValue(forKey: account.id)
imageView.image = transformedImage
} }
return
}
DispatchQueue.main.async {
self.avatarRequests.removeValue(forKey: account.id)
imageView.image = transformedImage
} }
} }
actionAvatarStackView.addArrangedSubview(imageView) actionAvatarStackView.addArrangedSubview(imageView)
@ -131,21 +132,22 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
continue continue
} }
let avatarURL = account.avatar if let avatarURL = account.avatar {
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
guard let self = self else { return } guard let self = self else { return }
guard let image = image, guard let image = image,
self.group.id == groupID, self.group.id == groupID,
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else { let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
DispatchQueue.main.async {
self.avatarRequests.removeValue(forKey: account.id)
}
return
}
DispatchQueue.main.async { DispatchQueue.main.async {
self.avatarRequests.removeValue(forKey: account.id) self.avatarRequests.removeValue(forKey: account.id)
imageView.image = transformedImage
} }
return
}
DispatchQueue.main.async {
self.avatarRequests.removeValue(forKey: account.id)
imageView.image = transformedImage
} }
} }
} }

View File

@ -64,18 +64,19 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
imageView.translatesAutoresizingMaskIntoConstraints = false imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.masksToBounds = true imageView.layer.masksToBounds = true
imageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * 30 imageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * 30
let avatarURL = account.avatar if let avatarURL = account.avatar {
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
guard let self = self, guard let self = self,
let image = image, let image = image,
self.group.id == group.id, self.group.id == group.id,
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else { let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
return return
} }
DispatchQueue.main.async { DispatchQueue.main.async {
self.avatarRequests.removeValue(forKey: account.id) self.avatarRequests.removeValue(forKey: account.id)
imageView.image = transformedImage imageView.image = transformedImage
}
} }
} }
avatarStackView.addArrangedSubview(imageView) avatarStackView.addArrangedSubview(imageView)
@ -98,21 +99,22 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
continue continue
} }
let avatarURL = account.avatar if let avatarURL = account.avatar {
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
guard let self = self else { return } guard let self = self else { return }
guard let image = image, guard let image = image,
self.group.id == groupID, self.group.id == groupID,
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else { let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
DispatchQueue.main.async {
self.avatarRequests.removeValue(forKey: account.id)
}
return
}
DispatchQueue.main.async { DispatchQueue.main.async {
self.avatarRequests.removeValue(forKey: account.id) self.avatarRequests.removeValue(forKey: account.id)
imageView.image = transformedImage
} }
return
}
DispatchQueue.main.async {
self.avatarRequests.removeValue(forKey: account.id)
imageView.image = transformedImage
} }
} }
} }

View File

@ -67,19 +67,21 @@ class FollowRequestNotificationTableViewCell: UITableViewCell {
actionLabel.text = "Request to follow from \(account.displayName)" actionLabel.text = "Request to follow from \(account.displayName)"
actionLabel.setEmojis(account.emojis, identifier: account.id) actionLabel.setEmojis(account.emojis, identifier: account.id)
} }
let avatarURL = account.avatar
avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
guard let self = self else { return }
self.avatarRequest = nil
guard self.account == account, if let avatarURL = account.avatar {
let image = image, avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else { guard let self = self else { return }
return self.avatarRequest = nil
}
DispatchQueue.main.async { guard self.account == account,
self.avatarImageView.image = transformedImage let image = image,
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
return
}
DispatchQueue.main.async {
self.avatarImageView.image = transformedImage
}
} }
} }
} }

View File

@ -202,22 +202,23 @@ class ProfileHeaderView: UIView {
isGrayscale = Preferences.shared.grayscaleImages isGrayscale = Preferences.shared.grayscaleImages
let accountID = account.id let accountID = account.id
let avatarURL = account.avatar if let avatarURL = account.avatar {
// always load original for avatars, because ImageCache.avatars stores them scaled-down in memory // always load original for avatars, because ImageCache.avatars stores them scaled-down in memory
avatarRequest = ImageCache.avatars.get(avatarURL, loadOriginal: true) { [weak self] (_, image) in avatarRequest = ImageCache.avatars.get(avatarURL, loadOriginal: true) { [weak self] (_, image) in
guard let self = self, guard let self = self,
let image = image, let image = image,
self.accountID == accountID, self.accountID == accountID,
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else { let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
DispatchQueue.main.async { DispatchQueue.main.async {
self?.avatarRequest = nil self?.avatarRequest = nil
}
return
} }
return
}
DispatchQueue.main.async { DispatchQueue.main.async {
self.avatarRequest = nil self.avatarRequest = nil
self.avatarImageView.image = transformedImage self.avatarImageView.image = transformedImage
}
} }
} }
if let header = account.header { if let header = account.header {
@ -243,10 +244,11 @@ class ProfileHeaderView: UIView {
// MARK: Interaction // MARK: Interaction
@objc func avatarPressed() { @objc func avatarPressed() {
guard let account = mastodonController.persistentContainer.account(for: accountID) else { guard let account = mastodonController.persistentContainer.account(for: accountID),
let avatar = account.avatar else {
return return
} }
delegate?.showLoadingLargeImage(url: account.avatar, cache: .avatars, description: nil, animatingFrom: avatarImageView) delegate?.showLoadingLargeImage(url: avatar, cache: .avatars, description: nil, animatingFrom: avatarImageView)
} }
@objc func headerPressed() { @objc func headerPressed() {

View File

@ -251,16 +251,17 @@ class BaseStatusTableViewCell: UITableViewCell, MenuPreviewProvider {
func updateGrayscaleableUI(account: AccountMO, status: StatusMO) { func updateGrayscaleableUI(account: AccountMO, status: StatusMO) {
isGrayscale = Preferences.shared.grayscaleImages isGrayscale = Preferences.shared.grayscaleImages
let avatarURL = account.avatar
let accountID = account.id let accountID = account.id
avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in if let avatarURL = account.avatar {
guard let self = self, avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
let image = image, guard let self = self,
self.accountID == accountID, let image = image,
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else { return } self.accountID == accountID,
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else { return }
DispatchQueue.main.async { DispatchQueue.main.async {
self.avatarImageView.image = transformedImage self.avatarImageView.image = transformedImage
}
} }
} }

View File

@ -269,7 +269,7 @@ struct XCBActions {
"followers": account.followersCount.description, "followers": account.followersCount.description,
"following": account.followingCount.description, "following": account.followingCount.description,
"url": account.url.absoluteString, "url": account.url.absoluteString,
"avatarURL": account.avatar.absoluteString, "avatarURL": account.avatar?.absoluteString,
"headerURL": account.header?.absoluteString "headerURL": account.header?.absoluteString
]) ])
} }
@ -284,7 +284,7 @@ struct XCBActions {
"followers": account.followersCount.description, "followers": account.followersCount.description,
"following": account.followingCount.description, "following": account.followingCount.description,
"url": account.url.absoluteString, "url": account.url.absoluteString,
"avatarURL": account.avatar.absoluteString, "avatarURL": account.avatar?.absoluteString,
"headerURL": account.header?.absoluteString "headerURL": account.header?.absoluteString
]) ])
} }