Compare commits
No commits in common. "930ec7ccff384fd8b64e59359c46589578148b27" and "126b0ae90a1033b01c7b59b058f1396368086a00" have entirely different histories.
930ec7ccff
...
126b0ae90a
15
CHANGELOG.md
15
CHANGELOG.md
|
@ -1,20 +1,5 @@
|
|||
# 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)
|
||||
Features/Improvements:
|
||||
- Synchronize GIF playback through animations and in gallery
|
||||
|
|
|
@ -20,9 +20,8 @@ public final class Account: AccountProtocol, Decodable {
|
|||
public let statusesCount: Int
|
||||
public let note: String
|
||||
public let url: URL
|
||||
// required on mastodon, but optional on gotosocial
|
||||
public let avatar: URL?
|
||||
public let avatarStatic: URL?
|
||||
public let avatar: URL
|
||||
public let avatarStatic: URL
|
||||
public let header: URL?
|
||||
public let headerStatic: URL?
|
||||
public private(set) var emojis: [Emoji]
|
||||
|
@ -45,8 +44,8 @@ public final class Account: AccountProtocol, Decodable {
|
|||
self.statusesCount = try container.decode(Int.self, forKey: .statusesCount)
|
||||
self.note = try container.decode(String.self, forKey: .note)
|
||||
self.url = try container.decode(URL.self, forKey: .url)
|
||||
self.avatar = try? container.decode(URL.self, forKey: .avatar)
|
||||
self.avatarStatic = try? container.decode(URL.self, forKey: .avatarStatic)
|
||||
self.avatar = try container.decode(URL.self, forKey: .avatar)
|
||||
self.avatarStatic = try container.decode(URL.self, forKey: .avatarStatic)
|
||||
self.header = try? container.decode(URL.self, forKey: .header)
|
||||
self.headerStatic = try? container.decode(URL.self, forKey: .headerStatic)
|
||||
self.emojis = try container.decode([Emoji].self, forKey: .emojis)
|
||||
|
|
|
@ -56,23 +56,6 @@ extension Attachment {
|
|||
case gifv
|
||||
case audio
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ public protocol AccountProtocol {
|
|||
var statusesCount: Int { get }
|
||||
var note: String { get }
|
||||
var url: URL { get }
|
||||
var avatar: URL? { get }
|
||||
var avatar: URL { get }
|
||||
var header: URL? { get }
|
||||
var moved: Bool? { get }
|
||||
var bot: Bool? { get }
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objectVersion = 52;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
|
@ -126,6 +126,7 @@
|
|||
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 */; };
|
||||
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 */; };
|
||||
D6370B9C24421FF30092A7FF /* Tusker.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = D6370B9A24421FF30092A7FF /* Tusker.xcdatamodeld */; };
|
||||
D63A8D0B2561C27F00D9DFFF /* ProfileStatusesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63A8D0A2561C27F00D9DFFF /* ProfileStatusesViewController.swift */; };
|
||||
|
@ -177,6 +178,7 @@
|
|||
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 */; };
|
||||
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 */; };
|
||||
D667E5E12134937B0057A976 /* TimelineStatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D667E5E02134937B0057A976 /* TimelineStatusTableViewCell.xib */; };
|
||||
D667E5F12134D5050057A976 /* UIViewController+Delegates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F02134D5050057A976 /* UIViewController+Delegates.swift */; };
|
||||
|
@ -339,7 +341,6 @@
|
|||
D6EE63FB2551F7F60065485C /* StatusCollapseButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6EE63FA2551F7F60065485C /* StatusCollapseButton.swift */; };
|
||||
D6F0B12B24A3071C001E48C3 /* MainSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F0B12A24A3071C001E48C3 /* MainSplitViewController.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 */; };
|
||||
D6F2E966249E8BFD005846BB /* CrashReporterViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6F2E964249E8BFD005846BB /* CrashReporterViewController.xib */; };
|
||||
D6F953F021251A2900CF0F2B /* MastodonController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F953EF21251A2900CF0F2B /* MastodonController.swift */; };
|
||||
|
@ -768,7 +769,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D6F1F9DF27B0613300CB7D88 /* WebURL in Frameworks */,
|
||||
D635ED5027ACD9260003635B /* WebURL in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -784,6 +785,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D6676CA327A8D0020052936B /* WebURL in Frameworks */,
|
||||
D61099C02144B0CC00432DC2 /* Pachyderm.framework in Frameworks */,
|
||||
D6B0539F23BD2BA300A066FA /* SheetController in Frameworks */,
|
||||
D69CCBBF249E6EFD000AF167 /* CrashReporter in Frameworks */,
|
||||
|
@ -1725,7 +1727,7 @@
|
|||
);
|
||||
name = Pachyderm;
|
||||
packageProductDependencies = (
|
||||
D6F1F9DE27B0613300CB7D88 /* WebURL */,
|
||||
D635ED4F27ACD9260003635B /* WebURL */,
|
||||
);
|
||||
productName = Pachyderm;
|
||||
productReference = D61099AB2144B0CC00432DC2 /* Pachyderm.framework */;
|
||||
|
@ -1760,7 +1762,6 @@
|
|||
D6F953E52125197500CF0F2B /* Embed Frameworks */,
|
||||
D65F612C23AE957600F3CFD3 /* Embed debug-only frameworks */,
|
||||
D6E3438F2659849800C4AA01 /* Embed App Extensions */,
|
||||
D6F1F9E127B0677000CB7D88 /* ShellScript */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
@ -1773,6 +1774,7 @@
|
|||
D6B0539E23BD2BA300A066FA /* SheetController */,
|
||||
D69CCBBE249E6EFD000AF167 /* CrashReporter */,
|
||||
D60CFFDA24A290BA00D00083 /* SwiftSoup */,
|
||||
D6676CA227A8D0020052936B /* WebURL */,
|
||||
D6676CA427A8D0020052936B /* WebURLFoundationExtras */,
|
||||
);
|
||||
productName = Tusker;
|
||||
|
@ -2006,24 +2008,6 @@
|
|||
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";
|
||||
};
|
||||
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 */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
|
@ -2430,11 +2414,11 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||
DEVELOPMENT_TEAM = HGYVAQA9FW;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
|
@ -2446,8 +2430,6 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
OTHER_CODE_SIGN_FLAGS = "--deep";
|
||||
OTHER_SWIFT_FLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.Pachyderm;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
|
@ -2463,11 +2445,11 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||
DEVELOPMENT_TEAM = HGYVAQA9FW;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
|
@ -2479,8 +2461,6 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
OTHER_CODE_SIGN_FLAGS = "--deep";
|
||||
OTHER_SWIFT_FLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.Pachyderm;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
|
@ -2661,7 +2641,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 24;
|
||||
CURRENT_PROJECT_VERSION = 23;
|
||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||
INFOPLIST_FILE = Tusker/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||
|
@ -2670,8 +2650,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2022.1;
|
||||
OTHER_CODE_SIGN_FLAGS = "";
|
||||
MARKETING_VERSION = 2021.1;
|
||||
OTHER_LDFLAGS = "";
|
||||
"OTHER_SWIFT_FLAGS[sdk=iphone*14*]" = "$(inherited) -D SDK_IOS_14";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker;
|
||||
|
@ -2692,7 +2671,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 24;
|
||||
CURRENT_PROJECT_VERSION = 23;
|
||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||
INFOPLIST_FILE = Tusker/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||
|
@ -2701,8 +2680,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2022.1;
|
||||
OTHER_CODE_SIGN_FLAGS = "";
|
||||
MARKETING_VERSION = 2021.1;
|
||||
"OTHER_SWIFT_FLAGS[sdk=iphone*14*]" = "$(inherited) -D SDK_IOS_14";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -2802,7 +2780,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 24;
|
||||
CURRENT_PROJECT_VERSION = 23;
|
||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||
INFOPLIST_FILE = OpenInTusker/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||
|
@ -2812,7 +2790,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2022.1;
|
||||
MARKETING_VERSION = 2021.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.OpenInTusker;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
|
@ -2829,7 +2807,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 24;
|
||||
CURRENT_PROJECT_VERSION = 23;
|
||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||
INFOPLIST_FILE = OpenInTusker/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||
|
@ -2839,7 +2817,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2022.1;
|
||||
MARKETING_VERSION = 2021.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.OpenInTusker;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
|
@ -2958,6 +2936,16 @@
|
|||
package = D60CFFD924A290BA00D00083 /* XCRemoteSwiftPackageReference "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 */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = D6676CA127A8D0020052936B /* XCRemoteSwiftPackageReference "swift-url" */;
|
||||
|
@ -2973,11 +2961,6 @@
|
|||
package = D6B0539D23BD2BA300A066FA /* XCRemoteSwiftPackageReference "SheetController" */;
|
||||
productName = SheetController;
|
||||
};
|
||||
D6F1F9DE27B0613300CB7D88 /* WebURL */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = D6676CA127A8D0020052936B /* XCRemoteSwiftPackageReference "swift-url" */;
|
||||
productName = WebURL;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
|
||||
/* Begin XCVersionGroup section */
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
"repositoryURL": "https://github.com/karwa/swift-url",
|
||||
"state": {
|
||||
"branch": "main",
|
||||
"revision": "9d06f9f89397de16c8942aa123c425568654fd6a",
|
||||
"revision": "1519936d9813af86e57c06dc7f727cb281de9f16",
|
||||
"version": null
|
||||
}
|
||||
},
|
||||
|
|
|
@ -29,9 +29,8 @@ class AccountActivityItemSource: NSObject, UIActivityItemSource {
|
|||
metadata.originalURL = account.url
|
||||
metadata.url = account.url
|
||||
metadata.title = "\(account.displayName) (@\(account.username)@\(account.url.host!)"
|
||||
if let avatar = account.avatar,
|
||||
let data = ImageCache.avatars.getData(avatar),
|
||||
let image = UIImage(data: data) {
|
||||
if let data = ImageCache.avatars.getData(account.avatar),
|
||||
let image = UIImage(data: data) {
|
||||
metadata.iconProvider = NSItemProvider(object: image)
|
||||
}
|
||||
return metadata
|
||||
|
|
|
@ -32,9 +32,8 @@ class StatusActivityItemSource: NSObject, UIActivityItemSource {
|
|||
let doc = try! SwiftSoup.parse(status.content)
|
||||
let content = try! doc.text()
|
||||
metadata.title = "\(status.account.displayName): \"\(content)\""
|
||||
if let avatar = status.account.avatar,
|
||||
let data = ImageCache.avatars.getData(avatar),
|
||||
let image = UIImage(data: data) {
|
||||
if let data = ImageCache.avatars.getData(status.account.avatar),
|
||||
let image = UIImage(data: data) {
|
||||
metadata.iconProvider = NSItemProvider(object: image)
|
||||
}
|
||||
return metadata
|
||||
|
|
|
@ -19,7 +19,7 @@ public final class AccountMO: NSManagedObject, AccountProtocol {
|
|||
}
|
||||
|
||||
@NSManaged public var acct: String
|
||||
@NSManaged public var avatar: URL?
|
||||
@NSManaged public var avatar: URL
|
||||
@NSManaged public var botCD: Bool
|
||||
@NSManaged public var createdAt: Date
|
||||
@NSManaged public var displayName: String
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21D49" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21C52" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="Account" representedClassName="AccountMO" syncable="YES">
|
||||
<attribute name="acct" attributeType="String"/>
|
||||
<attribute name="avatar" optional="YES" attributeType="URI"/>
|
||||
<attribute name="avatar" attributeType="URI"/>
|
||||
<attribute name="botCD" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="displayName" attributeType="String"/>
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Post videos from the camera.</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>Save photos directly from other people's posts.</string>
|
||||
<string>Save photos directly from other people's posts.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Post photos from the photo library.</string>
|
||||
<key>NSUserActivityTypes</key>
|
||||
|
|
|
@ -189,7 +189,7 @@ struct ComposeAutocompleteMentionsView: View {
|
|||
}
|
||||
}
|
||||
|
||||
var avatar: URL? {
|
||||
var avatar: URL {
|
||||
switch self {
|
||||
case let .pachyderm(account):
|
||||
return account.avatar
|
||||
|
|
|
@ -86,16 +86,14 @@ class ExpandThreadTableViewCell: UITableViewCell {
|
|||
xConstraint
|
||||
])
|
||||
|
||||
if let avatar = account.avatar {
|
||||
let req = ImageCache.avatars.get(avatar) { [weak accountImageView] (_, image) in
|
||||
DispatchQueue.main.async {
|
||||
accountImageView?.image = image
|
||||
}
|
||||
}
|
||||
if let req = req {
|
||||
avatarRequests.append(req)
|
||||
let req = ImageCache.avatars.get(account.avatar) { [weak accountImageView] (_, image) in
|
||||
DispatchQueue.main.async {
|
||||
accountImageView?.image = image
|
||||
}
|
||||
}
|
||||
if let req = req {
|
||||
avatarRequests.append(req)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,19 +55,17 @@ class FeaturedProfileCollectionViewCell: UICollectionViewCell {
|
|||
noteTextView.setEmojis(account.emojis)
|
||||
|
||||
avatarImageView.image = nil
|
||||
if let avatar = account.avatar {
|
||||
avatarRequest = ImageCache.avatars.get(avatar) { [weak self] (_, image) in
|
||||
defer {
|
||||
self?.avatarRequest = nil
|
||||
}
|
||||
guard let self = self,
|
||||
let image = image,
|
||||
self.account?.id == account.id else {
|
||||
return
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
self.avatarImageView.image = image
|
||||
}
|
||||
avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (_, image) in
|
||||
defer {
|
||||
self?.avatarRequest = nil
|
||||
}
|
||||
guard let self = self,
|
||||
let image = image,
|
||||
self.account?.id == account.id else {
|
||||
return
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
self.avatarImageView.image = image
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -86,10 +86,8 @@ class FastSwitchingAccountView: UIView {
|
|||
|
||||
let controller = MastodonController.getForAccount(account)
|
||||
controller.getOwnAccount { [weak self] (result) in
|
||||
guard let self = self,
|
||||
case let .success(account) = result,
|
||||
let avatar = account.avatar else { return }
|
||||
self.avatarRequest = ImageCache.avatars.get(avatar) { [weak avatarImageView] (_, image) in
|
||||
guard let self = self, case let .success(account) = result else { return }
|
||||
self.avatarRequest = ImageCache.avatars.get(account.avatar) { [weak avatarImageView] (_, image) in
|
||||
guard let avatarImageView = avatarImageView, let image = image else { return }
|
||||
DispatchQueue.main.async {
|
||||
avatarImageView.image = image
|
||||
|
|
|
@ -259,8 +259,7 @@ extension NotificationsTableViewController: UITableViewDataSourcePrefetching {
|
|||
for indexPath in indexPaths {
|
||||
guard let group = dataSource.itemIdentifier(for: indexPath) else { continue }
|
||||
for notification in group.notifications {
|
||||
guard let avatar = notification.account.avatar else { continue }
|
||||
ImageCache.avatars.fetchIfNotCached(avatar)
|
||||
ImageCache.avatars.fetchIfNotCached(notification.account.avatar)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -269,8 +268,7 @@ extension NotificationsTableViewController: UITableViewDataSourcePrefetching {
|
|||
for indexPath in indexPaths {
|
||||
guard let group = dataSource.itemIdentifier(for: indexPath) else { continue }
|
||||
for notification in group.notifications {
|
||||
guard let avatar = notification.account.avatar else { continue }
|
||||
ImageCache.avatars.cancelWithoutCallback(avatar)
|
||||
ImageCache.avatars.cancelWithoutCallback(notification.account.avatar)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,9 +37,8 @@ struct LocalAccountAvatarView: View {
|
|||
func loadImage() {
|
||||
let controller = MastodonController.getForAccount(localAccountInfo)
|
||||
controller.getOwnAccount { (result) in
|
||||
guard case let .success(account) = result,
|
||||
let avatar = account.avatar else { return }
|
||||
_ = ImageCache.avatars.get(avatar) { (_, image) in
|
||||
guard case let .success(account) = result else { return }
|
||||
_ = ImageCache.avatars.get(account.avatar) { (_, image) in
|
||||
DispatchQueue.main.async {
|
||||
self.avatarImage = image
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ class MyProfileViewController: ProfileViewController {
|
|||
}
|
||||
|
||||
private func setAvatarTabBarImage<Account: AccountProtocol>(account: Account) {
|
||||
guard let avatarURL = account.avatar else { return }
|
||||
let avatarURL = account.avatar
|
||||
_ = ImageCache.avatars.get(avatarURL, completion: { [weak self] (_, image) in
|
||||
guard let self = self,
|
||||
let image = image,
|
||||
|
|
|
@ -21,8 +21,7 @@ extension StatusTablePrefetching {
|
|||
return
|
||||
}
|
||||
for status in statuses {
|
||||
guard let avatar = status.account.avatar else { continue }
|
||||
ImageCache.avatars.fetchIfNotCached(avatar)
|
||||
ImageCache.avatars.fetchIfNotCached(status.account.avatar)
|
||||
for attachment in status.attachments where attachment.kind == .image {
|
||||
ImageCache.attachments.fetchIfNotCached(attachment.url)
|
||||
}
|
||||
|
@ -37,8 +36,7 @@ extension StatusTablePrefetching {
|
|||
return
|
||||
}
|
||||
for status in statuses {
|
||||
guard let avatar = status.account.avatar else { continue }
|
||||
ImageCache.avatars.cancelWithoutCallback(avatar)
|
||||
ImageCache.avatars.cancelWithoutCallback(status.account.avatar)
|
||||
for attachment in status.attachments where attachment.kind == .image {
|
||||
ImageCache.attachments.cancelWithoutCallback(attachment.url)
|
||||
}
|
||||
|
|
|
@ -62,18 +62,17 @@ class AccountTableViewCell: UITableViewCell {
|
|||
|
||||
let accountID = self.accountID
|
||||
|
||||
if let avatarURL = account.avatar {
|
||||
avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||
guard let self = self else { return }
|
||||
self.avatarRequest = nil
|
||||
|
||||
guard let image = image,
|
||||
self.accountID == accountID,
|
||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else { return }
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.avatarImageView.image = transformedImage
|
||||
}
|
||||
let avatarURL = account.avatar
|
||||
avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||
guard let self = self else { return }
|
||||
self.avatarRequest = nil
|
||||
|
||||
guard let image = image,
|
||||
self.accountID == accountID,
|
||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else { return }
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.avatarImageView.image = transformedImage
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,13 +69,11 @@ class LargeAccountDetailView: UIView {
|
|||
displayNameLabel.updateForAccountDisplayName(account: account)
|
||||
usernameLabel.text = "@\(account.acct)"
|
||||
|
||||
if let avatar = account.avatar {
|
||||
avatarRequest = ImageCache.avatars.get(avatar) { [weak self] (_, image) in
|
||||
guard let self = self, let image = image else { return }
|
||||
self.avatarRequest = nil
|
||||
DispatchQueue.main.async {
|
||||
self.avatarImageView.image = image
|
||||
}
|
||||
avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (_, image) in
|
||||
guard let self = self, let image = image else { return }
|
||||
self.avatarRequest = nil
|
||||
DispatchQueue.main.async {
|
||||
self.avatarImageView.image = image
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,22 +83,21 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
|||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
imageView.layer.masksToBounds = true
|
||||
imageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * 30
|
||||
if let avatarURL = account.avatar {
|
||||
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||
guard let self = self else { return }
|
||||
guard let image = image,
|
||||
self.group.id == group.id,
|
||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||
DispatchQueue.main.async {
|
||||
self.avatarRequests.removeValue(forKey: account.id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let avatarURL = account.avatar
|
||||
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||
guard let self = self else { return }
|
||||
guard let image = image,
|
||||
self.group.id == group.id,
|
||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||
DispatchQueue.main.async {
|
||||
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)
|
||||
|
@ -132,22 +131,21 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
|||
continue
|
||||
}
|
||||
|
||||
if let avatarURL = account.avatar {
|
||||
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||
guard let self = self else { return }
|
||||
guard let image = image,
|
||||
self.group.id == groupID,
|
||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||
DispatchQueue.main.async {
|
||||
self.avatarRequests.removeValue(forKey: account.id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let avatarURL = account.avatar
|
||||
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||
guard let self = self else { return }
|
||||
guard let image = image,
|
||||
self.group.id == groupID,
|
||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||
DispatchQueue.main.async {
|
||||
self.avatarRequests.removeValue(forKey: account.id)
|
||||
imageView.image = transformedImage
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.avatarRequests.removeValue(forKey: account.id)
|
||||
imageView.image = transformedImage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,19 +64,18 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
|
|||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
imageView.layer.masksToBounds = true
|
||||
imageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * 30
|
||||
if let avatarURL = account.avatar {
|
||||
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||
guard let self = self,
|
||||
let image = image,
|
||||
self.group.id == group.id,
|
||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.avatarRequests.removeValue(forKey: account.id)
|
||||
imageView.image = transformedImage
|
||||
}
|
||||
let avatarURL = account.avatar
|
||||
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||
guard let self = self,
|
||||
let image = image,
|
||||
self.group.id == group.id,
|
||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.avatarRequests.removeValue(forKey: account.id)
|
||||
imageView.image = transformedImage
|
||||
}
|
||||
}
|
||||
avatarStackView.addArrangedSubview(imageView)
|
||||
|
@ -99,22 +98,21 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
|
|||
continue
|
||||
}
|
||||
|
||||
if let avatarURL = account.avatar {
|
||||
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||
guard let self = self else { return }
|
||||
guard let image = image,
|
||||
self.group.id == groupID,
|
||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||
DispatchQueue.main.async {
|
||||
self.avatarRequests.removeValue(forKey: account.id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let avatarURL = account.avatar
|
||||
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||
guard let self = self else { return }
|
||||
guard let image = image,
|
||||
self.group.id == groupID,
|
||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||
DispatchQueue.main.async {
|
||||
self.avatarRequests.removeValue(forKey: account.id)
|
||||
imageView.image = transformedImage
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.avatarRequests.removeValue(forKey: account.id)
|
||||
imageView.image = transformedImage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,21 +67,19 @@ class FollowRequestNotificationTableViewCell: UITableViewCell {
|
|||
actionLabel.text = "Request to follow from \(account.displayName)"
|
||||
actionLabel.setEmojis(account.emojis, identifier: account.id)
|
||||
}
|
||||
|
||||
if 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,
|
||||
let image = image,
|
||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.avatarImageView.image = transformedImage
|
||||
}
|
||||
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,
|
||||
let image = image,
|
||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.avatarImageView.image = transformedImage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -202,23 +202,22 @@ class ProfileHeaderView: UIView {
|
|||
isGrayscale = Preferences.shared.grayscaleImages
|
||||
|
||||
let accountID = account.id
|
||||
if let avatarURL = account.avatar {
|
||||
// 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
|
||||
guard let self = self,
|
||||
let image = image,
|
||||
self.accountID == accountID,
|
||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||
DispatchQueue.main.async {
|
||||
self?.avatarRequest = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let avatarURL = account.avatar
|
||||
// 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
|
||||
guard let self = self,
|
||||
let image = image,
|
||||
self.accountID == accountID,
|
||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||
DispatchQueue.main.async {
|
||||
self.avatarRequest = nil
|
||||
self.avatarImageView.image = transformedImage
|
||||
self?.avatarRequest = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.avatarRequest = nil
|
||||
self.avatarImageView.image = transformedImage
|
||||
}
|
||||
}
|
||||
if let header = account.header {
|
||||
|
@ -244,11 +243,10 @@ class ProfileHeaderView: UIView {
|
|||
// MARK: Interaction
|
||||
|
||||
@objc func avatarPressed() {
|
||||
guard let account = mastodonController.persistentContainer.account(for: accountID),
|
||||
let avatar = account.avatar else {
|
||||
guard let account = mastodonController.persistentContainer.account(for: accountID) else {
|
||||
return
|
||||
}
|
||||
delegate?.showLoadingLargeImage(url: avatar, cache: .avatars, description: nil, animatingFrom: avatarImageView)
|
||||
delegate?.showLoadingLargeImage(url: account.avatar, cache: .avatars, description: nil, animatingFrom: avatarImageView)
|
||||
}
|
||||
|
||||
@objc func headerPressed() {
|
||||
|
|
|
@ -251,17 +251,16 @@ class BaseStatusTableViewCell: UITableViewCell, MenuPreviewProvider {
|
|||
func updateGrayscaleableUI(account: AccountMO, status: StatusMO) {
|
||||
isGrayscale = Preferences.shared.grayscaleImages
|
||||
|
||||
let avatarURL = account.avatar
|
||||
let accountID = account.id
|
||||
if let avatarURL = account.avatar {
|
||||
avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||
guard let self = self,
|
||||
let image = image,
|
||||
self.accountID == accountID,
|
||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else { return }
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.avatarImageView.image = transformedImage
|
||||
}
|
||||
avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||
guard let self = self,
|
||||
let image = image,
|
||||
self.accountID == accountID,
|
||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else { return }
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.avatarImageView.image = transformedImage
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -269,7 +269,7 @@ struct XCBActions {
|
|||
"followers": account.followersCount.description,
|
||||
"following": account.followingCount.description,
|
||||
"url": account.url.absoluteString,
|
||||
"avatarURL": account.avatar?.absoluteString,
|
||||
"avatarURL": account.avatar.absoluteString,
|
||||
"headerURL": account.header?.absoluteString
|
||||
])
|
||||
}
|
||||
|
@ -284,7 +284,7 @@ struct XCBActions {
|
|||
"followers": account.followersCount.description,
|
||||
"following": account.followingCount.description,
|
||||
"url": account.url.absoluteString,
|
||||
"avatarURL": account.avatar?.absoluteString,
|
||||
"avatarURL": account.avatar.absoluteString,
|
||||
"headerURL": account.header?.absoluteString
|
||||
])
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue