Compare commits
No commits in common. "e3cc0df28323e949afcfe7f823cae8f4060e7430" and "0691c3b9d65d6cd9b4b082030337c81dc02a1cf6" have entirely different histories.
e3cc0df283
...
0691c3b9d6
|
@ -29,15 +29,15 @@ public class Attachment: Decodable {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
self.id = try container.decode(String.self, forKey: .id)
|
self.id = try container.decode(String.self, forKey: .id)
|
||||||
self.kind = try container.decode(Kind.self, forKey: .kind)
|
self.kind = try container.decode(Kind.self, forKey: .kind)
|
||||||
self.url = URL(string: try container.decode(String.self, forKey: .url))!
|
self.url = URL(lenient: try container.decode(String.self, forKey: .url))!
|
||||||
self.previewURL = URL(string: try container.decode(String.self, forKey: .previewURL))!
|
|
||||||
if let remote = try? container.decode(String.self, forKey: .remoteURL) {
|
if let remote = try? container.decode(String.self, forKey: .remoteURL) {
|
||||||
self.remoteURL = URL(string: remote)!
|
self.remoteURL = URL(lenient: remote.replacingOccurrences(of: " ", with: "%20"))
|
||||||
} else {
|
} else {
|
||||||
self.remoteURL = nil
|
self.remoteURL = nil
|
||||||
}
|
}
|
||||||
|
self.previewURL = URL(lenient: try container.decode(String.self, forKey: .previewURL).replacingOccurrences(of: " ", with: "%20"))!
|
||||||
if let text = try? container.decode(String.self, forKey: .textURL) {
|
if let text = try? container.decode(String.self, forKey: .textURL) {
|
||||||
self.textURL = URL(string: text)!
|
self.textURL = URL(lenient: text.replacingOccurrences(of: " ", with: "%20"))
|
||||||
} else {
|
} else {
|
||||||
self.textURL = nil
|
self.textURL = nil
|
||||||
}
|
}
|
||||||
|
@ -113,3 +113,14 @@ extension Attachment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fileprivate extension URL {
|
||||||
|
private static let allowedChars = CharacterSet.urlHostAllowed.union(.urlPathAllowed).union(.urlQueryAllowed)
|
||||||
|
|
||||||
|
init?(lenient string: String) {
|
||||||
|
guard let escaped = string.addingPercentEncoding(withAllowedCharacters: URL.allowedChars) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
self.init(string: escaped)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
04ED00B121481ED800567C53 /* SteppedProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04ED00B021481ED800567C53 /* SteppedProgressView.swift */; };
|
04ED00B121481ED800567C53 /* SteppedProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04ED00B021481ED800567C53 /* SteppedProgressView.swift */; };
|
||||||
D6028B9B2150811100F223B9 /* MastodonCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6028B9A2150811100F223B9 /* MastodonCache.swift */; };
|
D6028B9B2150811100F223B9 /* MastodonCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6028B9A2150811100F223B9 /* MastodonCache.swift */; };
|
||||||
D60309B52419D4F100A465FF /* ComposeAttachmentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60309B42419D4F100A465FF /* ComposeAttachmentsViewController.swift */; };
|
D60309B52419D4F100A465FF /* ComposeAttachmentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60309B42419D4F100A465FF /* ComposeAttachmentsViewController.swift */; };
|
||||||
|
D60C07E421E8176B0057FAA8 /* ComposeMediaView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D60C07E321E8176B0057FAA8 /* ComposeMediaView.xib */; };
|
||||||
D60D2B8223844C71001B87A3 /* BaseStatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60D2B8123844C71001B87A3 /* BaseStatusTableViewCell.swift */; };
|
D60D2B8223844C71001B87A3 /* BaseStatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60D2B8123844C71001B87A3 /* BaseStatusTableViewCell.swift */; };
|
||||||
D61099B42144B0CC00432DC2 /* Pachyderm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D61099AB2144B0CC00432DC2 /* Pachyderm.framework */; };
|
D61099B42144B0CC00432DC2 /* Pachyderm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D61099AB2144B0CC00432DC2 /* Pachyderm.framework */; };
|
||||||
D61099BB2144B0CC00432DC2 /* PachydermTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61099BA2144B0CC00432DC2 /* PachydermTests.swift */; };
|
D61099BB2144B0CC00432DC2 /* PachydermTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61099BA2144B0CC00432DC2 /* PachydermTests.swift */; };
|
||||||
|
@ -105,6 +106,7 @@
|
||||||
D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2425217ABF63005076CC /* UserActivityType.swift */; };
|
D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2425217ABF63005076CC /* UserActivityType.swift */; };
|
||||||
D62FF04823D7CDD700909D6E /* AttributedStringHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62FF04723D7CDD700909D6E /* AttributedStringHelperTests.swift */; };
|
D62FF04823D7CDD700909D6E /* AttributedStringHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62FF04723D7CDD700909D6E /* AttributedStringHelperTests.swift */; };
|
||||||
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 */; };
|
||||||
|
D6333B772138D94E00CE884A /* ComposeMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B762138D94E00CE884A /* ComposeMediaView.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 */; };
|
||||||
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */; };
|
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */; };
|
||||||
|
@ -309,6 +311,7 @@
|
||||||
D6028B9A2150811100F223B9 /* MastodonCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonCache.swift; sourceTree = "<group>"; };
|
D6028B9A2150811100F223B9 /* MastodonCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonCache.swift; sourceTree = "<group>"; };
|
||||||
D60309B42419D4F100A465FF /* ComposeAttachmentsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeAttachmentsViewController.swift; sourceTree = "<group>"; };
|
D60309B42419D4F100A465FF /* ComposeAttachmentsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeAttachmentsViewController.swift; sourceTree = "<group>"; };
|
||||||
D60A4FFB238B726A008AC647 /* StatusState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusState.swift; sourceTree = "<group>"; };
|
D60A4FFB238B726A008AC647 /* StatusState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusState.swift; sourceTree = "<group>"; };
|
||||||
|
D60C07E321E8176B0057FAA8 /* ComposeMediaView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ComposeMediaView.xib; sourceTree = "<group>"; };
|
||||||
D60D2B8123844C71001B87A3 /* BaseStatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseStatusTableViewCell.swift; sourceTree = "<group>"; };
|
D60D2B8123844C71001B87A3 /* BaseStatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseStatusTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
D61099AB2144B0CC00432DC2 /* Pachyderm.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pachyderm.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
D61099AB2144B0CC00432DC2 /* Pachyderm.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pachyderm.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
D61099AD2144B0CC00432DC2 /* Pachyderm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Pachyderm.h; sourceTree = "<group>"; };
|
D61099AD2144B0CC00432DC2 /* Pachyderm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Pachyderm.h; sourceTree = "<group>"; };
|
||||||
|
@ -390,6 +393,7 @@
|
||||||
D62D2425217ABF63005076CC /* UserActivityType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActivityType.swift; sourceTree = "<group>"; };
|
D62D2425217ABF63005076CC /* UserActivityType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActivityType.swift; sourceTree = "<group>"; };
|
||||||
D62FF04723D7CDD700909D6E /* AttributedStringHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringHelperTests.swift; sourceTree = "<group>"; };
|
D62FF04723D7CDD700909D6E /* AttributedStringHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringHelperTests.swift; sourceTree = "<group>"; };
|
||||||
D6333B362137838300CE884A /* AttributedString+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Helpers.swift"; sourceTree = "<group>"; };
|
D6333B362137838300CE884A /* AttributedString+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Helpers.swift"; sourceTree = "<group>"; };
|
||||||
|
D6333B762138D94E00CE884A /* ComposeMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeMediaView.swift; sourceTree = "<group>"; };
|
||||||
D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+TimeAgo.swift"; sourceTree = "<group>"; };
|
D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+TimeAgo.swift"; sourceTree = "<group>"; };
|
||||||
D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesNavigationController.swift; sourceTree = "<group>"; };
|
D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesNavigationController.swift; sourceTree = "<group>"; };
|
||||||
D63F9C65241C4CC3004C03CF /* AddAttachmentTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AddAttachmentTableViewCell.xib; sourceTree = "<group>"; };
|
D63F9C65241C4CC3004C03CF /* AddAttachmentTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AddAttachmentTableViewCell.xib; sourceTree = "<group>"; };
|
||||||
|
@ -599,6 +603,15 @@
|
||||||
path = Transitions;
|
path = Transitions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
D60C07E221E817560057FAA8 /* Compose Media */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D60C07E321E8176B0057FAA8 /* ComposeMediaView.xib */,
|
||||||
|
D6333B762138D94E00CE884A /* ComposeMediaView.swift */,
|
||||||
|
);
|
||||||
|
path = "Compose Media";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
D61099AC2144B0CC00432DC2 /* Pachyderm */ = {
|
D61099AC2144B0CC00432DC2 /* Pachyderm */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -694,37 +707,6 @@
|
||||||
path = "Hashtag Cell";
|
path = "Hashtag Cell";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
D61959D0241E842400A37B8E /* Draft Cell */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
D627FF7C217E958900CC0648 /* DraftTableViewCell.xib */,
|
|
||||||
D627FF7E217E95E000CC0648 /* DraftTableViewCell.swift */,
|
|
||||||
);
|
|
||||||
path = "Draft Cell";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
D61959D1241E844900A37B8E /* Attachment Cells */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
D63F9C67241C4F79004C03CF /* AddAttachmentTableViewCell.swift */,
|
|
||||||
D63F9C65241C4CC3004C03CF /* AddAttachmentTableViewCell.xib */,
|
|
||||||
D63F9C69241C50B9004C03CF /* ComposeAttachmentTableViewCell.swift */,
|
|
||||||
D63F9C6A241C50B9004C03CF /* ComposeAttachmentTableViewCell.xib */,
|
|
||||||
);
|
|
||||||
path = "Attachment Cells";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
D61959D2241E846D00A37B8E /* Models */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
D6285B5221EA708700FE4B39 /* StatusFormat.swift */,
|
|
||||||
D620483123D2A6A3008A63EF /* CompositionState.swift */,
|
|
||||||
D63F9C6D241D2D85004C03CF /* CompositionAttachment.swift */,
|
|
||||||
D626493423BD94CE00612E6E /* CompositionAttachmentData.swift */,
|
|
||||||
);
|
|
||||||
path = Models;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
D61AC1DA232EA43100C54D2D /* Instance Cell */ = {
|
D61AC1DA232EA43100C54D2D /* Instance Cell */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -792,6 +774,8 @@
|
||||||
children = (
|
children = (
|
||||||
D627FF78217E950100CC0648 /* DraftsTableViewController.xib */,
|
D627FF78217E950100CC0648 /* DraftsTableViewController.xib */,
|
||||||
D627FF7A217E951500CC0648 /* DraftsTableViewController.swift */,
|
D627FF7A217E951500CC0648 /* DraftsTableViewController.swift */,
|
||||||
|
D627FF7C217E958900CC0648 /* DraftTableViewCell.xib */,
|
||||||
|
D627FF7E217E95E000CC0648 /* DraftTableViewCell.swift */,
|
||||||
);
|
);
|
||||||
path = Drafts;
|
path = Drafts;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -807,6 +791,20 @@
|
||||||
path = Shortcuts;
|
path = Shortcuts;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
D63F9C64241C4CAA004C03CF /* Attachments */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D63F9C6D241D2D85004C03CF /* CompositionAttachment.swift */,
|
||||||
|
D626493423BD94CE00612E6E /* CompositionAttachmentData.swift */,
|
||||||
|
D60309B42419D4F100A465FF /* ComposeAttachmentsViewController.swift */,
|
||||||
|
D63F9C67241C4F79004C03CF /* AddAttachmentTableViewCell.swift */,
|
||||||
|
D63F9C65241C4CC3004C03CF /* AddAttachmentTableViewCell.xib */,
|
||||||
|
D63F9C69241C50B9004C03CF /* ComposeAttachmentTableViewCell.swift */,
|
||||||
|
D63F9C6A241C50B9004C03CF /* ComposeAttachmentTableViewCell.xib */,
|
||||||
|
);
|
||||||
|
path = Attachments;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
D641C780213DD7C4004B4513 /* Screens */ = {
|
D641C780213DD7C4004B4513 /* Screens */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -818,8 +816,6 @@
|
||||||
D641C785213DD83B004B4513 /* Conversation */,
|
D641C785213DD83B004B4513 /* Conversation */,
|
||||||
D641C786213DD852004B4513 /* Notifications */,
|
D641C786213DD852004B4513 /* Notifications */,
|
||||||
D641C787213DD862004B4513 /* Compose */,
|
D641C787213DD862004B4513 /* Compose */,
|
||||||
D6B053A023BD2BED00A066FA /* Asset Picker */,
|
|
||||||
D627FF77217E94F200CC0648 /* Drafts */,
|
|
||||||
D627943C23A5635D00D38C68 /* Explore */,
|
D627943C23A5635D00D38C68 /* Explore */,
|
||||||
D6BC9DD8232D8BCA002CA326 /* Search */,
|
D6BC9DD8232D8BCA002CA326 /* Search */,
|
||||||
D627944B23A9A02400D38C68 /* Lists */,
|
D627944B23A9A02400D38C68 /* Lists */,
|
||||||
|
@ -891,9 +887,13 @@
|
||||||
D641C787213DD862004B4513 /* Compose */ = {
|
D641C787213DD862004B4513 /* Compose */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
D6B053A023BD2BED00A066FA /* Asset Picker */,
|
||||||
|
D63F9C64241C4CAA004C03CF /* Attachments */,
|
||||||
|
D627FF77217E94F200CC0648 /* Drafts */,
|
||||||
D6A5FAF0217B7E05003DB2D9 /* ComposeViewController.xib */,
|
D6A5FAF0217B7E05003DB2D9 /* ComposeViewController.xib */,
|
||||||
D66362702136338600C9CBA2 /* ComposeViewController.swift */,
|
D66362702136338600C9CBA2 /* ComposeViewController.swift */,
|
||||||
D60309B42419D4F100A465FF /* ComposeAttachmentsViewController.swift */,
|
D6285B5221EA708700FE4B39 /* StatusFormat.swift */,
|
||||||
|
D620483123D2A6A3008A63EF /* CompositionState.swift */,
|
||||||
);
|
);
|
||||||
path = Compose;
|
path = Compose;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1155,9 +1155,8 @@
|
||||||
D627944623A6AC9300D38C68 /* BasicTableViewCell.xib */,
|
D627944623A6AC9300D38C68 /* BasicTableViewCell.xib */,
|
||||||
D67C57A721E2649B00C3118B /* Account Detail */,
|
D67C57A721E2649B00C3118B /* Account Detail */,
|
||||||
D67C57B021E28F9400C3118B /* Compose Status Reply */,
|
D67C57B021E28F9400C3118B /* Compose Status Reply */,
|
||||||
|
D60C07E221E817560057FAA8 /* Compose Media */,
|
||||||
D626494023C122C800612E6E /* Asset Picker */,
|
D626494023C122C800612E6E /* Asset Picker */,
|
||||||
D61959D1241E844900A37B8E /* Attachment Cells */,
|
|
||||||
D61959D0241E842400A37B8E /* Draft Cell */,
|
|
||||||
D641C78A213DD926004B4513 /* Status */,
|
D641C78A213DD926004B4513 /* Status */,
|
||||||
D6C7D27B22B6EBE200071952 /* Attachments */,
|
D6C7D27B22B6EBE200071952 /* Attachments */,
|
||||||
D641C78B213DD92F004B4513 /* Profile Header */,
|
D641C78B213DD92F004B4513 /* Profile Header */,
|
||||||
|
@ -1238,7 +1237,6 @@
|
||||||
D663626021360A9600C9CBA2 /* Preferences */,
|
D663626021360A9600C9CBA2 /* Preferences */,
|
||||||
D6AEBB3F2321640F00E5038B /* Activities */,
|
D6AEBB3F2321640F00E5038B /* Activities */,
|
||||||
D667E5F62135C2ED0057A976 /* Extensions */,
|
D667E5F62135C2ED0057A976 /* Extensions */,
|
||||||
D61959D2241E846D00A37B8E /* Models */,
|
|
||||||
D6F953F121251A2F00CF0F2B /* Controllers */,
|
D6F953F121251A2F00CF0F2B /* Controllers */,
|
||||||
D641C780213DD7C4004B4513 /* Screens */,
|
D641C780213DD7C4004B4513 /* Screens */,
|
||||||
D6BED1722126661300F02DA0 /* Views */,
|
D6BED1722126661300F02DA0 /* Views */,
|
||||||
|
@ -1511,6 +1509,7 @@
|
||||||
D63F9C6C241C50B9004C03CF /* ComposeAttachmentTableViewCell.xib in Resources */,
|
D63F9C6C241C50B9004C03CF /* ComposeAttachmentTableViewCell.xib in Resources */,
|
||||||
D6A3BC812321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.xib in Resources */,
|
D6A3BC812321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.xib in Resources */,
|
||||||
D63F9C66241C4CC3004C03CF /* AddAttachmentTableViewCell.xib in Resources */,
|
D63F9C66241C4CC3004C03CF /* AddAttachmentTableViewCell.xib in Resources */,
|
||||||
|
D60C07E421E8176B0057FAA8 /* ComposeMediaView.xib in Resources */,
|
||||||
D667E5E12134937B0057A976 /* TimelineStatusTableViewCell.xib in Resources */,
|
D667E5E12134937B0057A976 /* TimelineStatusTableViewCell.xib in Resources */,
|
||||||
D6A5FAF1217B7E05003DB2D9 /* ComposeViewController.xib in Resources */,
|
D6A5FAF1217B7E05003DB2D9 /* ComposeViewController.xib in Resources */,
|
||||||
);
|
);
|
||||||
|
@ -1672,6 +1671,7 @@
|
||||||
D627FF7B217E951500CC0648 /* DraftsTableViewController.swift in Sources */,
|
D627FF7B217E951500CC0648 /* DraftsTableViewController.swift in Sources */,
|
||||||
D6A3BC8F2321FFB900FD64D5 /* StatusActionAccountListTableViewController.swift in Sources */,
|
D6A3BC8F2321FFB900FD64D5 /* StatusActionAccountListTableViewController.swift in Sources */,
|
||||||
D6AEBB4823216B1D00E5038B /* AccountActivity.swift in Sources */,
|
D6AEBB4823216B1D00E5038B /* AccountActivity.swift in Sources */,
|
||||||
|
D6333B772138D94E00CE884A /* ComposeMediaView.swift in Sources */,
|
||||||
04ED00B121481ED800567C53 /* SteppedProgressView.swift in Sources */,
|
04ED00B121481ED800567C53 /* SteppedProgressView.swift in Sources */,
|
||||||
D6BC9DDA232D8BE5002CA326 /* SearchResultsViewController.swift in Sources */,
|
D6BC9DDA232D8BE5002CA326 /* SearchResultsViewController.swift in Sources */,
|
||||||
D627FF7F217E95E000CC0648 /* DraftTableViewCell.swift in Sources */,
|
D627FF7F217E95E000CC0648 /* DraftTableViewCell.swift in Sources */,
|
||||||
|
|
|
@ -82,9 +82,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
// This occurs shortly after the scene enters the background, or when its session is discarded.
|
// This occurs shortly after the scene enters the background, or when its session is discarded.
|
||||||
// Release any resources associated with this scene that can be re-created the next time the scene connects.
|
// Release any resources associated with this scene that can be re-created the next time the scene connects.
|
||||||
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
|
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
|
||||||
|
|
||||||
Preferences.save()
|
|
||||||
DraftsManager.save()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func sceneDidBecomeActive(_ scene: UIScene) {
|
func sceneDidBecomeActive(_ scene: UIScene) {
|
||||||
|
@ -95,9 +92,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
func sceneWillResignActive(_ scene: UIScene) {
|
func sceneWillResignActive(_ scene: UIScene) {
|
||||||
// Called when the scene will move from an active state to an inactive state.
|
// Called when the scene will move from an active state to an inactive state.
|
||||||
// This may occur due to temporary interruptions (ex. an incoming phone call).
|
// This may occur due to temporary interruptions (ex. an incoming phone call).
|
||||||
|
|
||||||
Preferences.save()
|
|
||||||
DraftsManager.save()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func sceneWillEnterForeground(_ scene: UIScene) {
|
func sceneWillEnterForeground(_ scene: UIScene) {
|
||||||
|
@ -109,6 +103,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
// Called as the scene transitions from the foreground to the background.
|
// Called as the scene transitions from the foreground to the background.
|
||||||
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
||||||
// to restore the scene back to its current state.
|
// to restore the scene back to its current state.
|
||||||
|
|
||||||
|
Preferences.save()
|
||||||
|
DraftsManager.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
func activateAccount(_ account: LocalData.UserAccountInfo) {
|
func activateAccount(_ account: LocalData.UserAccountInfo) {
|
||||||
|
|
|
@ -177,11 +177,10 @@ class AssetCollectionViewController: UICollectionViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func collectionView(_ collectionView: UICollectionView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
|
override func collectionView(_ collectionView: UICollectionView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
|
||||||
if let indexPath = (configuration.identifier as? NSIndexPath) as IndexPath?,
|
if let indexPath = (configuration.identifier as? NSIndexPath) as IndexPath?, let cell = collectionView.cellForItem(at: indexPath) {
|
||||||
let cell = collectionView.cellForItem(at: indexPath) as? AssetCollectionViewCell {
|
|
||||||
let parameters = UIPreviewParameters()
|
let parameters = UIPreviewParameters()
|
||||||
parameters.backgroundColor = .black
|
parameters.backgroundColor = .black
|
||||||
return UITargetedPreview(view: cell.imageView, parameters: parameters)
|
return UITargetedPreview(view: cell, parameters: parameters)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
|
@ -13,18 +13,14 @@ import AVKit
|
||||||
|
|
||||||
class AssetPreviewViewController: UIViewController {
|
class AssetPreviewViewController: UIViewController {
|
||||||
|
|
||||||
let attachment: CompositionAttachmentData
|
let asset: PHAsset
|
||||||
|
|
||||||
init(attachment: CompositionAttachmentData) {
|
init(asset: PHAsset) {
|
||||||
self.attachment = attachment
|
self.asset = asset
|
||||||
|
|
||||||
super.init(nibName: nil, bundle: nil)
|
super.init(nibName: nil, bundle: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
convenience init(asset: PHAsset) {
|
|
||||||
self.init(attachment: .asset(asset))
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
@ -34,29 +30,21 @@ class AssetPreviewViewController: UIViewController {
|
||||||
|
|
||||||
view.backgroundColor = .black
|
view.backgroundColor = .black
|
||||||
|
|
||||||
switch attachment {
|
if asset.mediaType == .image {
|
||||||
case let .image(image):
|
if asset.mediaSubtypes.contains(.photoLive) {
|
||||||
showImage(image)
|
showLivePhoto()
|
||||||
case let .video(url):
|
} else {
|
||||||
showVideo(asset: AVURLAsset(url: url))
|
showImage()
|
||||||
case let .asset(asset):
|
|
||||||
switch asset.mediaType {
|
|
||||||
case .image:
|
|
||||||
if asset.mediaSubtypes.contains(.photoLive) {
|
|
||||||
showLivePhoto(asset)
|
|
||||||
} else {
|
|
||||||
showAssetImage(asset)
|
|
||||||
}
|
|
||||||
case .video:
|
|
||||||
showAssetVideo(asset)
|
|
||||||
default:
|
|
||||||
fatalError("asset mediaType must be image or video")
|
|
||||||
}
|
}
|
||||||
|
} else if asset.mediaType == .video {
|
||||||
|
playVideo()
|
||||||
|
} else {
|
||||||
|
fatalError("asset mediaType must be image or video")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func showImage(_ image: UIImage) {
|
func showImage() {
|
||||||
let imageView = UIImageView(image: image)
|
let imageView = UIImageView()
|
||||||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
imageView.contentMode = .scaleAspectFit
|
imageView.contentMode = .scaleAspectFit
|
||||||
view.addSubview(imageView)
|
view.addSubview(imageView)
|
||||||
|
@ -66,10 +54,7 @@ class AssetPreviewViewController: UIViewController {
|
||||||
imageView.topAnchor.constraint(equalTo: view.topAnchor),
|
imageView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||||
imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
|
imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
|
||||||
])
|
])
|
||||||
preferredContentSize = image.size
|
|
||||||
}
|
|
||||||
|
|
||||||
func showAssetImage(_ asset: PHAsset) {
|
|
||||||
let options = PHImageRequestOptions()
|
let options = PHImageRequestOptions()
|
||||||
options.version = .current
|
options.version = .current
|
||||||
options.deliveryMode = .opportunistic
|
options.deliveryMode = .opportunistic
|
||||||
|
@ -77,12 +62,13 @@ class AssetPreviewViewController: UIViewController {
|
||||||
options.isNetworkAccessAllowed = true
|
options.isNetworkAccessAllowed = true
|
||||||
PHImageManager.default().requestImage(for: asset, targetSize: view.bounds.size, contentMode: .aspectFit, options: options) { (image, _) in
|
PHImageManager.default().requestImage(for: asset, targetSize: view.bounds.size, contentMode: .aspectFit, options: options) { (image, _) in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.showImage(image!)
|
imageView.image = image
|
||||||
|
self.preferredContentSize = image!.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func showLivePhoto(_ asset: PHAsset) {
|
func showLivePhoto() {
|
||||||
let options = PHLivePhotoRequestOptions()
|
let options = PHLivePhotoRequestOptions()
|
||||||
options.deliveryMode = .opportunistic
|
options.deliveryMode = .opportunistic
|
||||||
options.version = .current
|
options.version = .current
|
||||||
|
@ -110,18 +96,7 @@ class AssetPreviewViewController: UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func showVideo(asset: AVAsset) {
|
func playVideo() {
|
||||||
let playerController = AVPlayerViewController()
|
|
||||||
let item = AVPlayerItem(asset: asset)
|
|
||||||
let player = AVPlayer(playerItem: item)
|
|
||||||
player.isMuted = true
|
|
||||||
player.play()
|
|
||||||
playerController.player = player
|
|
||||||
self.embedChild(playerController)
|
|
||||||
self.preferredContentSize = item.presentationSize
|
|
||||||
}
|
|
||||||
|
|
||||||
func showAssetVideo(_ asset: PHAsset) {
|
|
||||||
let options = PHVideoRequestOptions()
|
let options = PHVideoRequestOptions()
|
||||||
options.deliveryMode = .automatic
|
options.deliveryMode = .automatic
|
||||||
options.isNetworkAccessAllowed = true
|
options.isNetworkAccessAllowed = true
|
||||||
|
@ -131,7 +106,14 @@ class AssetPreviewViewController: UIViewController {
|
||||||
fatalError("failed to get AVAsset")
|
fatalError("failed to get AVAsset")
|
||||||
}
|
}
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.showVideo(asset: avAsset)
|
let playerController = AVPlayerViewController()
|
||||||
|
let item = AVPlayerItem(asset: avAsset)
|
||||||
|
let player = AVPlayer(playerItem: item)
|
||||||
|
player.isMuted = true
|
||||||
|
player.play()
|
||||||
|
playerController.player = player
|
||||||
|
self.embedChild(playerController)
|
||||||
|
self.preferredContentSize = item.presentationSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -82,8 +82,8 @@ class ComposeAttachmentTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
extension ComposeAttachmentTableViewCell: UITextViewDelegate {
|
extension ComposeAttachmentTableViewCell: UITextViewDelegate {
|
||||||
func textViewDidChange(_ textView: UITextView) {
|
func textViewDidChange(_ textView: UITextView) {
|
||||||
|
delegate?.attachmentDescriptionChanged(self)
|
||||||
attachment.attachmentDescription = textView.text
|
attachment.attachmentDescription = textView.text
|
||||||
updateDescriptionPlaceholderLabel()
|
updateDescriptionPlaceholderLabel()
|
||||||
delegate?.attachmentDescriptionChanged(self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -25,14 +25,13 @@ class ComposeAttachmentsViewController: UITableViewController {
|
||||||
var attachments: [CompositionAttachment] = [] {
|
var attachments: [CompositionAttachment] = [] {
|
||||||
didSet {
|
didSet {
|
||||||
delegate?.composeSelectedAttachmentsDidChange()
|
delegate?.composeSelectedAttachmentsDidChange()
|
||||||
delegate?.composeRequiresAttachmentDescriptionsDidChange()
|
|
||||||
updateAddAttachmentsButtonEnabled()
|
updateAddAttachmentsButtonEnabled()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var requiresAttachmentDescriptions: Bool {
|
var requiresAttachmentDescriptions: Bool {
|
||||||
if Preferences.shared.requireAttachmentDescriptions {
|
if Preferences.shared.requireAttachmentDescriptions {
|
||||||
return attachments.contains { $0.attachmentDescription.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }
|
return !attachments.allSatisfy { $0.description.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -265,37 +264,6 @@ class ComposeAttachmentsViewController: UITableViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
|
|
||||||
let attachment = attachments[indexPath.row]
|
|
||||||
// cast to NSIndexPath because identifier needs to conform to NSCopying
|
|
||||||
return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: { () -> UIViewController? in
|
|
||||||
return AssetPreviewViewController(attachment: attachment.data)
|
|
||||||
}) { (_) -> UIMenu? in
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func targetedPreview(forConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
|
|
||||||
if let indexPath = (configuration.identifier as? NSIndexPath) as IndexPath?,
|
|
||||||
let cell = tableView.cellForRow(at: indexPath) as? ComposeAttachmentTableViewCell {
|
|
||||||
let parameters = UIPreviewParameters()
|
|
||||||
parameters.backgroundColor = .black
|
|
||||||
return UITargetedPreview(view: cell.assetImageView, parameters: parameters)
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
|
|
||||||
return targetedPreview(forConfiguration: configuration)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, previewForDismissingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
|
|
||||||
return targetedPreview(forConfiguration: configuration)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Interaction
|
|
||||||
|
|
||||||
func addAttachmentPressed() {
|
func addAttachmentPressed() {
|
||||||
let sheetContainer = AssetPickerSheetContainerViewController()
|
let sheetContainer = AssetPickerSheetContainerViewController()
|
||||||
sheetContainer.assetPicker.assetPickerDelegate = self
|
sheetContainer.assetPicker.assetPickerDelegate = self
|
|
@ -7,7 +7,6 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import SwiftSoup
|
|
||||||
|
|
||||||
class AccountTableViewCell: UITableViewCell {
|
class AccountTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
|
@ -17,7 +16,6 @@ class AccountTableViewCell: UITableViewCell {
|
||||||
@IBOutlet weak var avatarImageView: UIImageView!
|
@IBOutlet weak var avatarImageView: UIImageView!
|
||||||
@IBOutlet weak var displayNameLabel: EmojiLabel!
|
@IBOutlet weak var displayNameLabel: EmojiLabel!
|
||||||
@IBOutlet weak var usernameLabel: UILabel!
|
@IBOutlet weak var usernameLabel: UILabel!
|
||||||
@IBOutlet weak var noteLabel: EmojiLabel!
|
|
||||||
|
|
||||||
var accountID: String!
|
var accountID: String!
|
||||||
|
|
||||||
|
@ -56,10 +54,6 @@ class AccountTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
usernameLabel.text = "@\(account.acct)"
|
usernameLabel.text = "@\(account.acct)"
|
||||||
|
|
||||||
let doc = try! SwiftSoup.parse(account.note)
|
|
||||||
noteLabel.text = try! doc.text()
|
|
||||||
noteLabel.setEmojis(account.emojis, identifier: account.id)
|
|
||||||
|
|
||||||
updateUIForPrefrences()
|
updateUIForPrefrences()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,45 +9,40 @@
|
||||||
<objects>
|
<objects>
|
||||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="100" id="KGk-i7-Jjw" customClass="AccountTableViewCell" customModule="Tusker" customModuleProvider="target">
|
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="AccountTableViewCell" customModule="Tusker" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="100"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="66"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="100"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="66"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Rp2-O5-Vew">
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Rp2-O5-Vew">
|
||||||
<rect key="frame" x="16" y="8" width="50" height="50"/>
|
<rect key="frame" x="16" y="8" width="50" height="50"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" secondItem="Rp2-O5-Vew" secondAttribute="height" multiplier="1:1" id="1AQ-lU-ptd"/>
|
<constraint firstAttribute="width" secondItem="Rp2-O5-Vew" secondAttribute="height" multiplier="1:1" id="1AQ-lU-ptd"/>
|
||||||
<constraint firstAttribute="height" constant="50" id="NqI-m0-owe"/>
|
<constraint firstAttribute="height" priority="999" constant="50" id="NqI-m0-owe"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</imageView>
|
</imageView>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="Iif-9m-vM5">
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="Iif-9m-vM5">
|
||||||
<rect key="frame" x="74" y="11" width="230" height="78"/>
|
<rect key="frame" x="74" y="11" width="230" height="44"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Display Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fhc-bZ-lkB" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Display Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fhc-bZ-lkB" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="230" height="20.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="230" height="26"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="@username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JMo-QH-1is">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="@username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JMo-QH-1is">
|
||||||
<rect key="frame" x="0.0" y="20.5" width="230" height="18"/>
|
<rect key="frame" x="0.0" y="26" width="230" height="18"/>
|
||||||
<fontDescription key="fontDescription" type="system" weight="light" pointSize="15"/>
|
<fontDescription key="fontDescription" type="system" weight="light" pointSize="15"/>
|
||||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Note" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bNO-qR-YEe" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
|
|
||||||
<rect key="frame" x="0.0" y="38.5" width="230" height="39.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
</subviews>
|
||||||
</stackView>
|
</stackView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
<constraints>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="Rp2-O5-Vew" secondAttribute="bottom" constant="8" id="Vw1-OF-tnw"/>
|
||||||
<constraint firstAttribute="bottomMargin" secondItem="Iif-9m-vM5" secondAttribute="bottom" id="dV0-Vm-DUb"/>
|
<constraint firstAttribute="bottomMargin" secondItem="Iif-9m-vM5" secondAttribute="bottom" id="dV0-Vm-DUb"/>
|
||||||
<constraint firstItem="Iif-9m-vM5" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" id="ihr-er-kLO"/>
|
<constraint firstItem="Iif-9m-vM5" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" id="ihr-er-kLO"/>
|
||||||
<constraint firstAttribute="trailingMargin" secondItem="Iif-9m-vM5" secondAttribute="trailing" id="q7a-DT-WPF"/>
|
<constraint firstAttribute="trailingMargin" secondItem="Iif-9m-vM5" secondAttribute="trailing" id="q7a-DT-WPF"/>
|
||||||
|
@ -60,10 +55,9 @@
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="avatarImageView" destination="Rp2-O5-Vew" id="3Gw-Xg-bd5"/>
|
<outlet property="avatarImageView" destination="Rp2-O5-Vew" id="3Gw-Xg-bd5"/>
|
||||||
<outlet property="displayNameLabel" destination="Fhc-bZ-lkB" id="1b0-3k-KR8"/>
|
<outlet property="displayNameLabel" destination="Fhc-bZ-lkB" id="1b0-3k-KR8"/>
|
||||||
<outlet property="noteLabel" destination="bNO-qR-YEe" id="4oO-c0-BOT"/>
|
|
||||||
<outlet property="usernameLabel" destination="JMo-QH-1is" id="ElX-ua-xcQ"/>
|
<outlet property="usernameLabel" destination="JMo-QH-1is" id="ElX-ua-xcQ"/>
|
||||||
</connections>
|
</connections>
|
||||||
<point key="canvasLocation" x="173.91304347826087" y="35.491071428571423"/>
|
<point key="canvasLocation" x="173.91304347826087" y="24.107142857142858"/>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
</objects>
|
</objects>
|
||||||
</document>
|
</document>
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
//
|
||||||
|
// ComposeAttachmentView.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 1/10/19.
|
||||||
|
// Copyright © 2019 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Photos
|
||||||
|
import AVFoundation
|
||||||
|
|
||||||
|
protocol ComposeMediaViewDelegate: class {
|
||||||
|
func didRemoveMedia(_ mediaView: ComposeMediaView)
|
||||||
|
func descriptionTextViewDidChange(_ mediaView: ComposeMediaView)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ComposeMediaView: UIView {
|
||||||
|
|
||||||
|
weak var delegate: ComposeMediaViewDelegate?
|
||||||
|
|
||||||
|
@IBOutlet weak var imageView: UIImageView!
|
||||||
|
@IBOutlet weak var descriptionTextView: UITextView!
|
||||||
|
@IBOutlet weak var placeholderLabel: UILabel!
|
||||||
|
|
||||||
|
var attachment: CompositionAttachmentData!
|
||||||
|
|
||||||
|
static func create() -> ComposeMediaView {
|
||||||
|
return UINib(nibName: "ComposeMediaView", bundle: nil).instantiate(withOwner: nil, options: nil).first as! ComposeMediaView
|
||||||
|
}
|
||||||
|
|
||||||
|
override func awakeFromNib() {
|
||||||
|
super.awakeFromNib()
|
||||||
|
|
||||||
|
imageView.layer.masksToBounds = true
|
||||||
|
imageView.layer.cornerRadius = 10 // 0.1 * imageView.frame.width
|
||||||
|
|
||||||
|
descriptionTextView.delegate = self
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(attachment: CompositionAttachmentData) {
|
||||||
|
self.attachment = attachment
|
||||||
|
|
||||||
|
switch attachment {
|
||||||
|
case let .image(image):
|
||||||
|
imageView.image = image
|
||||||
|
case let .asset(asset):
|
||||||
|
let size = CGSize(width: 80, height: 80)
|
||||||
|
PHImageManager.default().requestImage(for: asset, targetSize: size, contentMode: .aspectFill, options: nil) { (image, _) in
|
||||||
|
guard self.attachment == attachment else { return }
|
||||||
|
self.imageView.image = image
|
||||||
|
}
|
||||||
|
case let .video(url):
|
||||||
|
let asset = AVURLAsset(url: url)
|
||||||
|
let imageGenerator = AVAssetImageGenerator(asset: asset)
|
||||||
|
if let cgImage = try? imageGenerator.copyCGImage(at: .zero, actualTime: nil) {
|
||||||
|
imageView.image = UIImage(cgImage: cgImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Interaction
|
||||||
|
|
||||||
|
@IBAction func removePressed(_ sender: Any) {
|
||||||
|
delegate?.didRemoveMedia(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ComposeMediaView: UITextViewDelegate {
|
||||||
|
func textViewDidChange(_ textView: UITextView) {
|
||||||
|
placeholderLabel.isHidden = !descriptionTextView.text.isEmpty
|
||||||
|
delegate?.descriptionTextViewDidChange(self)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
|
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||||
|
<dependencies>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||||
|
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="ComposeMediaView" customModule="Tusker" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="u7I-sx-kUe">
|
||||||
|
<rect key="frame" x="8" y="293.5" width="80" height="80"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="80" id="CgF-eC-We6"/>
|
||||||
|
<constraint firstAttribute="width" constant="80" id="S3g-yM-TRb"/>
|
||||||
|
</constraints>
|
||||||
|
</imageView>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="G1g-Lw-Ren">
|
||||||
|
<rect key="frame" x="345" y="322.5" width="22" height="22"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="22" id="dyG-5Y-91s"/>
|
||||||
|
<constraint firstAttribute="width" constant="22" id="sWQ-3z-Z5r"/>
|
||||||
|
</constraints>
|
||||||
|
<state key="normal" image="xmark.circle.fill" catalog="system">
|
||||||
|
<preferredSymbolConfiguration key="preferredSymbolConfiguration" scale="large"/>
|
||||||
|
</state>
|
||||||
|
<connections>
|
||||||
|
<action selector="removePressed:" destination="iN0-l3-epB" eventType="touchUpInside" id="aor-Cq-YjJ"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="O6b-Zs-u8r">
|
||||||
|
<rect key="frame" x="96" y="0.0" width="241" height="667"/>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="80" id="GsE-uM-fhe"/>
|
||||||
|
</constraints>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||||
|
</textView>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Describe for the visually impaired..." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rkD-NP-09H">
|
||||||
|
<rect key="frame" x="100" y="8" width="233" height="41"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0h4-wv-2R8">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="1"/>
|
||||||
|
<color key="backgroundColor" systemColor="separatorColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.28999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="1" id="aQa-2T-uYY"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="O6b-Zs-u8r" secondAttribute="bottom" id="3sv-wo-gxe"/>
|
||||||
|
<constraint firstItem="u7I-sx-kUe" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="5Qs-i7-glv"/>
|
||||||
|
<constraint firstItem="u7I-sx-kUe" firstAttribute="top" relation="greaterThanOrEqual" secondItem="0h4-wv-2R8" secondAttribute="bottom" constant="8" id="86L-Cb-Lsk"/>
|
||||||
|
<constraint firstItem="O6b-Zs-u8r" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" id="9QY-MR-Yc2"/>
|
||||||
|
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="0h4-wv-2R8" secondAttribute="trailing" id="FCS-un-JT6"/>
|
||||||
|
<constraint firstItem="u7I-sx-kUe" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="8" id="UWq-Lf-zGB"/>
|
||||||
|
<constraint firstItem="rkD-NP-09H" firstAttribute="leading" secondItem="O6b-Zs-u8r" secondAttribute="leading" constant="4" id="aQd-T9-n1I"/>
|
||||||
|
<constraint firstItem="0h4-wv-2R8" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" id="edp-4a-YGq"/>
|
||||||
|
<constraint firstItem="G1g-Lw-Ren" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="hJA-JF-MHp"/>
|
||||||
|
<constraint firstItem="O6b-Zs-u8r" firstAttribute="leading" secondItem="u7I-sx-kUe" secondAttribute="trailing" constant="8" id="hvZ-m4-TOV"/>
|
||||||
|
<constraint firstItem="G1g-Lw-Ren" firstAttribute="leading" secondItem="O6b-Zs-u8r" secondAttribute="trailing" constant="8" id="ith-0X-3Yz"/>
|
||||||
|
<constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="u7I-sx-kUe" secondAttribute="bottom" constant="8" id="lGN-Qg-mBO"/>
|
||||||
|
<constraint firstItem="rkD-NP-09H" firstAttribute="top" secondItem="O6b-Zs-u8r" secondAttribute="top" constant="8" id="mRb-3M-7uz"/>
|
||||||
|
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="G1g-Lw-Ren" secondAttribute="trailing" constant="8" id="o4F-MV-ahd"/>
|
||||||
|
<constraint firstItem="0h4-wv-2R8" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="sFo-wp-MYP"/>
|
||||||
|
<constraint firstItem="O6b-Zs-u8r" firstAttribute="trailing" secondItem="rkD-NP-09H" secondAttribute="trailing" constant="4" id="vwg-7l-8ca"/>
|
||||||
|
</constraints>
|
||||||
|
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="descriptionTextView" destination="O6b-Zs-u8r" id="TNy-7h-0sY"/>
|
||||||
|
<outlet property="imageView" destination="u7I-sx-kUe" id="o3a-O0-m85"/>
|
||||||
|
<outlet property="placeholderLabel" destination="rkD-NP-09H" id="WtV-2h-L3n"/>
|
||||||
|
</connections>
|
||||||
|
<point key="canvasLocation" x="136.80000000000001" y="142.57871064467767"/>
|
||||||
|
</view>
|
||||||
|
</objects>
|
||||||
|
<resources>
|
||||||
|
<image name="xmark.circle.fill" catalog="system" width="64" height="60"/>
|
||||||
|
</resources>
|
||||||
|
</document>
|
|
@ -101,10 +101,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateTimestamp() {
|
func updateTimestamp() {
|
||||||
// if the mastodonController is nil (i.e. the delegate is nil), then the screen this cell was a part of has been deallocated
|
guard let mastodonController = mastodonController, let status = mastodonController.cache.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") }
|
||||||
// so we bail out immediately, since there's nothing to update
|
|
||||||
guard let mastodonController = mastodonController else { return }
|
|
||||||
guard let status = mastodonController.cache.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") }
|
|
||||||
|
|
||||||
timestampLabel.text = status.createdAt.timeAgoString()
|
timestampLabel.text = status.createdAt.timeAgoString()
|
||||||
timestampLabel.accessibilityLabel = TimelineStatusTableViewCell.relativeDateFormatter.localizedString(for: status.createdAt, relativeTo: Date())
|
timestampLabel.accessibilityLabel = TimelineStatusTableViewCell.relativeDateFormatter.localizedString(for: status.createdAt, relativeTo: Date())
|
||||||
|
|
Loading…
Reference in New Issue