Compare commits

..

9 Commits

Author SHA1 Message Date
Shadowfacts 804636dcbb
Don't show warning when loading draft on top of for empty statuses
Closes #87
2020-02-28 19:50:04 -05:00
Shadowfacts 5bed38f661
Show gallery instead of large image when previewing status attachments
Fixes crash when attempting to preview audio/video attachments
2020-02-28 19:47:38 -05:00
Shadowfacts 56de0ab359
Update profile header to always reflect most recently cached data 2020-02-28 19:47:31 -05:00
Shadowfacts 387623a309
Remove old code 2020-02-28 19:24:14 -05:00
Shadowfacts 70bca052c4
Tweak notification grouping
Notifications that are of the same type but are separated by a groupable
notification of a different type are now considered groupable. For
example:

favorite 1 (status 1)
reblog 1 (status 1)
favorite 2 (status 1)
reblog 2 (status 1)
mention 1
reblog 3 (status 1)

will be grouped into:

favorite 1, 2 (status 1)
reblog 1, 2 (status 1)
mention 1
reblog 3 (status 1)
2020-02-28 19:21:39 -05:00
Shadowfacts d9bae42f81
Prevent empty drafts from being saved 2020-02-22 15:43:17 -05:00
Shadowfacts a814ee37cc
Update SheetController
Fixes image picker losing velocity during dismiss animation
2020-02-22 15:29:42 -05:00
Shadowfacts 1a8e84f5fa
Reorganize behavior preferences 2020-02-22 13:19:31 -05:00
Shadowfacts 1f56823a17
Add preference to disable gif animation in timelines 2020-02-22 13:12:28 -05:00
16 changed files with 193 additions and 154 deletions

View File

@ -68,11 +68,6 @@ public class Client {
completion(.failure(Error.invalidModel)) completion(.failure(Error.invalidModel))
return return
} }
if var result = result as? ClientModel {
result.client = self
} else if var result = result as? [ClientModel] {
result.client = self
}
let pagination = response.allHeaderFields["Link"].flatMap { $0 as? String }.flatMap(Pagination.init) let pagination = response.allHeaderFields["Link"].flatMap { $0 as? String }.flatMap(Pagination.init)
completion(.success(result, pagination)) completion(.success(result, pagination))

View File

@ -1,39 +0,0 @@
//
// ClientModel.swift
// Pachyderm
//
// Created by Shadowfacts on 9/9/18.
// Copyright © 2018 Shadowfacts. All rights reserved.
//
import Foundation
protocol ClientModel {
var client: Client! { get set }
}
extension Array where Element == ClientModel {
var client: Client! {
get {
return first?.client
}
set {
for var el in self {
el.client = newValue
}
}
}
}
extension Array where Element: ClientModel {
var client: Client! {
get {
return first?.client
}
set {
for var el in self {
el.client = newValue
}
}
}
}

View File

@ -27,18 +27,24 @@ public class NotificationGroup {
} }
public static func createGroups(notifications: [Notification], only allowedTypes: [Notification.Kind]) -> [NotificationGroup] { public static func createGroups(notifications: [Notification], only allowedTypes: [Notification.Kind]) -> [NotificationGroup] {
return notifications.reduce(into: [[Notification]]()) { (groups, notification) in var groups = [[Notification]]()
if allowedTypes.contains(notification.kind), for notification in notifications {
let lastGroup = groups.last, if allowedTypes.contains(notification.kind) {
let firstStatus = lastGroup.first, if let lastGroup = groups.last, let firstNotification = lastGroup.first, firstNotification.kind == notification.kind, firstNotification.status?.id == notification.status?.id {
firstStatus.kind == notification.kind,
firstStatus.status?.id == notification.status?.id {
groups[groups.count - 1].append(notification) groups[groups.count - 1].append(notification)
} else { continue
} else if groups.count >= 2 {
let secondToLastGroup = groups[groups.count - 2]
if allowedTypes.contains(groups[groups.count - 1][0].kind), let firstNotification = secondToLastGroup.first, firstNotification.kind == notification.kind, firstNotification.status?.id == notification.status?.id {
groups[groups.count - 2].append(notification)
continue
}
}
}
groups.append([notification]) groups.append([notification])
} }
}.map { return groups.map {
NotificationGroup(notifications: $0)! NotificationGroup(notifications: $0)!
} }
} }

View File

