Compare commits
No commits in common. "ef1db466b9519c1260ab5fbf9a04d9f581700853" and "806591f5b78a8e2c27803f2365d8743ef49a4d3e" have entirely different histories.
ef1db466b9
...
806591f5b7
|
@ -186,7 +186,6 @@
|
||||||
D67895C0246870DE00D4CD9E /* LocalAccountAvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67895BF246870DE00D4CD9E /* LocalAccountAvatarView.swift */; };
|
D67895C0246870DE00D4CD9E /* LocalAccountAvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67895BF246870DE00D4CD9E /* LocalAccountAvatarView.swift */; };
|
||||||
D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D679C09E215850EF00DA27FE /* XCBActions.swift */; };
|
D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D679C09E215850EF00DA27FE /* XCBActions.swift */; };
|
||||||
D67B506D250B291200FAECFB /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67B506C250B291200FAECFB /* BlurHashDecode.swift */; };
|
D67B506D250B291200FAECFB /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67B506C250B291200FAECFB /* BlurHashDecode.swift */; };
|
||||||
D67C1795266D57D10070F250 /* FastAccountSwitcherIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67C1794266D57D10070F250 /* FastAccountSwitcherIndicatorView.swift */; };
|
|
||||||
D67C57AD21E265FC00C3118B /* LargeAccountDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67C57AC21E265FC00C3118B /* LargeAccountDetailView.swift */; };
|
D67C57AD21E265FC00C3118B /* LargeAccountDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67C57AC21E265FC00C3118B /* LargeAccountDetailView.swift */; };
|
||||||
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 */; };
|
||||||
D68015402401A6BA00D6103B /* ComposingPrefsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D680153F2401A6BA00D6103B /* ComposingPrefsView.swift */; };
|
D68015402401A6BA00D6103B /* ComposingPrefsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D680153F2401A6BA00D6103B /* ComposingPrefsView.swift */; };
|
||||||
|
@ -588,7 +587,6 @@
|
||||||
D67895BF246870DE00D4CD9E /* LocalAccountAvatarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAccountAvatarView.swift; sourceTree = "<group>"; };
|
D67895BF246870DE00D4CD9E /* LocalAccountAvatarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAccountAvatarView.swift; sourceTree = "<group>"; };
|
||||||
D679C09E215850EF00DA27FE /* XCBActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBActions.swift; sourceTree = "<group>"; };
|
D679C09E215850EF00DA27FE /* XCBActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBActions.swift; sourceTree = "<group>"; };
|
||||||
D67B506C250B291200FAECFB /* BlurHashDecode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashDecode.swift; sourceTree = "<group>"; };
|
D67B506C250B291200FAECFB /* BlurHashDecode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashDecode.swift; sourceTree = "<group>"; };
|
||||||
D67C1794266D57D10070F250 /* FastAccountSwitcherIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FastAccountSwitcherIndicatorView.swift; sourceTree = "<group>"; };
|
|
||||||
D67C57AC21E265FC00C3118B /* LargeAccountDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeAccountDetailView.swift; sourceTree = "<group>"; };
|
D67C57AC21E265FC00C3118B /* LargeAccountDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeAccountDetailView.swift; sourceTree = "<group>"; };
|
||||||
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>"; };
|
||||||
D680153F2401A6BA00D6103B /* ComposingPrefsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposingPrefsView.swift; sourceTree = "<group>"; };
|
D680153F2401A6BA00D6103B /* ComposingPrefsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposingPrefsView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1451,7 +1449,6 @@
|
||||||
D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameLabel.swift */,
|
D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameLabel.swift */,
|
||||||
D6E426B225337C7000C02E1C /* CustomEmojiImageView.swift */,
|
D6E426B225337C7000C02E1C /* CustomEmojiImageView.swift */,
|
||||||
D6EAE0DA2550CC8A002DB0AC /* FocusableTextField.swift */,
|
D6EAE0DA2550CC8A002DB0AC /* FocusableTextField.swift */,
|
||||||
D67C1794266D57D10070F250 /* FastAccountSwitcherIndicatorView.swift */,
|
|
||||||
D67C57A721E2649B00C3118B /* Account Detail */,
|
D67C57A721E2649B00C3118B /* Account Detail */,
|
||||||
D626494023C122C800612E6E /* Asset Picker */,
|
D626494023C122C800612E6E /* Asset Picker */,
|
||||||
D61959D0241E842400A37B8E /* Draft Cell */,
|
D61959D0241E842400A37B8E /* Draft Cell */,
|
||||||
|
@ -2171,7 +2168,6 @@
|
||||||
D6AEBB432321685E00E5038B /* OpenInSafariActivity.swift in Sources */,
|
D6AEBB432321685E00E5038B /* OpenInSafariActivity.swift in Sources */,
|
||||||
D6C693FE2162FEEA007D6A6D /* UIViewController+Children.swift in Sources */,
|
D6C693FE2162FEEA007D6A6D /* UIViewController+Children.swift in Sources */,
|
||||||
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */,
|
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */,
|
||||||
D67C1795266D57D10070F250 /* FastAccountSwitcherIndicatorView.swift in Sources */,
|
|
||||||
D627FF76217E923E00CC0648 /* DraftsManager.swift in Sources */,
|
D627FF76217E923E00CC0648 /* DraftsManager.swift in Sources */,
|
||||||
D64F80E2215875CC00BEF393 /* XCBActionType.swift in Sources */,
|
D64F80E2215875CC00BEF393 /* XCBActionType.swift in Sources */,
|
||||||
04586B4322B301470021BD04 /* AppearancePrefsView.swift in Sources */,
|
04586B4322B301470021BD04 /* AppearancePrefsView.swift in Sources */,
|
||||||
|
|
|
@ -65,9 +65,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch type {
|
switch type {
|
||||||
case .mainScene:
|
|
||||||
return "main-scene"
|
|
||||||
|
|
||||||
case .showConversation,
|
case .showConversation,
|
||||||
.showTimeline,
|
.showTimeline,
|
||||||
.checkNotifications,
|
.checkNotifications,
|
||||||
|
|
|
@ -69,7 +69,6 @@
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.bookmarks</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.bookmarks</string>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.my-profile</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.my-profile</string>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.show-profile</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.show-profile</string>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.main-scene</string>
|
|
||||||
</array>
|
</array>
|
||||||
<key>UIApplicationSceneManifest</key>
|
<key>UIApplicationSceneManifest</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
|
|
@ -15,15 +15,12 @@ class MainSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
|
|
||||||
var window: UIWindow?
|
var window: UIWindow?
|
||||||
|
|
||||||
private var launchActivity: NSUserActivity?
|
|
||||||
|
|
||||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||||
|
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
|
||||||
|
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
|
||||||
|
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
|
||||||
guard let windowScene = scene as? UIWindowScene else { return }
|
guard let windowScene = scene as? UIWindowScene else { return }
|
||||||
|
|
||||||
if let activity = connectionOptions.userActivities.first ?? session.stateRestorationActivity {
|
|
||||||
launchActivity = activity
|
|
||||||
}
|
|
||||||
|
|
||||||
window = UIWindow(windowScene: windowScene)
|
window = UIWindow(windowScene: windowScene)
|
||||||
|
|
||||||
if let report = AppDelegate.pendingCrashReport {
|
if let report = AppDelegate.pendingCrashReport {
|
||||||
|
@ -94,14 +91,6 @@ class MainSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
DraftsManager.save()
|
DraftsManager.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? {
|
|
||||||
if let mastodonController = window?.windowScene?.session.mastodonController {
|
|
||||||
return UserActivityManager.mainSceneActivity(accountID: mastodonController.accountInfo!.id)
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sceneWillEnterForeground(_ scene: UIScene) {
|
func sceneWillEnterForeground(_ scene: UIScene) {
|
||||||
// Called as the scene transitions from the background to the foreground.
|
// Called as the scene transitions from the background to the foreground.
|
||||||
// Use this method to undo the changes made on entering the background.
|
// Use this method to undo the changes made on entering the background.
|
||||||
|
@ -134,14 +123,7 @@ class MainSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
func showAppOrOnboardingUI(session: UISceneSession? = nil) {
|
func showAppOrOnboardingUI(session: UISceneSession? = nil) {
|
||||||
let session = session ?? window!.windowScene!.session
|
let session = session ?? window!.windowScene!.session
|
||||||
if LocalData.shared.onboardingComplete {
|
if LocalData.shared.onboardingComplete {
|
||||||
let account: LocalData.UserAccountInfo
|
let account = LocalData.shared.getMostRecentAccount()!
|
||||||
if let activity = launchActivity,
|
|
||||||
let activityAccount = UserActivityManager.getAccount(from: activity) {
|
|
||||||
account = activityAccount
|
|
||||||
} else {
|
|
||||||
account = LocalData.shared.getMostRecentAccount()!
|
|
||||||
}
|
|
||||||
|
|
||||||
if session.mastodonController == nil {
|
if session.mastodonController == nil {
|
||||||
session.mastodonController = MastodonController.getForAccount(account)
|
session.mastodonController = MastodonController.getForAccount(account)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,6 @@ class ComposeDrawingViewController: UIViewController {
|
||||||
private(set) var undoBarButtonItem: UIBarButtonItem!
|
private(set) var undoBarButtonItem: UIBarButtonItem!
|
||||||
private(set) var redoBarButtonItem: UIBarButtonItem!
|
private(set) var redoBarButtonItem: UIBarButtonItem!
|
||||||
|
|
||||||
private var toolPicker: PKToolPicker!
|
|
||||||
|
|
||||||
private var initialDrawing: PKDrawing?
|
private var initialDrawing: PKDrawing?
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
@ -72,19 +70,25 @@ class ComposeDrawingViewController: UIViewController {
|
||||||
canvasView.topAnchor.constraint(equalTo: view.topAnchor),
|
canvasView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||||
canvasView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
|
canvasView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
|
||||||
])
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
toolPicker = PKToolPicker()
|
// todo: should the PKToolPicker be owned by this VC or something else?
|
||||||
toolPicker.setVisible(true, forFirstResponder: canvasView)
|
if let window = parent?.view.window, let toolPicker = PKToolPicker.shared(for: window) {
|
||||||
toolPicker.addObserver(canvasView)
|
toolPicker.setVisible(true, forFirstResponder: canvasView)
|
||||||
toolPicker.addObserver(self)
|
toolPicker.addObserver(canvasView)
|
||||||
|
toolPicker.addObserver(self)
|
||||||
updateLayout(for: toolPicker)
|
|
||||||
canvasView.becomeFirstResponder()
|
updateLayout(for: toolPicker)
|
||||||
|
canvasView.becomeFirstResponder()
|
||||||
// wait until the next run loop iteration so that the canvas view has become first responder and it's undo manager exists
|
|
||||||
DispatchQueue.main.async {
|
// wait until the next run loop iteration so that the canvas view has become first responder and it's undo manager exists
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(self.updateUndoRedoButtonState), name: .NSUndoManagerDidUndoChange, object: self.undoManager!)
|
DispatchQueue.main.async {
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(self.updateUndoRedoButtonState), name: .NSUndoManagerDidRedoChange, object: self.undoManager!)
|
NotificationCenter.default.addObserver(self, selector: #selector(self.updateUndoRedoButtonState), name: .NSUndoManagerDidUndoChange, object: self.undoManager!)
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(self.updateUndoRedoButtonState), name: .NSUndoManagerDidRedoChange, object: self.undoManager!)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,6 @@ class MainTabBarViewController: UITabBarController, UITabBarControllerDelegate {
|
||||||
private var composePlaceholder: UIViewController!
|
private var composePlaceholder: UIViewController!
|
||||||
private var fastAccountSwitcher: FastAccountSwitcherViewController!
|
private var fastAccountSwitcher: FastAccountSwitcherViewController!
|
||||||
|
|
||||||
private var fastSwitcherIndicator: FastAccountSwitcherIndicatorView!
|
|
||||||
private var fastSwitcherConstraints: [NSLayoutConstraint] = []
|
|
||||||
|
|
||||||
var selectedTab: Tab {
|
var selectedTab: Tab {
|
||||||
return Tab(rawValue: selectedIndex)!
|
return Tab(rawValue: selectedIndex)!
|
||||||
}
|
}
|
||||||
|
@ -73,62 +70,9 @@ class MainTabBarViewController: UITabBarController, UITabBarControllerDelegate {
|
||||||
tapRecognizer.cancelsTouchesInView = false
|
tapRecognizer.cancelsTouchesInView = false
|
||||||
tabBar.addGestureRecognizer(tapRecognizer)
|
tabBar.addGestureRecognizer(tapRecognizer)
|
||||||
|
|
||||||
if findMyProfileTabBarButton() != nil {
|
|
||||||
fastSwitcherIndicator = FastAccountSwitcherIndicatorView()
|
|
||||||
fastSwitcherIndicator.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
view.addSubview(fastSwitcherIndicator)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
fastSwitcherIndicator.widthAnchor.constraint(equalToConstant: 10),
|
|
||||||
fastSwitcherIndicator.heightAnchor.constraint(equalToConstant: 12),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
tabBar.isSpringLoaded = true
|
tabBar.isSpringLoaded = true
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
|
||||||
super.viewWillAppear(animated)
|
|
||||||
|
|
||||||
repositionFastSwitcherIndicator()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
|
||||||
super.traitCollectionDidChange(previousTraitCollection)
|
|
||||||
|
|
||||||
repositionFastSwitcherIndicator()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func repositionFastSwitcherIndicator() {
|
|
||||||
guard let myProfileButton = findMyProfileTabBarButton() else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
NSLayoutConstraint.deactivate(fastSwitcherConstraints)
|
|
||||||
// using interfaceOrientation isn't ideal, but UITabBar buttons may lay out horizontally even in the compact size class
|
|
||||||
if traitCollection.horizontalSizeClass == .compact && interfaceOrientation.isPortrait {
|
|
||||||
fastSwitcherConstraints = [
|
|
||||||
fastSwitcherIndicator.centerYAnchor.constraint(equalTo: myProfileButton.centerYAnchor, constant: -4),
|
|
||||||
// tab bar button image width is 30
|
|
||||||
fastSwitcherIndicator.leftAnchor.constraint(equalTo: myProfileButton.centerXAnchor, constant: 15 + 2),
|
|
||||||
]
|
|
||||||
} else {
|
|
||||||
fastSwitcherConstraints = [
|
|
||||||
fastSwitcherIndicator.centerYAnchor.constraint(equalTo: myProfileButton.centerYAnchor),
|
|
||||||
fastSwitcherIndicator.trailingAnchor.constraint(equalTo: myProfileButton.trailingAnchor),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
NSLayoutConstraint.activate(fastSwitcherConstraints)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func findMyProfileTabBarButton() -> UIView? {
|
|
||||||
let tabBarButtons = tabBar.subviews.filter { String(describing: type(of: $0)).lowercased().contains("button") }
|
|
||||||
// sanity check that there is 1 button per VC
|
|
||||||
guard tabBarButtons.count == viewControllers!.count,
|
|
||||||
let myProfileButton = tabBarButtons.last else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return myProfileButton
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func tabBarTapped(_ recognizer: UITapGestureRecognizer) {
|
@objc private func tabBarTapped(_ recognizer: UITapGestureRecognizer) {
|
||||||
fastAccountSwitcher.hide()
|
fastAccountSwitcher.hide()
|
||||||
}
|
}
|
||||||
|
@ -202,9 +146,13 @@ extension MainTabBarViewController {
|
||||||
|
|
||||||
extension MainTabBarViewController: FastAccountSwitcherViewControllerDelegate {
|
extension MainTabBarViewController: FastAccountSwitcherViewControllerDelegate {
|
||||||
func fastAccountSwitcher(_ fastAccountSwitcher: FastAccountSwitcherViewController, triggerZoneContains point: CGPoint) -> Bool {
|
func fastAccountSwitcher(_ fastAccountSwitcher: FastAccountSwitcherViewController, triggerZoneContains point: CGPoint) -> Bool {
|
||||||
guard let myProfileButton = findMyProfileTabBarButton() else {
|
let tabBarButtons = tabBar.subviews.filter { String(describing: type(of: $0)).lowercased().contains("button") }
|
||||||
|
// sanity check that there is 1 button per VC
|
||||||
|
guard tabBarButtons.count == viewControllers!.count,
|
||||||
|
let myProfileButton = tabBarButtons.last else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
let locationInButton = myProfileButton.convert(point, from: fastAccountSwitcher.view)
|
let locationInButton = myProfileButton.convert(point, from: fastAccountSwitcher.view)
|
||||||
return myProfileButton.bounds.contains(locationInButton)
|
return myProfileButton.bounds.contains(locationInButton)
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,9 +36,6 @@ struct PreferencesView: View {
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.onDrag {
|
|
||||||
let activity = UserActivityManager.mainSceneActivity(accountID: account.id)
|
|
||||||
return NSItemProvider(object: activity)
|
|
||||||
}
|
}
|
||||||
}.onDelete { (indices: IndexSet) in
|
}.onDelete { (indices: IndexSet) in
|
||||||
var indices = indices
|
var indices = indices
|
||||||
|
|
|
@ -37,15 +37,6 @@ class UserActivityManager {
|
||||||
}
|
}
|
||||||
return LocalData.shared.getAccount(id: id)
|
return LocalData.shared.getAccount(id: id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Main Scene
|
|
||||||
static func mainSceneActivity(accountID: String) -> NSUserActivity {
|
|
||||||
let activity = NSUserActivity(type: .mainScene)
|
|
||||||
activity.userInfo = [
|
|
||||||
"accountID": accountID,
|
|
||||||
]
|
|
||||||
return activity
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - New Post
|
// MARK: - New Post
|
||||||
static func newPostActivity(mentioning: Account? = nil, accountID: String) -> NSUserActivity {
|
static func newPostActivity(mentioning: Account? = nil, accountID: String) -> NSUserActivity {
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum UserActivityType: String {
|
enum UserActivityType: String {
|
||||||
case mainScene = "space.vaccor.Tusker.activity.main-scene"
|
|
||||||
case newPost = "space.vaccor.Tusker.activity.new-post"
|
case newPost = "space.vaccor.Tusker.activity.new-post"
|
||||||
case checkNotifications = "space.vaccor.Tusker.activity.check-notifications"
|
case checkNotifications = "space.vaccor.Tusker.activity.check-notifications"
|
||||||
case showTimeline = "space.vaccor.Tusker.activity.show-timeline"
|
case showTimeline = "space.vaccor.Tusker.activity.show-timeline"
|
||||||
|
@ -23,8 +22,6 @@ enum UserActivityType: String {
|
||||||
extension UserActivityType {
|
extension UserActivityType {
|
||||||
var handle: (NSUserActivity) -> Void {
|
var handle: (NSUserActivity) -> Void {
|
||||||
switch self {
|
switch self {
|
||||||
case .mainScene:
|
|
||||||
fatalError("cannot handle main scene activity")
|
|
||||||
case .newPost:
|
case .newPost:
|
||||||
return UserActivityManager.handleNewPost
|
return UserActivityManager.handleNewPost
|
||||||
case .checkNotifications:
|
case .checkNotifications:
|
||||||
|
|
|
@ -75,9 +75,6 @@ class AttachmentView: UIImageView, GIFAnimatable {
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(gifPlaybackModeChanged), name: .NSProcessInfoPowerStateDidChange, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(gifPlaybackModeChanged), name: .NSProcessInfoPowerStateDidChange, object: nil)
|
||||||
|
|
||||||
addInteraction(UIContextMenuInteraction(delegate: self))
|
addInteraction(UIContextMenuInteraction(delegate: self))
|
||||||
|
|
||||||
isAccessibilityElement = true
|
|
||||||
accessibilityTraits = [.image, .button]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func preferencesChanged() {
|
@objc private func preferencesChanged() {
|
||||||
|
@ -296,13 +293,6 @@ class AttachmentView: UIImageView, GIFAnimatable {
|
||||||
@objc func imagePressed() {
|
@objc func imagePressed() {
|
||||||
showGallery()
|
showGallery()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Accessibility
|
|
||||||
|
|
||||||
override func accessibilityActivate() -> Bool {
|
|
||||||
showGallery()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -248,11 +248,9 @@ class AttachmentsContainerView: UIView {
|
||||||
let attachmentView = AttachmentView(attachment: attachments[index], index: index, expectedSize: size)
|
let attachmentView = AttachmentView(attachment: attachments[index], index: index, expectedSize: size)
|
||||||
attachmentView.delegate = delegate
|
attachmentView.delegate = delegate
|
||||||
attachmentView.translatesAutoresizingMaskIntoConstraints = false
|
attachmentView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
attachmentView.isAccessibilityElement = true
|
||||||
|
attachmentView.accessibilityTraits = [.image, .button]
|
||||||
attachmentView.accessibilityLabel = String(format: NSLocalizedString("Attachment %d", comment: "attachment at index accessiblity label"), index + 1)
|
attachmentView.accessibilityLabel = String(format: NSLocalizedString("Attachment %d", comment: "attachment at index accessiblity label"), index + 1)
|
||||||
attachmentView.accessibilityLabel = "Attachment \(index + 1)"
|
|
||||||
if let desc = attachments[index].description {
|
|
||||||
attachmentView.accessibilityLabel! += ", \(desc)"
|
|
||||||
}
|
|
||||||
attachmentViews.add(attachmentView)
|
attachmentViews.add(attachmentView)
|
||||||
return attachmentView
|
return attachmentView
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
//
|
|
||||||
// FastAccountSwitchingIndicatorView.swift
|
|
||||||
// Tusker
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 6/6/21.
|
|
||||||
// Copyright © 2021 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class FastAccountSwitcherIndicatorView: UIView {
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
|
|
||||||
tintColor = .lightGray
|
|
||||||
|
|
||||||
let up = UIImageView(image: UIImage(systemName: "arrowtriangle.up.fill")!)
|
|
||||||
up.frame = CGRect(x: 0, y: 0, width: 10, height: 5)
|
|
||||||
addSubview(up)
|
|
||||||
let down = UIImageView(image: UIImage(systemName: "arrowtriangle.down.fill")!)
|
|
||||||
down.frame = CGRect(x: 0, y: 7, width: 10, height: 5)
|
|
||||||
addSubview(down)
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -44,15 +44,12 @@ class PollOptionView: UIView {
|
||||||
percentLabel.setContentHuggingPriority(.required, for: .horizontal)
|
percentLabel.setContentHuggingPriority(.required, for: .horizontal)
|
||||||
addSubview(percentLabel)
|
addSubview(percentLabel)
|
||||||
|
|
||||||
accessibilityLabel = option.title
|
|
||||||
|
|
||||||
if (poll.voted ?? false) || poll.effectiveExpired,
|
if (poll.voted ?? false) || poll.effectiveExpired,
|
||||||
let optionVotes = option.votesCount {
|
let optionVotes = option.votesCount {
|
||||||
let frac = poll.votesCount == 0 ? 0 : CGFloat(optionVotes) / CGFloat(poll.votesCount)
|
let frac = poll.votesCount == 0 ? 0 : CGFloat(optionVotes) / CGFloat(poll.votesCount)
|
||||||
let percent = String(format: "%.0f%%", frac * 100)
|
|
||||||
|
|
||||||
percentLabel.isHidden = false
|
percentLabel.isHidden = false
|
||||||
percentLabel.text = percent
|
percentLabel.text = String(format: "%.0f%%", frac * 100)
|
||||||
|
|
||||||
let fillView = UIView()
|
let fillView = UIView()
|
||||||
fillView.translatesAutoresizingMaskIntoConstraints = false
|
fillView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
@ -67,8 +64,6 @@ class PollOptionView: UIView {
|
||||||
fillView.topAnchor.constraint(equalTo: topAnchor),
|
fillView.topAnchor.constraint(equalTo: topAnchor),
|
||||||
fillView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
fillView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||||
])
|
])
|
||||||
|
|
||||||
accessibilityLabel! += ", \(percent)"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let minHeightConstraint = heightAnchor.constraint(greaterThanOrEqualToConstant: minHeight)
|
let minHeightConstraint = heightAnchor.constraint(greaterThanOrEqualToConstant: minHeight)
|
||||||
|
@ -91,8 +86,6 @@ class PollOptionView: UIView {
|
||||||
percentLabel.bottomAnchor.constraint(equalTo: bottomAnchor),
|
percentLabel.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||||
percentLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8),
|
percentLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8),
|
||||||
])
|
])
|
||||||
|
|
||||||
isAccessibilityElement = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
|
|
|
@ -69,8 +69,6 @@ class PollOptionsView: UIControl {
|
||||||
stack.addArrangedSubview(optionView)
|
stack.addArrangedSubview(optionView)
|
||||||
return optionView
|
return optionView
|
||||||
}
|
}
|
||||||
|
|
||||||
accessibilityElements = options
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func selectOption(_ option: PollOptionView) {
|
private func selectOption(_ option: PollOptionView) {
|
||||||
|
|
|
@ -28,6 +28,7 @@ class StatusPollView: UIView {
|
||||||
private var optionsView: PollOptionsView!
|
private var optionsView: PollOptionsView!
|
||||||
private var voteButton: UIButton!
|
private var voteButton: UIButton!
|
||||||
private var infoLabel: UILabel!
|
private var infoLabel: UILabel!
|
||||||
|
private var options: [PollOptionView] = []
|
||||||
|
|
||||||
private var canVote = true
|
private var canVote = true
|
||||||
private var animator: UIViewPropertyAnimator!
|
private var animator: UIViewPropertyAnimator!
|
||||||
|
@ -73,14 +74,15 @@ class StatusPollView: UIView {
|
||||||
voteButton.topAnchor.constraint(equalTo: optionsView.bottomAnchor),
|
voteButton.topAnchor.constraint(equalTo: optionsView.bottomAnchor),
|
||||||
voteButton.bottomAnchor.constraint(equalTo: bottomAnchor),
|
voteButton.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||||
])
|
])
|
||||||
|
|
||||||
accessibilityElements = [optionsView!, infoLabel!, voteButton!]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUI(status: StatusMO, poll: Poll?) {
|
func updateUI(status: StatusMO, poll: Poll?) {
|
||||||
self.statusID = status.id
|
self.statusID = status.id
|
||||||
self.poll = poll
|
self.poll = poll
|
||||||
|
|
||||||
|
// remove old options
|
||||||
|
options.forEach { $0.removeFromSuperview() }
|
||||||
|
|
||||||
guard let poll = poll else { return }
|
guard let poll = poll else { return }
|
||||||
|
|
||||||
// poll.voted is nil if there is no user (e.g., public timeline), in which case the poll also cannot be voted upon
|
// poll.voted is nil if there is no user (e.g., public timeline), in which case the poll also cannot be voted upon
|
||||||
|
|
|
@ -132,7 +132,6 @@ class ProfileHeaderView: UIView {
|
||||||
|
|
||||||
fieldNamesStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
|
fieldNamesStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
|
||||||
fieldValuesStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
|
fieldValuesStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
|
||||||
var fieldAccessibilityElements = [Any]()
|
|
||||||
for field in account.fields {
|
for field in account.fields {
|
||||||
let nameLabel = EmojiLabel()
|
let nameLabel = EmojiLabel()
|
||||||
nameLabel.text = field.name
|
nameLabel.text = field.name
|
||||||
|
@ -156,18 +155,7 @@ class ProfileHeaderView: UIView {
|
||||||
fieldValuesStackView.addArrangedSubview(valueTextView)
|
fieldValuesStackView.addArrangedSubview(valueTextView)
|
||||||
|
|
||||||
nameLabel.heightAnchor.constraint(equalTo: valueTextView.heightAnchor).isActive = true
|
nameLabel.heightAnchor.constraint(equalTo: valueTextView.heightAnchor).isActive = true
|
||||||
fieldAccessibilityElements.append(nameLabel)
|
|
||||||
fieldAccessibilityElements.append(valueTextView)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
accessibilityElements = [
|
|
||||||
displayNameLabel!,
|
|
||||||
usernameLabel!,
|
|
||||||
noteTextView!,
|
|
||||||
] + fieldAccessibilityElements + [
|
|
||||||
moreButton!,
|
|
||||||
pagesSegmentedControl!,
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateRelationship() {
|
private func updateRelationship() {
|
||||||
|
|
|
@ -94,7 +94,8 @@ class BaseStatusTableViewCell: UITableViewCell, MenuPreviewProvider {
|
||||||
collapseButton.layer.masksToBounds = true
|
collapseButton.layer.masksToBounds = true
|
||||||
collapseButton.layer.cornerRadius = 5
|
collapseButton.layer.cornerRadius = 5
|
||||||
|
|
||||||
accessibilityElements = [displayNameLabel!, contentWarningLabel!, collapseButton!, contentTextView!, attachmentsView!, pollView!]
|
accessibilityElements = [displayNameLabel!, contentWarningLabel!, collapseButton!, contentTextView!, attachmentsView!]
|
||||||
|
attachmentsView.isAccessibilityElement = true
|
||||||
|
|
||||||
moreButton.showsMenuAsPrimaryAction = true
|
moreButton.showsMenuAsPrimaryAction = true
|
||||||
|
|
||||||
|
@ -156,6 +157,8 @@ class BaseStatusTableViewCell: UITableViewCell, MenuPreviewProvider {
|
||||||
cardView.navigationDelegate = navigationDelegate
|
cardView.navigationDelegate = navigationDelegate
|
||||||
|
|
||||||
attachmentsView.updateUI(status: status)
|
attachmentsView.updateUI(status: status)
|
||||||
|
attachmentsView.isAccessibilityElement = status.attachments.count > 0
|
||||||
|
attachmentsView.accessibilityLabel = String(format: NSLocalizedString("%d attachments", comment: "status attachments count accessibility label"), status.attachments.count)
|
||||||
|
|
||||||
updateStatusState(status: status)
|
updateStatusState(status: status)
|
||||||
|
|
||||||
|
|
|
@ -33,21 +33,7 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
|
|
||||||
profileAccessibilityElement = UIAccessibilityElement(accessibilityContainer: self)
|
profileAccessibilityElement = UIAccessibilityElement(accessibilityContainer: self)
|
||||||
profileAccessibilityElement.accessibilityFrameInContainerSpace = profileDetailContainerView.convert(profileDetailContainerView.frame, to: self)
|
profileAccessibilityElement.accessibilityFrameInContainerSpace = profileDetailContainerView.convert(profileDetailContainerView.frame, to: self)
|
||||||
accessibilityElements = [
|
accessibilityElements = [profileAccessibilityElement!, contentWarningLabel!, collapseButton!, contentTextView!, totalFavoritesButton!, totalReblogsButton!, timestampAndClientLabel!, replyButton!, favoriteButton!, reblogButton!, moreButton!]
|
||||||
profileAccessibilityElement!,
|
|
||||||
contentWarningLabel!,
|
|
||||||
collapseButton!,
|
|
||||||
contentTextView!,
|
|
||||||
attachmentsView!,
|
|
||||||
pollView!,
|
|
||||||
totalFavoritesButton!,
|
|
||||||
totalReblogsButton!,
|
|
||||||
timestampAndClientLabel!,
|
|
||||||
replyButton!,
|
|
||||||
favoriteButton!,
|
|
||||||
reblogButton!,
|
|
||||||
moreButton!,
|
|
||||||
]
|
|
||||||
|
|
||||||
contentTextView.defaultFont = .systemFont(ofSize: 18)
|
contentTextView.defaultFont = .systemFont(ofSize: 18)
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
static let relativeDateFormatter: RelativeDateTimeFormatter = {
|
static let relativeDateFormatter: RelativeDateTimeFormatter = {
|
||||||
let formatter = RelativeDateTimeFormatter()
|
let formatter = RelativeDateTimeFormatter()
|
||||||
formatter.dateTimeStyle = .numeric
|
formatter.dateTimeStyle = .numeric
|
||||||
formatter.unitsStyle = .full
|
formatter.unitsStyle = .short
|
||||||
return formatter
|
return formatter
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
|
|
||||||
reblogLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(reblogLabelPressed)))
|
reblogLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(reblogLabelPressed)))
|
||||||
|
|
||||||
isAccessibilityElement = true
|
accessibilityElements!.insert(reblogLabel!, at: 0)
|
||||||
|
|
||||||
// todo: double check this on RTL layouts
|
// todo: double check this on RTL layouts
|
||||||
replyButton.imageView!.leadingAnchor.constraint(equalTo: contentTextView.leadingAnchor).isActive = true
|
replyButton.imageView!.leadingAnchor.constraint(equalTo: contentTextView.leadingAnchor).isActive = true
|
||||||
|
@ -134,6 +134,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
|
|
||||||
private func doUpdateTimestamp(status: StatusMO) {
|
private func doUpdateTimestamp(status: StatusMO) {
|
||||||
timestampLabel.text = status.createdAt.timeAgoString()
|
timestampLabel.text = status.createdAt.timeAgoString()
|
||||||
|
timestampLabel.accessibilityLabel = TimelineStatusTableViewCell.relativeDateFormatter.localizedString(for: status.createdAt, relativeTo: Date())
|
||||||
|
|
||||||
let delay: DispatchTimeInterval?
|
let delay: DispatchTimeInterval?
|
||||||
switch status.createdAt.timeAgo().1 {
|
switch status.createdAt.timeAgo().1 {
|
||||||
|
@ -192,38 +193,6 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
actions: { self.actionsForStatus(status, sourceView: self) }
|
actions: { self.actionsForStatus(status, sourceView: self) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Accessibility
|
|
||||||
|
|
||||||
override var accessibilityLabel: String? {
|
|
||||||
get {
|
|
||||||
guard let status = mastodonController.persistentContainer.status(for: statusID) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var str = "\(status.account.displayName), \(contentTextView.text ?? "")"
|
|
||||||
|
|
||||||
if status.attachments.count > 0 {
|
|
||||||
// todo: localize me
|
|
||||||
str += ", \(status.attachments.count) attachments"
|
|
||||||
}
|
|
||||||
if status.poll != nil {
|
|
||||||
str += ", poll"
|
|
||||||
}
|
|
||||||
str += ", \(TimelineStatusTableViewCell.relativeDateFormatter.localizedString(for: status.createdAt, relativeTo: Date()))"
|
|
||||||
if let rebloggerID = rebloggerID,
|
|
||||||
let reblogger = mastodonController.persistentContainer.account(for: rebloggerID) {
|
|
||||||
str += ", reblogged by \(reblogger.displayName)"
|
|
||||||
}
|
|
||||||
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
set {}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func accessibilityActivate() -> Bool {
|
|
||||||
didSelectCell()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,8 +55,6 @@ class VisualEffectImageButton: UIControl {
|
||||||
addInteraction(UIContextMenuInteraction(delegate: self))
|
addInteraction(UIContextMenuInteraction(delegate: self))
|
||||||
|
|
||||||
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTap)))
|
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTap)))
|
||||||
|
|
||||||
isAccessibilityElement = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func onTap() {
|
@objc private func onTap() {
|
||||||
|
|
Loading…
Reference in New Issue