forked from shadowfacts/Tusker
313 lines
14 KiB
Swift
313 lines
14 KiB
Swift
//
|
|
// UserActivityManager.swift
|
|
// Tusker
|
|
//
|
|
// Created by Shadowfacts on 10/19/18.
|
|
// Copyright © 2018 Shadowfacts. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import Intents
|
|
import Pachyderm
|
|
import OSLog
|
|
|
|
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "UserActivityManager")
|
|
|
|
class UserActivityManager {
|
|
|
|
private let scene: UIWindowScene
|
|
|
|
init(scene: UIWindowScene) {
|
|
self.scene = scene
|
|
}
|
|
|
|
// MARK: - Utils
|
|
private static let encoder = PropertyListEncoder()
|
|
private static let decoder = PropertyListDecoder()
|
|
|
|
private var mastodonController: MastodonController {
|
|
scene.session.mastodonController!
|
|
}
|
|
|
|
private func getMainViewController() -> TuskerRootViewController {
|
|
let window = scene.windows.first { $0.isKeyWindow } ?? scene.windows.first!
|
|
return window.rootViewController as! TuskerRootViewController
|
|
}
|
|
|
|
private func present(_ vc: UIViewController, animated: Bool = true) {
|
|
getMainViewController().present(vc, animated: animated)
|
|
}
|
|
|
|
static func getAccount(from activity: NSUserActivity) -> LocalData.UserAccountInfo? {
|
|
guard let id = activity.userInfo?["accountID"] as? String else {
|
|
return nil
|
|
}
|
|
return LocalData.shared.getAccount(id: id)
|
|
}
|
|
|
|
// MARK: - Main Scene
|
|
static func mainSceneActivity(accountID: String?) -> NSUserActivity {
|
|
let activity = NSUserActivity(type: .mainScene)
|
|
if let accountID {
|
|
activity.userInfo = [
|
|
"accountID": accountID,
|
|
]
|
|
}
|
|
return activity
|
|
}
|
|
|
|
// MARK: - New Post
|
|
static func newPostActivity(mentioning: Account? = nil, accountID: String) -> NSUserActivity {
|
|
// todo: update to use managed objects
|
|
let activity = NSUserActivity(type: .newPost)
|
|
activity.isEligibleForPrediction = true
|
|
activity.userInfo = [
|
|
"accountID": accountID,
|
|
]
|
|
if let mentioning = mentioning {
|
|
activity.userInfo!["mentioning"] = mentioning.acct
|
|
activity.title = "Send a message to \(mentioning.displayName)"
|
|
activity.suggestedInvocationPhrase = "Send a message to \(mentioning.displayName)"
|
|
} else {
|
|
activity.title = "New Post"
|
|
activity.suggestedInvocationPhrase = "Post in Tusker"
|
|
}
|
|
return activity
|
|
}
|
|
|
|
func handleNewPost(activity: NSUserActivity) {
|
|
// TODO: check not currently showing compose screen
|
|
let mentioning = activity.userInfo?["mentioning"] as? String
|
|
let draft = mastodonController.createDraft(mentioningAcct: mentioning)
|
|
// todo: this shouldn't use self.mastodonController, it should get the right one based on the userInfo accountID
|
|
let composeVC = ComposeHostingController(draft: draft, mastodonController: mastodonController)
|
|
present(UINavigationController(rootViewController: composeVC))
|
|
}
|
|
|
|
static func editDraftActivity(id: UUID, accountID: String) -> NSUserActivity {
|
|
let activity = NSUserActivity(type: .newPost)
|
|
activity.userInfo = [
|
|
"accountID": accountID,
|
|
"draftID": id.uuidString,
|
|
]
|
|
return activity
|
|
}
|
|
|
|
static func addDuckedDraft(to activity: NSUserActivity?, draft: Draft) -> NSUserActivity {
|
|
if let activity {
|
|
activity.addUserInfoEntries(from: [
|
|
"duckedDraftID": draft.id.uuidString
|
|
])
|
|
return activity
|
|
} else {
|
|
return editDraftActivity(id: draft.id, accountID: draft.accountID)
|
|
}
|
|
}
|
|
|
|
static func addEditedDraft(to activity: NSUserActivity?, draft: Draft) -> NSUserActivity {
|
|
if let activity {
|
|
activity.addUserInfoEntries(from: [
|
|
"editedDraftID": draft.id.uuidString
|
|
])
|
|
return activity
|
|
} else {
|
|
return editDraftActivity(id: draft.id, accountID: draft.accountID)
|
|
}
|
|
}
|
|
|
|
static func getDraft(from activity: NSUserActivity) -> Draft? {
|
|
let idStr: String?
|
|
if activity.activityType == UserActivityType.newPost.rawValue {
|
|
idStr = activity.userInfo?["draftID"] as? String
|
|
} else {
|
|
idStr = activity.userInfo?["duckedDraftID"] as? String ?? activity.userInfo?["editedDraftID"] as? String
|
|
}
|
|
guard let idStr,
|
|
let uuid = UUID(uuidString: idStr) else {
|
|
return nil
|
|
}
|
|
return DraftsManager.shared.getBy(id: uuid)
|
|
}
|
|
|
|
// MARK: - Check Notifications
|
|
static func checkNotificationsActivity(mode: NotificationsMode = .allNotifications) -> NSUserActivity {
|
|
let activity = NSUserActivity(type: .checkNotifications)
|
|
activity.isEligibleForPrediction = true
|
|
activity.addUserInfoEntries(from: [
|
|
"notificationsMode": mode.rawValue
|
|
])
|
|
switch mode {
|
|
case .allNotifications:
|
|
activity.title = NSLocalizedString("Check Notifications", comment: "check notifications shortcut title")
|
|
activity.suggestedInvocationPhrase = NSLocalizedString("Check my Tusker notifications", comment: "check notifications shortcut invocation phrase")
|
|
case .mentionsOnly:
|
|
activity.title = NSLocalizedString("Check Mentions", comment: "check mentions shortcut title")
|
|
activity.suggestedInvocationPhrase = NSLocalizedString("Check my mentions", comment: "check mentions shortcut invocation phrase")
|
|
}
|
|
return activity
|
|
}
|
|
|
|
func handleCheckNotifications(activity: NSUserActivity) {
|
|
let mainViewController = getMainViewController()
|
|
mainViewController.select(tab: .notifications)
|
|
if let navigationController = mainViewController.getTabController(tab: .notifications) as? UINavigationController,
|
|
let notificationsPageController = navigationController.viewControllers.first as? NotificationsPageViewController {
|
|
navigationController.popToRootViewController(animated: false)
|
|
notificationsPageController.loadViewIfNeeded()
|
|
notificationsPageController.selectMode(Self.getNotificationsMode(from: activity) ?? Preferences.shared.defaultNotificationsMode)
|
|
}
|
|
}
|
|
|
|
static func getNotificationsMode(from activity: NSUserActivity) -> NotificationsMode? {
|
|
guard let str = activity.userInfo?["notificationsMode"] as? String else {
|
|
return nil
|
|
}
|
|
return NotificationsMode(rawValue: str)
|
|
}
|
|
|
|
// MARK: - Show Timeline
|
|
static func showTimelineActivity(timeline: Timeline, accountID: String) -> NSUserActivity? {
|
|
guard let timelineData = try? encoder.encode(timeline) else { return nil }
|
|
|
|
let activity = NSUserActivity(type: .showTimeline)
|
|
activity.isEligibleForPrediction = true
|
|
activity.userInfo = [
|
|
"timelineData": timelineData,
|
|
"accountID": accountID,
|
|
]
|
|
switch timeline {
|
|
case .home:
|
|
activity.title = NSLocalizedString("Show Home Timeline", comment: "home timeline shortcut title")
|
|
activity.suggestedInvocationPhrase = NSLocalizedString("Show my home timeline", comment: "home timeline shortcut invocation phrase")
|
|
case .public(local: true):
|
|
activity.title = NSLocalizedString("Show Local Timeline", comment: "local timeline shortcut title")
|
|
activity.suggestedInvocationPhrase = NSLocalizedString("Show my local timeline", comment: "local timeline shortcut invocation phrase")
|
|
case .public(local: false):
|
|
activity.title = NSLocalizedString("Show Federated Timeline", comment: "federated timeline shortcut title")
|
|
activity.suggestedInvocationPhrase = NSLocalizedString("Show my federated timeline", comment: "federated timeline invocation phrase")
|
|
case let .tag(hashtag):
|
|
activity.title = String(format: NSLocalizedString("Show #%@", comment: "show hashtag shortcut title"), hashtag)
|
|
activity.suggestedInvocationPhrase = String(format: NSLocalizedString("Show the %@ hashtag", comment: "hashtag shortcut invocation phrase"), hashtag)
|
|
case .list:
|
|
// todo: add title to list
|
|
activity.title = NSLocalizedString("Show List", comment: "list timeline shortcut title")
|
|
activity.suggestedInvocationPhrase = NSLocalizedString("Show my list", comment: "list timeline invocation phrase")
|
|
case .direct:
|
|
activity.title = NSLocalizedString("Show Direct Messages", comment: "direct message timeline shortcut title")
|
|
activity.suggestedInvocationPhrase = NSLocalizedString("Show my direct messages", comment: "direct message timeline invocation phrase")
|
|
}
|
|
return activity
|
|
}
|
|
|
|
static func getTimeline(from activity: NSUserActivity) -> Timeline? {
|
|
guard activity.activityType == UserActivityType.showTimeline.rawValue,
|
|
let data = activity.userInfo?["timelineData"] as? Data else {
|
|
return nil
|
|
}
|
|
return try? UserActivityManager.decoder.decode(Timeline.self, from: data)
|
|
}
|
|
|
|
func handleShowTimeline(activity: NSUserActivity) {
|
|
guard let timeline = Self.getTimeline(from: activity) else { return }
|
|
|
|
let mainViewController = getMainViewController()
|
|
mainViewController.select(tab: .timelines)
|
|
guard let navigationController = mainViewController.getTabController(tab: .timelines) as? UINavigationController else {
|
|
return
|
|
}
|
|
|
|
if mastodonController.accountPreferences.pinnedTimelines.contains(timeline) {
|
|
navigationController.popToRootViewController(animated: false)
|
|
let rootController = navigationController.viewControllers.first! as! TimelinesPageViewController
|
|
rootController.selectTimeline(timeline, animated: false)
|
|
} else {
|
|
let timeline = TimelineViewController(for: timeline, mastodonController: mastodonController)
|
|
navigationController.pushViewController(timeline, animated: false)
|
|
}
|
|
}
|
|
|
|
// MARK: - Show Conversation
|
|
static func showConversationActivity(mainStatusID: String, accountID: String, isEligibleForPrediction: Bool = false) -> NSUserActivity {
|
|
let activity = NSUserActivity(type: .showConversation)
|
|
activity.userInfo = [
|
|
"mainStatusID": mainStatusID,
|
|
"accountID": accountID,
|
|
]
|
|
activity.isEligibleForPrediction = isEligibleForPrediction
|
|
return activity
|
|
}
|
|
|
|
static func getConversationStatus(from activity: NSUserActivity) -> String? {
|
|
return activity.userInfo?["mainStatusID"] as? String
|
|
}
|
|
|
|
// MARK: - Explore
|
|
|
|
static func searchActivity() -> NSUserActivity {
|
|
let activity = NSUserActivity(type: .search)
|
|
activity.isEligibleForPrediction = true
|
|
activity.title = NSLocalizedString("Search", comment: "search shortcut title")
|
|
activity.suggestedInvocationPhrase = NSLocalizedString("Search the fediverse", comment: "search shortcut invocation phrase")
|
|
return activity
|
|
}
|
|
|
|
func handleSearch(activity: NSUserActivity) {
|
|
let mainViewController = getMainViewController()
|
|
mainViewController.select(tab: .explore)
|
|
if let navigationController = mainViewController.getTabController(tab: .explore) as? UINavigationController,
|
|
let exploreController = navigationController.viewControllers.first as? ExploreViewController {
|
|
navigationController.popToRootViewController(animated: false)
|
|
exploreController.loadViewIfNeeded()
|
|
exploreController.searchController.isActive = true
|
|
exploreController.searchController.searchBar.becomeFirstResponder()
|
|
}
|
|
}
|
|
|
|
static func bookmarksActivity() -> NSUserActivity {
|
|
let activity = NSUserActivity(type: .bookmarks)
|
|
activity.isEligibleForPrediction = true
|
|
activity.title = NSLocalizedString("View Bookmarks", comment: "bookmarks shortcut title")
|
|
activity.suggestedInvocationPhrase = NSLocalizedString("Show my bookmarks in Tusker", comment: "bookmarks shortcut invocation phrase")
|
|
return activity
|
|
}
|
|
|
|
func handleBookmarks(activity: NSUserActivity) {
|
|
let mainViewController = getMainViewController()
|
|
mainViewController.select(tab: .explore)
|
|
if let navigationController = mainViewController.getTabController(tab: .explore) as? UINavigationController {
|
|
navigationController.popToRootViewController(animated: false)
|
|
navigationController.pushViewController(BookmarksTableViewController(mastodonController: mastodonController), animated: false)
|
|
}
|
|
}
|
|
|
|
// MARK: - My Profile
|
|
static func myProfileActivity() -> NSUserActivity {
|
|
let activity = NSUserActivity(type: .myProfile)
|
|
activity.isEligibleForPrediction = true
|
|
activity.title = NSLocalizedString("My Profile", comment: "my profile shortcut title")
|
|
activity.suggestedInvocationPhrase = NSLocalizedString("Show my Mastodon profile", comment: "my profile shortuct invocation phrase")
|
|
return activity
|
|
}
|
|
|
|
func handleMyProfile(activity: NSUserActivity) {
|
|
let mainViewController = getMainViewController()
|
|
mainViewController.select(tab: .myProfile)
|
|
}
|
|
|
|
// MARK: - Show Profile
|
|
static func showProfileActivity(id profileID: String, accountID: String) -> NSUserActivity {
|
|
let activity = NSUserActivity(type: .showProfile)
|
|
activity.userInfo = [
|
|
"profileID": profileID,
|
|
"accountID": accountID,
|
|
]
|
|
// todo: should this be eligible for prediction?
|
|
return activity
|
|
}
|
|
|
|
static func getProfile(from activity: NSUserActivity) -> String? {
|
|
return activity.userInfo?["profileID"] as? String
|
|
}
|
|
|
|
}
|