@ -61,7 +61,6 @@
D6109A05214572BF00432DC2 /* Scope.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6109A04214572BF00432DC2 /* Scope.swift */; }; D6109A05214572BF00432DC2 /* Scope.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6109A04214572BF00432DC2 /* Scope.swift */; };
D6109A072145756700432DC2 /* LoginSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6109A062145756700432DC2 /* LoginSettings.swift */; }; D6109A072145756700432DC2 /* LoginSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6109A062145756700432DC2 /* LoginSettings.swift */; };
D6109A0921458C4A00432DC2 /* Empty.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6109A0821458C4A00432DC2 /* Empty.swift */; }; D6109A0921458C4A00432DC2 /* Empty.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6109A0821458C4A00432DC2 /* Empty.swift */; };
D6109A0B2145953C00432DC2 /* ClientModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6109A0A2145953C00432DC2 /* ClientModel.swift */; };
D6109A0D214599E100432DC2 /* RequestRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6109A0C214599E100432DC2 /* RequestRange.swift */; }; D6109A0D214599E100432DC2 /* RequestRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6109A0C214599E100432DC2 /* RequestRange.swift */; };
D6109A0F21459B6900432DC2 /* Pagination.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6109A0E21459B6900432DC2 /* Pagination.swift */; }; D6109A0F21459B6900432DC2 /* Pagination.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6109A0E21459B6900432DC2 /* Pagination.swift */; };
D6109A11214607D500432DC2 /* Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6109A10214607D500432DC2 /* Timeline.swift */; }; D6109A11214607D500432DC2 /* Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6109A10214607D500432DC2 /* Timeline.swift */; };
@ -156,6 +155,8 @@
D67C57AF21E28EAD00C3118B /* Array+Uniques.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67C57AE21E28EAD00C3118B /* Array+Uniques.swift */; }; D67C57AF21E28EAD00C3118B /* Array+Uniques.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67C57AE21E28EAD00C3118B /* Array+Uniques.swift */; };
D67C57B221E28FAD00C3118B /* ComposeStatusReplyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D67C57B121E28FAD00C3118B /* ComposeStatusReplyView.xib */; }; D67C57B221E28FAD00C3118B /* ComposeStatusReplyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D67C57B121E28FAD00C3118B /* ComposeStatusReplyView.xib */; };
D67C57B421E2910700C3118B /* ComposeStatusReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67C57B321E2910700C3118B /* ComposeStatusReplyView.swift */; }; D67C57B421E2910700C3118B /* ComposeStatusReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67C57B321E2910700C3118B /* ComposeStatusReplyView.swift */; };
D68015402401A6BA00D6103B /* ComposingPrefsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D680153F2401A6BA00D6103B /* ComposingPrefsView.swift */; };
D68015422401A74600D6103B /* MediaPrefsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68015412401A74600D6103B /* MediaPrefsView.swift */; };
D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */; }; D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */; };
D693DE5723FE1A6A0061E07D /* EnhancedNavigationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693DE5623FE1A6A0061E07D /* EnhancedNavigationViewController.swift */; }; D693DE5723FE1A6A0061E07D /* EnhancedNavigationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693DE5623FE1A6A0061E07D /* EnhancedNavigationViewController.swift */; };
D693DE5923FE24310061E07D /* InteractivePushTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693DE5823FE24300061E07D /* InteractivePushTransition.swift */; }; D693DE5923FE24310061E07D /* InteractivePushTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693DE5823FE24300061E07D /* InteractivePushTransition.swift */; };
@ -338,7 +339,6 @@
D6109A04214572BF00432DC2 /* Scope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scope.swift; sourceTree = "<group>"; }; D6109A04214572BF00432DC2 /* Scope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scope.swift; sourceTree = "<group>"; };
D6109A062145756700432DC2 /* LoginSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginSettings.swift; sourceTree = "<group>"; }; D6109A062145756700432DC2 /* LoginSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginSettings.swift; sourceTree = "<group>"; };
D6109A0821458C4A00432DC2 /* Empty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Empty.swift; sourceTree = "<group>"; }; D6109A0821458C4A00432DC2 /* Empty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Empty.swift; sourceTree = "<group>"; };
D6109A0A2145953C00432DC2 /* ClientModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientModel.swift; sourceTree = "<group>"; };
D6109A0C214599E100432DC2 /* RequestRange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestRange.swift; sourceTree = "<group>"; }; D6109A0C214599E100432DC2 /* RequestRange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestRange.swift; sourceTree = "<group>"; };
D6109A0E21459B6900432DC2 /* Pagination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pagination.swift; sourceTree = "<group>"; }; D6109A0E21459B6900432DC2 /* Pagination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pagination.swift; sourceTree = "<group>"; };
D6109A10214607D500432DC2 /* Timeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Timeline.swift; sourceTree = "<group>"; }; D6109A10214607D500432DC2 /* Timeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Timeline.swift; sourceTree = "<group>"; };
@ -433,6 +433,8 @@
D67C57AE21E28EAD00C3118B /* Array+Uniques.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Uniques.swift"; sourceTree = "<group>"; }; D67C57AE21E28EAD00C3118B /* Array+Uniques.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Uniques.swift"; sourceTree = "<group>"; };
D67C57B121E28FAD00C3118B /* ComposeStatusReplyView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ComposeStatusReplyView.xib; sourceTree = "<group>"; }; D67C57B121E28FAD00C3118B /* ComposeStatusReplyView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ComposeStatusReplyView.xib; sourceTree = "<group>"; };
D67C57B321E2910700C3118B /* ComposeStatusReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusReplyView.swift; sourceTree = "<group>"; }; D67C57B321E2910700C3118B /* ComposeStatusReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusReplyView.swift; sourceTree = "<group>"; };
D680153F2401A6BA00D6103B /* ComposingPrefsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposingPrefsView.swift; sourceTree = "<group>"; };
D68015412401A74600D6103B /* MediaPrefsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPrefsView.swift; sourceTree = "<group>"; };
D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedPageViewController.swift; sourceTree = "<group>"; }; D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedPageViewController.swift; sourceTree = "<group>"; };
D693DE5623FE1A6A0061E07D /* EnhancedNavigationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnhancedNavigationViewController.swift; sourceTree = "<group>"; }; D693DE5623FE1A6A0061E07D /* EnhancedNavigationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnhancedNavigationViewController.swift; sourceTree = "<group>"; };
D693DE5823FE24300061E07D /* InteractivePushTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractivePushTransition.swift; sourceTree = "<group>"; }; D693DE5823FE24300061E07D /* InteractivePushTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractivePushTransition.swift; sourceTree = "<group>"; };
@ -598,7 +600,6 @@
D61099AD2144B0CC00432DC2 /* Pachyderm.h */, D61099AD2144B0CC00432DC2 /* Pachyderm.h */,
D61099AE2144B0CC00432DC2 /* Info.plist */, D61099AE2144B0CC00432DC2 /* Info.plist */,
D61099C82144B13C00432DC2 /* Client.swift */, D61099C82144B13C00432DC2 /* Client.swift */,
D6109A0A2145953C00432DC2 /* ClientModel.swift */,
D6A3BC7223218C6E00FD64D5 /* Utilities */, D6A3BC7223218C6E00FD64D5 /* Utilities */,
D61099D72144B74500432DC2 /* Extensions */, D61099D72144B74500432DC2 /* Extensions */,
D61099CC2144B2C300432DC2 /* Request */, D61099CC2144B2C300432DC2 /* Request */,
@ -882,6 +883,8 @@
04586B4022B2FFB10021BD04 /* PreferencesView.swift */, 04586B4022B2FFB10021BD04 /* PreferencesView.swift */,
04586B4222B301470021BD04 /* AppearancePrefsView.swift */, 04586B4222B301470021BD04 /* AppearancePrefsView.swift */,
0427033722B30F5F000D31B6 /* BehaviorPrefsView.swift */, 0427033722B30F5F000D31B6 /* BehaviorPrefsView.swift */,
D680153F2401A6BA00D6103B /* ComposingPrefsView.swift */,
D68015412401A74600D6103B /* MediaPrefsView.swift */,
D6BC9DB2232D4C07002CA326 /* WellnessPrefsView.swift */, D6BC9DB2232D4C07002CA326 /* WellnessPrefsView.swift */,
0427033922B31269000D31B6 /* AdvancedPrefsView.swift */, 0427033922B31269000D31B6 /* AdvancedPrefsView.swift */,
0427037B22B316B9000D31B6 /* SilentActionPrefs.swift */, 0427037B22B316B9000D31B6 /* SilentActionPrefs.swift */,
@ -1539,7 +1542,6 @@
D61099FB214569F600432DC2 /* Report.swift in Sources */, D61099FB214569F600432DC2 /* Report.swift in Sources */,
D61099F92145698900432DC2 /* Relationship.swift in Sources */, D61099F92145698900432DC2 /* Relationship.swift in Sources */,
D61099E12144C1DC00432DC2 /* Account.swift in Sources */, D61099E12144C1DC00432DC2 /* Account.swift in Sources */,
D6109A0B2145953C00432DC2 /* ClientModel.swift in Sources */,
D61099E92145658300432DC2 /* Card.swift in Sources */, D61099E92145658300432DC2 /* Card.swift in Sources */,
D61099F32145688600432DC2 /* Mention.swift in Sources */, D61099F32145688600432DC2 /* Mention.swift in Sources */,
D6109A0F21459B6900432DC2 /* Pagination.swift in Sources */, D6109A0F21459B6900432DC2 /* Pagination.swift in Sources */,
@ -1688,6 +1690,7 @@
04586B4122B2FFB10021BD04 /* PreferencesView.swift in Sources */, 04586B4122B2FFB10021BD04 /* PreferencesView.swift in Sources */,
D620483223D2A6A3008A63EF /* CompositionState.swift in Sources */, D620483223D2A6A3008A63EF /* CompositionState.swift in Sources */,
D6BC9DB5232D4CE3002CA326 /* NotificationsMode.swift in Sources */, D6BC9DB5232D4CE3002CA326 /* NotificationsMode.swift in Sources */,
D68015402401A6BA00D6103B /* ComposingPrefsView.swift in Sources */,
D667E5EB21349EF80057A976 /* ProfileHeaderTableViewCell.swift in Sources */, D667E5EB21349EF80057A976 /* ProfileHeaderTableViewCell.swift in Sources */,
04D14BB022B34A2800642648 /* GalleryViewController.swift in Sources */, 04D14BB022B34A2800642648 /* GalleryViewController.swift in Sources */,
D641C773213CAA25004B4513 /* NotificationsTableViewController.swift in Sources */, D641C773213CAA25004B4513 /* NotificationsTableViewController.swift in Sources */,
@ -1698,6 +1701,7 @@
D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */, D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */,
D61AC1D5232E9FA600C54D2D /* InstanceSelectorTableViewController.swift in Sources */, D61AC1D5232E9FA600C54D2D /* InstanceSelectorTableViewController.swift in Sources */,
D626493F23C101C500612E6E /* AlbumAssetCollectionViewController.swift in Sources */, D626493F23C101C500612E6E /* AlbumAssetCollectionViewController.swift in Sources */,
D68015422401A74600D6103B /* MediaPrefsView.swift in Sources */,
D6757A7E2157E02600721E32 /* XCBRequestSpec.swift in Sources */, D6757A7E2157E02600721E32 /* XCBRequestSpec.swift in Sources */,
D667E5F12134D5050057A976 /* UIViewController+Delegates.swift in Sources */, D667E5F12134D5050057A976 /* UIViewController+Delegates.swift in Sources */,
D6BC8748219738E1006163F1 /* EnhancedTableViewController.swift in Sources */, D6BC8748219738E1006163F1 /* EnhancedTableViewController.swift in Sources */,

View File

@ -6,7 +6,7 @@
"repositoryURL": "https://git.shadowfacts.net/shadowfacts/SheetController.git", "repositoryURL": "https://git.shadowfacts.net/shadowfacts/SheetController.git",
"state": { "state": {
"branch": "master", "branch": "master",
"revision": "6ee1ad24ec8620f5c17416d6141643f0787708ba", "revision": "6926446c4e15eb7f4513c4c00df9279553b330be",
"version": null "version": null
} }
} }

View File

@ -10,31 +10,6 @@ import Foundation
extension Date { extension Date {
// var timeAgo: String {
// let calendar = NSCalendar.current
// let unitFlags = Set<Calendar.Component>([.second, .minute, .hour, .day, .weekOfYear, .month, .year])
//
// let components = calendar.dateComponents(unitFlags, from: self, to: Date())
//
// if components.year! >= 1 {
// return "\(components.year!)y"
// } else if components.month! >= 1 {
// return "\(components.month!)mo"
// } else if components.weekOfYear! >= 1 {
// return "\(components.weekOfYear!)w"
// } else if components.day! >= 1 {
// return "\(components.day!)d"
// } else if components.hour! >= 1 {
// return "\(components.hour!)h"
// } else if components.minute! >= 1 {
// return "\(components.minute!)m"
// } else if components.second! >= 3 {
// return "\(components.second!)s"
// } else {
// return "Now"
// }
// }
func timeAgo() -> (Int, Calendar.Component) { func timeAgo() -> (Int, Calendar.Component) {
let calendar = NSCalendar.current let calendar = NSCalendar.current
let unitFlags = Set<Calendar.Component>([.second, .minute, .hour, .day, .weekOfYear, .month, .year]) let unitFlags = Set<Calendar.Component>([.second, .minute, .hour, .day, .weekOfYear, .month, .year])

View File

@ -48,6 +48,7 @@ class Preferences: Codable, ObservableObject {
self.contentWarningCopyMode = try container.decode(ContentWarningCopyMode.self, forKey: .contentWarningCopyMode) self.contentWarningCopyMode = try container.decode(ContentWarningCopyMode.self, forKey: .contentWarningCopyMode)
self.mentionReblogger = try container.decode(Bool.self, forKey: .mentionReblogger) self.mentionReblogger = try container.decode(Bool.self, forKey: .mentionReblogger)
self.blurAllMedia = try container.decode(Bool.self, forKey: .blurAllMedia) self.blurAllMedia = try container.decode(Bool.self, forKey: .blurAllMedia)
self.automaticallyPlayGifs = try container.decode(Bool.self, forKey: .automaticallyPlayGifs)
self.openLinksInApps = try container.decode(Bool.self, forKey: .openLinksInApps) self.openLinksInApps = try container.decode(Bool.self, forKey: .openLinksInApps)
self.useInAppSafari = try container.decode(Bool.self, forKey: .useInAppSafari) self.useInAppSafari = try container.decode(Bool.self, forKey: .useInAppSafari)
self.inAppSafariAutomaticReaderMode = try container.decode(Bool.self, forKey: .inAppSafariAutomaticReaderMode) self.inAppSafariAutomaticReaderMode = try container.decode(Bool.self, forKey: .inAppSafariAutomaticReaderMode)
@ -73,6 +74,7 @@ class Preferences: Codable, ObservableObject {
try container.encode(contentWarningCopyMode, forKey: .contentWarningCopyMode) try container.encode(contentWarningCopyMode, forKey: .contentWarningCopyMode)
try container.encode(mentionReblogger, forKey: .mentionReblogger) try container.encode(mentionReblogger, forKey: .mentionReblogger)
try container.encode(blurAllMedia, forKey: .blurAllMedia) try container.encode(blurAllMedia, forKey: .blurAllMedia)
try container.encode(automaticallyPlayGifs, forKey: .automaticallyPlayGifs)
try container.encode(openLinksInApps, forKey: .openLinksInApps) try container.encode(openLinksInApps, forKey: .openLinksInApps)
try container.encode(useInAppSafari, forKey: .useInAppSafari) try container.encode(useInAppSafari, forKey: .useInAppSafari)
try container.encode(inAppSafariAutomaticReaderMode, forKey: .inAppSafariAutomaticReaderMode) try container.encode(inAppSafariAutomaticReaderMode, forKey: .inAppSafariAutomaticReaderMode)
@ -97,6 +99,7 @@ class Preferences: Codable, ObservableObject {
@Published var contentWarningCopyMode = ContentWarningCopyMode.asIs @Published var contentWarningCopyMode = ContentWarningCopyMode.asIs
@Published var mentionReblogger = false @Published var mentionReblogger = false
@Published var blurAllMedia = false @Published var blurAllMedia = false
@Published var automaticallyPlayGifs = true
@Published var openLinksInApps = true @Published var openLinksInApps = true
@Published var useInAppSafari = true @Published var useInAppSafari = true
@Published var inAppSafariAutomaticReaderMode = false @Published var inAppSafariAutomaticReaderMode = false
@ -121,6 +124,7 @@ class Preferences: Codable, ObservableObject {
case contentWarningCopyMode case contentWarningCopyMode
case mentionReblogger case mentionReblogger
case blurAllMedia case blurAllMedia
case automaticallyPlayGifs
case openLinksInApps case openLinksInApps
case useInAppSafari case useInAppSafari
case inAppSafariAutomaticReaderMode case inAppSafariAutomaticReaderMode

View File

@ -40,13 +40,6 @@ class AssetPickerSheetContainerViewController: SheetContainerViewController {
} }
extension AssetPickerSheetContainerViewController: SheetContainerViewControllerDelegate { extension AssetPickerSheetContainerViewController: SheetContainerViewControllerDelegate {
func sheetContainer(_ sheetContainer: SheetContainerViewController, willSnapToDetent detent: Detent) -> Bool {
if detent == .bottom {
dismiss(animated: true)
return false
}
return true
}
func sheetContainerContentScrollView(_ sheetContainer: SheetContainerViewController) -> UIScrollView? { func sheetContainerContentScrollView(_ sheetContainer: SheetContainerViewController) -> UIScrollView? {
if let vc = assetPicker.visibleViewController as? UITableViewController { if let vc = assetPicker.visibleViewController as? UITableViewController {
return vc.tableView return vc.tableView

View File

@ -367,12 +367,21 @@ class ComposeViewController: UIViewController {
let description = mediaView.descriptionTextView.text ?? "" let description = mediaView.descriptionTextView.text ?? ""
attachments.append(.init(attachment: attachment, description: description)) attachments.append(.init(attachment: attachment, description: description))
} }
let cw = contentWarningEnabled ? contentWarningTextField.text : nil let statusText = statusTextView.text.trimmingCharacters(in: .whitespacesAndNewlines)
let cw = contentWarningEnabled ? contentWarningTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) : nil
let account = mastodonController.accountInfo! let account = mastodonController.accountInfo!
if attachments.count == 0, statusText.isEmpty, cw?.isEmpty ?? true {
if let currentDraft = self.currentDraft { if let currentDraft = self.currentDraft {
currentDraft.update(accountID: account.id, text: self.statusTextView.text, contentWarning: cw, attachments: attachments) DraftsManager.shared.remove(currentDraft)
} else { } else {
self.currentDraft = DraftsManager.shared.create(accountID: account.id, text: self.statusTextView.text, contentWarning: cw, inReplyToID: inReplyToID, attachments: attachments) return
}
} else {
if let currentDraft = self.currentDraft {
currentDraft.update(accountID: account.id, text: statusText, contentWarning: cw, attachments: attachments)
} else {
self.currentDraft = DraftsManager.shared.create(accountID: account.id, text: statusText, contentWarning: cw, inReplyToID: inReplyToID, attachments: attachments)
}
} }
DraftsManager.save() DraftsManager.save()
} }
@ -623,7 +632,7 @@ extension ComposeViewController: DraftsTableViewControllerDelegate {
} }
func shouldSelectDraft(_ draft: DraftsManager.Draft, completion: @escaping (Bool) -> Void) { func shouldSelectDraft(_ draft: DraftsManager.Draft, completion: @escaping (Bool) -> Void) {
if draft.inReplyToID != self.inReplyToID { if draft.inReplyToID != self.inReplyToID, hasChanges {
let alertController = UIAlertController(title: "Different Reply", message: "The selected draft is a reply to a different status, do you wish to use it?", preferredStyle: .alert) let alertController = UIAlertController(title: "Different Reply", message: "The selected draft is a reply to a different status, do you wish to use it?", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (_) in alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (_) in
completion(false) completion(false)
@ -640,6 +649,10 @@ extension ComposeViewController: DraftsTableViewControllerDelegate {
} }
func draftSelected(_ draft: DraftsManager.Draft) { func draftSelected(_ draft: DraftsManager.Draft) {
if hasChanges {
saveDraft()
}
self.currentDraft = draft self.currentDraft = draft
inReplyToID = draft.inReplyToID inReplyToID = draft.inReplyToID

View File

@ -13,54 +13,8 @@ struct BehaviorPrefsView: View {
var body: some View { var body: some View {
List { List {
composingSection
replyingSection
readingSection
linksSection linksSection
}.listStyle(GroupedListStyle()) }.listStyle(GroupedListStyle()).navigationBarTitle(Text("Behavior"))
.navigationBarTitle(Text("Behavior"))
}
var composingSection: some View {
Section(header: Text("COMPOSING")) {
Picker(selection: $preferences.defaultPostVisibility, label: Text("Default Post Visibility")) {
ForEach(Status.Visibility.allCases, id: \.self) { visibility in
HStack {
Image(systemName: visibility.imageName)
Text(visibility.displayName)
}
.tag(visibility)
}//.navigationBarTitle("Default Post Visibility")
// navbar title on the ForEach is currently incorrectly applied when the picker is not expanded, see FB6838291
}
Toggle(isOn: $preferences.automaticallySaveDrafts) {
Text("Automatically Save Drafts")
}
Toggle(isOn: $preferences.requireAttachmentDescriptions) {
Text("Require Attachment Descriptions")
}
}
}
var replyingSection: some View {
Section(header: Text("REPLYING")) {
Picker(selection: $preferences.contentWarningCopyMode, label: Text("Content Warning Copy Style")) {
Text("As-is").tag(ContentWarningCopyMode.asIs)
Text("Prepend 're: '").tag(ContentWarningCopyMode.prependRe)
Text("Don't copy").tag(ContentWarningCopyMode.doNotCopy)
}
Toggle(isOn: $preferences.mentionReblogger) {
Text("Mention Reblogger")
}
}
}
var readingSection: some View {
Section(header: Text("READING")) {
Toggle(isOn: $preferences.blurAllMedia) {
Text("Blur All Media")
}
}
} }
var linksSection: some View { var linksSection: some View {

View File

@ -0,0 +1,62 @@
//
// ComposingPrefsView.swift
// Tusker
//
// Created by Shadowfacts on 2/22/20.
// Copyright © 2020 Shadowfacts. All rights reserved.
//
import SwiftUI
import Pachyderm
struct ComposingPrefsView: View {
@ObservedObject var preferences = Preferences.shared
var body: some View {
List {
composingSection
replyingSection
}.listStyle(GroupedListStyle()).navigationBarTitle("Composing")
}
var composingSection: some View {
Section(header: Text("COMPOSING")) {
Picker(selection: $preferences.defaultPostVisibility, label: Text("Default Post Visibility")) {
ForEach(Status.Visibility.allCases, id: \.self) { visibility in
HStack {
Image(systemName: visibility.imageName)
Text(visibility.displayName)
}
.tag(visibility)
}//.navigationBarTitle("Default Post Visibility")
// navbar title on the ForEach is currently incorrectly applied when the picker is not expanded, see FB6838291
}
Toggle(isOn: $preferences.automaticallySaveDrafts) {
Text("Automatically Save Drafts")
}
Toggle(isOn: $preferences.requireAttachmentDescriptions) {
Text("Require Attachment Descriptions")
}
}
}
var replyingSection: some View {
Section(header: Text("REPLYING")) {
Picker(selection: $preferences.contentWarningCopyMode, label: Text("Copy Content Warnings")) {
Text("As-is").tag(ContentWarningCopyMode.asIs)
Text("Prepend 're: '").tag(ContentWarningCopyMode.prependRe)
Text("Don't copy").tag(ContentWarningCopyMode.doNotCopy)
}
Toggle(isOn: $preferences.mentionReblogger) {
Text("Mention Reblogger")
}
}
}
}
struct ComposingPrefsView_Previews: PreviewProvider {
static var previews: some View {
ComposingPrefsView()
}
}

View File

@ -0,0 +1,36 @@
//
// MediaPrefsView.swift
// Tusker
//
// Created by Shadowfacts on 2/22/20.
// Copyright © 2020 Shadowfacts. All rights reserved.
//
import SwiftUI
struct MediaPrefsView: View {
@ObservedObject var preferences = Preferences.shared
var body: some View {
List {
viewingSection
}.listStyle(GroupedListStyle()).navigationBarTitle("Media")
}
var viewingSection: some View {
Section(header: Text("VIEWING")) {
Toggle(isOn: $preferences.blurAllMedia) {
Text("Blur All Media")
}
Toggle(isOn: $preferences.automaticallyPlayGifs) {
Text("Automatically Play GIFs")
}
}
}
}
struct MediaPrefsView_Previews: PreviewProvider {
static var previews: some View {
MediaPrefsView()
}
}

View File

@ -52,6 +52,12 @@ struct PreferencesView: View {
NavigationLink(destination: AppearancePrefsView()) { NavigationLink(destination: AppearancePrefsView()) {
Text("Appearance") Text("Appearance")
} }
NavigationLink(destination: ComposingPrefsView()) {
Text("Composing")
}
NavigationLink(destination: MediaPrefsView()) {
Text("Media")
}
NavigationLink(destination: BehaviorPrefsView()) { NavigationLink(destination: BehaviorPrefsView()) {
Text("Behavior") Text("Behavior")
} }

View File

@ -54,6 +54,17 @@ class AttachmentView: UIImageView, GIFAnimatable {
isUserInteractionEnabled = true isUserInteractionEnabled = true
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(imagePressed))) addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(imagePressed)))
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
}
@objc func preferencesChanged() {
if let gifData = gifData {
if Preferences.shared.automaticallyPlayGifs && !isAnimatingGIF {
animate(withGIFData: gifData)
} else if !Preferences.shared.automaticallyPlayGifs && isAnimatingGIF {
stopAnimatingGIF()
}
}
} }
func loadAttachment() { func loadAttachment() {
@ -78,8 +89,12 @@ class AttachmentView: UIImageView, GIFAnimatable {
self.attachmentRequest = nil self.attachmentRequest = nil
DispatchQueue.main.async { DispatchQueue.main.async {
if self.attachment.url.pathExtension == "gif" { if self.attachment.url.pathExtension == "gif" {
self.animate(withGIFData: data)
self.gifData = data self.gifData = data
if Preferences.shared.automaticallyPlayGifs {
self.animate(withGIFData: data)
} else {
self.image = UIImage(data: data)
}
} else { } else {
self.image = UIImage(data: data) self.image = UIImage(data: data)
} }

View File

@ -8,6 +8,7 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
import Combine
protocol ProfileHeaderTableViewCellDelegate: TuskerNavigationDelegate { protocol ProfileHeaderTableViewCellDelegate: TuskerNavigationDelegate {
func showMoreOptions(cell: ProfileHeaderTableViewCell) func showMoreOptions(cell: ProfileHeaderTableViewCell)
@ -35,6 +36,8 @@ class ProfileHeaderTableViewCell: UITableViewCell {
var avatarRequest: ImageCache.Request? var avatarRequest: ImageCache.Request?
var headerRequest: ImageCache.Request? var headerRequest: ImageCache.Request?
private var accountUpdater: Cancellable?
override func awakeFromNib() { override func awakeFromNib() {
avatarContainerView.layer.masksToBounds = true avatarContainerView.layer.masksToBounds = true
avatarImageView.layer.masksToBounds = true avatarImageView.layer.masksToBounds = true
@ -62,7 +65,6 @@ class ProfileHeaderTableViewCell: UITableViewCell {
usernameLabel.text = "@\(account.acct)" usernameLabel.text = "@\(account.acct)"
avatarImageView.image = nil
avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (data) in avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (data) in
guard let self = self, let data = data, self.accountID == accountID else { return } guard let self = self, let data = data, self.accountID == accountID else { return }
self.avatarRequest = nil self.avatarRequest = nil
@ -70,7 +72,6 @@ class ProfileHeaderTableViewCell: UITableViewCell {
self.avatarImageView.image = UIImage(data: data) self.avatarImageView.image = UIImage(data: data)
} }
} }
headerImageView.image = nil
headerRequest = ImageCache.headers.get(account.header) { [weak self] (data) in headerRequest = ImageCache.headers.get(account.header) { [weak self] (data) in
guard let self = self, let data = data, self.accountID == accountID else { return } guard let self = self, let data = data, self.accountID == accountID else { return }
self.headerRequest = nil self.headerRequest = nil
@ -99,6 +100,7 @@ class ProfileHeaderTableViewCell: UITableViewCell {
if let fields = account.fields, !fields.isEmpty { if let fields = account.fields, !fields.isEmpty {
fieldsStackView.isHidden = false fieldsStackView.isHidden = false
fieldsStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
for field in fields { for field in fields {
let nameLabel = UILabel() let nameLabel = UILabel()
nameLabel.text = field.name nameLabel.text = field.name
@ -120,6 +122,13 @@ class ProfileHeaderTableViewCell: UITableViewCell {
} else { } else {
fieldsStackView.isHidden = true fieldsStackView.isHidden = true
} }
if accountUpdater == nil {
accountUpdater = mastodonController.cache.accountSubject
.filter { [unowned self] in $0.id == self.accountID }
.receive(on: DispatchQueue.main)
.sink { [unowned self] in self.updateUI(for: $0.id) }
}
} }
@objc func updateUIForPreferences() { @objc func updateUIForPreferences() {

View File

@ -328,10 +328,16 @@ extension BaseStatusTableViewCell: MenuPreviewProvider {
) )
} else if attachmentsView.frame.contains(location) { } else if attachmentsView.frame.contains(location) {
let attachmentsViewLocation = attachmentsView.convert(location, from: self) let attachmentsViewLocation = attachmentsView.convert(location, from: self)
if let attachmentView = attachmentsView.attachmentViews.allObjects.first(where: { $0.frame.contains(attachmentsViewLocation) }), if let attachmentView = attachmentsView.attachmentViews.allObjects.first(where: { $0.frame.contains(attachmentsViewLocation) }) {
let image = attachmentView.image { return (
let description = attachmentView.attachment.description content: {
return (content: { self.delegate?.largeImage(image, description: description, sourceView: attachmentView) }, actions: { [] }) let attachments = self.attachmentsView.attachments!
let sourceViews = attachments.map(self.attachmentsView.getAttachmentView(for:))
let startIndex = sourceViews.firstIndex(of: attachmentView)!
return self.delegate?.gallery(attachments: attachments, sourceViews: sourceViews, startIndex: startIndex)
},
actions: { [] }
)
} }
}/* else if contentLabel.frame.contains(location), }/* else if contentLabel.frame.contains(location),
let link = contentLabel.getLink(atPoint: contentLabel.convert(location, from: self)) { let link = contentLabel.getLink(atPoint: contentLabel.convert(location, from: self)) {