mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-25 01:31:02 +00:00
wip
This commit is contained in:
parent
6dd991f086
commit
02747215c5
12 changed files with 514 additions and 42 deletions
|
@ -18,7 +18,7 @@ public extension Timeline {
|
||||||
typealias Id = String
|
typealias Id = String
|
||||||
|
|
||||||
static let unauthenticatedDefaults: [Timeline] = [.local, .federated]
|
static let unauthenticatedDefaults: [Timeline] = [.local, .federated]
|
||||||
static let authenticatedDefaults: [Timeline] = [.home, .local, .federated, .favorites, .bookmarks]
|
static let authenticatedDefaults: [Timeline] = [.home, .local, .federated]
|
||||||
|
|
||||||
var filterContext: Filter.Context? {
|
var filterContext: Filter.Context? {
|
||||||
switch self {
|
switch self {
|
||||||
|
|
33
Extensions/NavigationViewModel+Extensions.swift
Normal file
33
Extensions/NavigationViewModel+Extensions.swift
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright © 2021 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import ViewModels
|
||||||
|
|
||||||
|
extension NavigationViewModel.Tab {
|
||||||
|
var title: String {
|
||||||
|
switch self {
|
||||||
|
case .timelines:
|
||||||
|
return NSLocalizedString("main-navigation.timelines", comment: "")
|
||||||
|
case .explore:
|
||||||
|
return NSLocalizedString("main-navigation.explore", comment: "")
|
||||||
|
case .notifications:
|
||||||
|
return NSLocalizedString("main-navigation.notifications", comment: "")
|
||||||
|
case .messages:
|
||||||
|
return NSLocalizedString("main-navigation.conversations", comment: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var systemImageName: String {
|
||||||
|
switch self {
|
||||||
|
case .timelines: return "newspaper"
|
||||||
|
case .explore: return "magnifyingglass"
|
||||||
|
case .notifications: return "bell"
|
||||||
|
case .messages: return "envelope"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tabBarItem: UITabBarItem {
|
||||||
|
UITabBarItem(title: title, image: UIImage(systemName: systemImageName), selectedImage: nil)
|
||||||
|
}
|
||||||
|
}
|
|
@ -88,6 +88,10 @@
|
||||||
"identities.pending" = "Pending";
|
"identities.pending" = "Pending";
|
||||||
"lists.new-list-title" = "New List Title";
|
"lists.new-list-title" = "New List Title";
|
||||||
"load-more" = "Load More";
|
"load-more" = "Load More";
|
||||||
|
"main-navigation.timelines" = "Timelines";
|
||||||
|
"main-navigation.explore" = "Explore";
|
||||||
|
"main-navigation.notifications" = "Notifications";
|
||||||
|
"main-navigation.conversations" = "Conversations";
|
||||||
"messages" = "Messages";
|
"messages" = "Messages";
|
||||||
"ok" = "OK";
|
"ok" = "OK";
|
||||||
"pending.pending-confirmation" = "Your account is pending confirmation";
|
"pending.pending-confirmation" = "Your account is pending confirmation";
|
||||||
|
@ -201,5 +205,8 @@
|
||||||
"status.visibility.direct.description" = "Visible for mentioned users only";
|
"status.visibility.direct.description" = "Visible for mentioned users only";
|
||||||
"submit" = "Submit";
|
"submit" = "Submit";
|
||||||
"timelines.home" = "Home";
|
"timelines.home" = "Home";
|
||||||
|
"timelines.home.description" = "Posts from accounts you're following";
|
||||||
"timelines.local" = "Local";
|
"timelines.local" = "Local";
|
||||||
|
"timelines.local.description-%@" = "Public posts on %@";
|
||||||
"timelines.federated" = "Federated";
|
"timelines.federated" = "Federated";
|
||||||
|
"timelines.federated.description-%@" = "Public posts on instances known by %@";
|
||||||
|
|
|
@ -23,6 +23,11 @@
|
||||||
D01F41D924F880C400D55A2D /* TouchFallthroughTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */; };
|
D01F41D924F880C400D55A2D /* TouchFallthroughTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */; };
|
||||||
D01F41E424F8889700D55A2D /* AttachmentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41E224F8889700D55A2D /* AttachmentsView.swift */; };
|
D01F41E424F8889700D55A2D /* AttachmentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41E224F8889700D55A2D /* AttachmentsView.swift */; };
|
||||||
D02E1F95250B13210071AD56 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02E1F94250B13210071AD56 /* SafariView.swift */; };
|
D02E1F95250B13210071AD56 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02E1F94250B13210071AD56 /* SafariView.swift */; };
|
||||||
|
D035F86925B7F2ED00DC75ED /* MainNavigationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D035F86825B7F2ED00DC75ED /* MainNavigationViewController.swift */; };
|
||||||
|
D035F86F25B7F30E00DC75ED /* MainNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D035F86E25B7F30E00DC75ED /* MainNavigationView.swift */; };
|
||||||
|
D035F87D25B7F61600DC75ED /* TimelinesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D035F87C25B7F61600DC75ED /* TimelinesViewController.swift */; };
|
||||||
|
D035F88725B8016000DC75ED /* NavigationViewModel+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D035F88625B8016000DC75ED /* NavigationViewModel+Extensions.swift */; };
|
||||||
|
D035F89125B8067100DC75ED /* TimelinesTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D035F89025B8067100DC75ED /* TimelinesTitleView.swift */; };
|
||||||
D036AA02254B6101009094DF /* NotificationListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D036AA01254B6101009094DF /* NotificationListCell.swift */; };
|
D036AA02254B6101009094DF /* NotificationListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D036AA01254B6101009094DF /* NotificationListCell.swift */; };
|
||||||
D036AA07254B6118009094DF /* NotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D036AA06254B6118009094DF /* NotificationView.swift */; };
|
D036AA07254B6118009094DF /* NotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D036AA06254B6118009094DF /* NotificationView.swift */; };
|
||||||
D036AA0C254B612B009094DF /* NotificationContentConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D036AA0B254B612B009094DF /* NotificationContentConfiguration.swift */; };
|
D036AA0C254B612B009094DF /* NotificationContentConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D036AA0B254B612B009094DF /* NotificationContentConfiguration.swift */; };
|
||||||
|
@ -204,6 +209,11 @@
|
||||||
D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchFallthroughTextView.swift; sourceTree = "<group>"; };
|
D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchFallthroughTextView.swift; sourceTree = "<group>"; };
|
||||||
D01F41E224F8889700D55A2D /* AttachmentsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentsView.swift; sourceTree = "<group>"; };
|
D01F41E224F8889700D55A2D /* AttachmentsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentsView.swift; sourceTree = "<group>"; };
|
||||||
D02E1F94250B13210071AD56 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = "<group>"; };
|
D02E1F94250B13210071AD56 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = "<group>"; };
|
||||||
|
D035F86825B7F2ED00DC75ED /* MainNavigationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainNavigationViewController.swift; sourceTree = "<group>"; };
|
||||||
|
D035F86E25B7F30E00DC75ED /* MainNavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainNavigationView.swift; sourceTree = "<group>"; };
|
||||||
|
D035F87C25B7F61600DC75ED /* TimelinesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelinesViewController.swift; sourceTree = "<group>"; };
|
||||||
|
D035F88625B8016000DC75ED /* NavigationViewModel+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NavigationViewModel+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
|
D035F89025B8067100DC75ED /* TimelinesTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelinesTitleView.swift; sourceTree = "<group>"; };
|
||||||
D036AA01254B6101009094DF /* NotificationListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationListCell.swift; sourceTree = "<group>"; };
|
D036AA01254B6101009094DF /* NotificationListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationListCell.swift; sourceTree = "<group>"; };
|
||||||
D036AA06254B6118009094DF /* NotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationView.swift; sourceTree = "<group>"; };
|
D036AA06254B6118009094DF /* NotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationView.swift; sourceTree = "<group>"; };
|
||||||
D036AA0B254B612B009094DF /* NotificationContentConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationContentConfiguration.swift; sourceTree = "<group>"; };
|
D036AA0B254B612B009094DF /* NotificationContentConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationContentConfiguration.swift; sourceTree = "<group>"; };
|
||||||
|
@ -504,6 +514,7 @@
|
||||||
D0B8510B25259E56004E0744 /* LoadMoreCell.swift */,
|
D0B8510B25259E56004E0744 /* LoadMoreCell.swift */,
|
||||||
D0E569DF252931B100FA1D72 /* LoadMoreContentConfiguration.swift */,
|
D0E569DF252931B100FA1D72 /* LoadMoreContentConfiguration.swift */,
|
||||||
D0E569DA2529319100FA1D72 /* LoadMoreView.swift */,
|
D0E569DA2529319100FA1D72 /* LoadMoreView.swift */,
|
||||||
|
D035F86E25B7F30E00DC75ED /* MainNavigationView.swift */,
|
||||||
D05936FE25AA94EA00754FDF /* MarkAttachmentsSensitiveView.swift */,
|
D05936FE25AA94EA00754FDF /* MarkAttachmentsSensitiveView.swift */,
|
||||||
D03B1B29253818F3008F964B /* MediaPreferencesView.swift */,
|
D03B1B29253818F3008F964B /* MediaPreferencesView.swift */,
|
||||||
D0FCC10F259C4F20000B67DF /* NewStatusView.swift */,
|
D0FCC10F259C4F20000B67DF /* NewStatusView.swift */,
|
||||||
|
@ -526,6 +537,7 @@
|
||||||
D0625E55250F086B00502611 /* Status */,
|
D0625E55250F086B00502611 /* Status */,
|
||||||
D0C7D42524F76169001EBDBB /* TableView.swift */,
|
D0C7D42524F76169001EBDBB /* TableView.swift */,
|
||||||
D0C7D42E24F76169001EBDBB /* TabNavigationView.swift */,
|
D0C7D42E24F76169001EBDBB /* TabNavigationView.swift */,
|
||||||
|
D035F89025B8067100DC75ED /* TimelinesTitleView.swift */,
|
||||||
D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */,
|
D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */,
|
||||||
D0EA59472522B8B600804347 /* ViewConstants.swift */,
|
D0EA59472522B8B600804347 /* ViewConstants.swift */,
|
||||||
D0F2D54A2581CF7D00986197 /* VisualEffectBlur.swift */,
|
D0F2D54A2581CF7D00986197 /* VisualEffectBlur.swift */,
|
||||||
|
@ -542,9 +554,11 @@
|
||||||
D08B8D49253FC36500B1EBEF /* ImageNavigationController.swift */,
|
D08B8D49253FC36500B1EBEF /* ImageNavigationController.swift */,
|
||||||
D08B8D41253F92B600B1EBEF /* ImagePageViewController.swift */,
|
D08B8D41253F92B600B1EBEF /* ImagePageViewController.swift */,
|
||||||
D08B8D3C253F929E00B1EBEF /* ImageViewController.swift */,
|
D08B8D3C253F929E00B1EBEF /* ImageViewController.swift */,
|
||||||
|
D035F86825B7F2ED00DC75ED /* MainNavigationViewController.swift */,
|
||||||
D0FCC104259C4E61000B67DF /* NewStatusViewController.swift */,
|
D0FCC104259C4E61000B67DF /* NewStatusViewController.swift */,
|
||||||
D06BC5E525202AD90079541D /* ProfileViewController.swift */,
|
D06BC5E525202AD90079541D /* ProfileViewController.swift */,
|
||||||
D0F0B12D251A97E400942152 /* TableViewController.swift */,
|
D0F0B12D251A97E400942152 /* TableViewController.swift */,
|
||||||
|
D035F87C25B7F61600DC75ED /* TimelinesViewController.swift */,
|
||||||
);
|
);
|
||||||
path = "View Controllers";
|
path = "View Controllers";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -574,6 +588,7 @@
|
||||||
D05E688425B55AE8001FB2C6 /* AVURLAsset+Extensions.swift */,
|
D05E688425B55AE8001FB2C6 /* AVURLAsset+Extensions.swift */,
|
||||||
D0F0B135251AA12700942152 /* CollectionItem+Extensions.swift */,
|
D0F0B135251AA12700942152 /* CollectionItem+Extensions.swift */,
|
||||||
D0C7D46E24F76169001EBDBB /* KingfisherOptionsInfo+Extensions.swift */,
|
D0C7D46E24F76169001EBDBB /* KingfisherOptionsInfo+Extensions.swift */,
|
||||||
|
D035F88625B8016000DC75ED /* NavigationViewModel+Extensions.swift */,
|
||||||
D0C7D46B24F76169001EBDBB /* NSMutableAttributedString+Extensions.swift */,
|
D0C7D46B24F76169001EBDBB /* NSMutableAttributedString+Extensions.swift */,
|
||||||
D07EC7CE25B13921006DF726 /* PickerEmoji+Extensions.swift */,
|
D07EC7CE25B13921006DF726 /* PickerEmoji+Extensions.swift */,
|
||||||
D0B5FE9A251583DB00478838 /* ProfileCollection+Extensions.swift */,
|
D0B5FE9A251583DB00478838 /* ProfileCollection+Extensions.swift */,
|
||||||
|
@ -856,6 +871,7 @@
|
||||||
D08B8D822544D80000B1EBEF /* PollOptionButton.swift in Sources */,
|
D08B8D822544D80000B1EBEF /* PollOptionButton.swift in Sources */,
|
||||||
D0C7D4DA24F7616A001EBDBB /* View+Extensions.swift in Sources */,
|
D0C7D4DA24F7616A001EBDBB /* View+Extensions.swift in Sources */,
|
||||||
D07EC81125B232C2006DF726 /* SystemEmoji+Extensions.swift in Sources */,
|
D07EC81125B232C2006DF726 /* SystemEmoji+Extensions.swift in Sources */,
|
||||||
|
D035F87D25B7F61600DC75ED /* TimelinesViewController.swift in Sources */,
|
||||||
D059373325AAEA7000754FDF /* CompositionPollView.swift in Sources */,
|
D059373325AAEA7000754FDF /* CompositionPollView.swift in Sources */,
|
||||||
D08B8D8D2544E6EC00B1EBEF /* PollResultView.swift in Sources */,
|
D08B8D8D2544E6EC00B1EBEF /* PollResultView.swift in Sources */,
|
||||||
D0C7D4D524F7616A001EBDBB /* String+Extensions.swift in Sources */,
|
D0C7D4D524F7616A001EBDBB /* String+Extensions.swift in Sources */,
|
||||||
|
@ -874,7 +890,9 @@
|
||||||
D0C7D49724F7616A001EBDBB /* IdentitiesView.swift in Sources */,
|
D0C7D49724F7616A001EBDBB /* IdentitiesView.swift in Sources */,
|
||||||
D01EF22425182B1F00650C6B /* AccountHeaderView.swift in Sources */,
|
D01EF22425182B1F00650C6B /* AccountHeaderView.swift in Sources */,
|
||||||
D059373E25AB8D5200754FDF /* CompositionPollOptionView.swift in Sources */,
|
D059373E25AB8D5200754FDF /* CompositionPollOptionView.swift in Sources */,
|
||||||
|
D035F89125B8067100DC75ED /* TimelinesTitleView.swift in Sources */,
|
||||||
D036AA17254CA824009094DF /* StatusBodyView.swift in Sources */,
|
D036AA17254CA824009094DF /* StatusBodyView.swift in Sources */,
|
||||||
|
D035F86F25B7F30E00DC75ED /* MainNavigationView.swift in Sources */,
|
||||||
D08E512125786A6600FA2C5F /* UIButton+Extensions.swift in Sources */,
|
D08E512125786A6600FA2C5F /* UIButton+Extensions.swift in Sources */,
|
||||||
D05936F425AA66A600754FDF /* UIView+Extensions.swift in Sources */,
|
D05936F425AA66A600754FDF /* UIView+Extensions.swift in Sources */,
|
||||||
D05936E925AA3F3D00754FDF /* EditAttachmentView.swift in Sources */,
|
D05936E925AA3F3D00754FDF /* EditAttachmentView.swift in Sources */,
|
||||||
|
@ -883,6 +901,7 @@
|
||||||
D036AA0C254B612B009094DF /* NotificationContentConfiguration.swift in Sources */,
|
D036AA0C254B612B009094DF /* NotificationContentConfiguration.swift in Sources */,
|
||||||
D0C7D49824F7616A001EBDBB /* CustomEmojiText.swift in Sources */,
|
D0C7D49824F7616A001EBDBB /* CustomEmojiText.swift in Sources */,
|
||||||
D08B8D3D253F929E00B1EBEF /* ImageViewController.swift in Sources */,
|
D08B8D3D253F929E00B1EBEF /* ImageViewController.swift in Sources */,
|
||||||
|
D035F86925B7F2ED00DC75ED /* MainNavigationViewController.swift in Sources */,
|
||||||
D0B8510C25259E56004E0744 /* LoadMoreCell.swift in Sources */,
|
D0B8510C25259E56004E0744 /* LoadMoreCell.swift in Sources */,
|
||||||
D08E52612579D2E100FA2C5F /* DomainBlocksView.swift in Sources */,
|
D08E52612579D2E100FA2C5F /* DomainBlocksView.swift in Sources */,
|
||||||
D01F41E424F8889700D55A2D /* AttachmentsView.swift in Sources */,
|
D01F41E424F8889700D55A2D /* AttachmentsView.swift in Sources */,
|
||||||
|
@ -901,6 +920,7 @@
|
||||||
D0C7D4C324F7616A001EBDBB /* MetatextApp.swift in Sources */,
|
D0C7D4C324F7616A001EBDBB /* MetatextApp.swift in Sources */,
|
||||||
D0E1F583251F13EC00D45315 /* WebfingerIndicatorView.swift in Sources */,
|
D0E1F583251F13EC00D45315 /* WebfingerIndicatorView.swift in Sources */,
|
||||||
D0BEB20524FA1107001B0F04 /* FiltersView.swift in Sources */,
|
D0BEB20524FA1107001B0F04 /* FiltersView.swift in Sources */,
|
||||||
|
D035F88725B8016000DC75ED /* NavigationViewModel+Extensions.swift in Sources */,
|
||||||
D0FCC110259C4F20000B67DF /* NewStatusView.swift in Sources */,
|
D0FCC110259C4F20000B67DF /* NewStatusView.swift in Sources */,
|
||||||
D0C7D49B24F7616A001EBDBB /* PreferencesView.swift in Sources */,
|
D0C7D49B24F7616A001EBDBB /* PreferencesView.swift in Sources */,
|
||||||
D088406D25AFBBE200BB749B /* EmojiPickerViewController.swift in Sources */,
|
D088406D25AFBBE200BB749B /* EmojiPickerViewController.swift in Sources */,
|
||||||
|
|
64
View Controllers/MainNavigationViewController.swift
Normal file
64
View Controllers/MainNavigationViewController.swift
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright © 2021 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import ViewModels
|
||||||
|
|
||||||
|
final class MainNavigationViewController: UITabBarController {
|
||||||
|
private let viewModel: NavigationViewModel
|
||||||
|
private let rootViewModel: RootViewModel
|
||||||
|
|
||||||
|
init(viewModel: NavigationViewModel, rootViewModel: RootViewModel) {
|
||||||
|
self.viewModel = viewModel
|
||||||
|
self.rootViewModel = rootViewModel
|
||||||
|
|
||||||
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, unavailable)
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
let timelinesViewController = TimelinesViewController(
|
||||||
|
viewModel: viewModel,
|
||||||
|
rootViewModel: rootViewModel)
|
||||||
|
let timelinesNavigationController = UINavigationController(rootViewController: timelinesViewController)
|
||||||
|
|
||||||
|
if let notificationsViewModel = viewModel.notificationsViewModel,
|
||||||
|
let conversationsViewModel = viewModel.conversationsViewModel {
|
||||||
|
let notificationsViewController = TableViewController(
|
||||||
|
viewModel: notificationsViewModel,
|
||||||
|
rootViewModel: rootViewModel,
|
||||||
|
identification: viewModel.identification)
|
||||||
|
|
||||||
|
notificationsViewController.tabBarItem = NavigationViewModel.Tab.notifications.tabBarItem
|
||||||
|
|
||||||
|
let notificationsNavigationViewController = UINavigationController(
|
||||||
|
rootViewController: notificationsViewController)
|
||||||
|
|
||||||
|
let conversationsViewController = TableViewController(
|
||||||
|
viewModel: conversationsViewModel,
|
||||||
|
rootViewModel: rootViewModel,
|
||||||
|
identification: viewModel.identification)
|
||||||
|
|
||||||
|
conversationsViewController.tabBarItem = NavigationViewModel.Tab.messages.tabBarItem
|
||||||
|
conversationsViewController.navigationItem.title = NavigationViewModel.Tab.messages.title
|
||||||
|
|
||||||
|
let conversationsNavigationViewController = UINavigationController(
|
||||||
|
rootViewController: conversationsViewController)
|
||||||
|
|
||||||
|
viewControllers = [
|
||||||
|
timelinesNavigationController,
|
||||||
|
notificationsNavigationViewController,
|
||||||
|
conversationsNavigationViewController
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
viewControllers = [
|
||||||
|
timelinesNavigationController
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
131
View Controllers/TimelinesViewController.swift
Normal file
131
View Controllers/TimelinesViewController.swift
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
// Copyright © 2021 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import UIKit
|
||||||
|
import ViewModels
|
||||||
|
|
||||||
|
final class TimelinesViewController: UIPageViewController {
|
||||||
|
private let titleView: TimelinesTitleView
|
||||||
|
private let timelineViewControllers: [TableViewController]
|
||||||
|
private let viewModel: NavigationViewModel
|
||||||
|
private let rootViewModel: RootViewModel
|
||||||
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
init(viewModel: NavigationViewModel, rootViewModel: RootViewModel) {
|
||||||
|
self.viewModel = viewModel
|
||||||
|
self.rootViewModel = rootViewModel
|
||||||
|
|
||||||
|
let timelineViewModels: [CollectionViewModel]
|
||||||
|
|
||||||
|
if let homeTimelineViewModel = viewModel.homeTimelineViewModel {
|
||||||
|
timelineViewModels = [
|
||||||
|
homeTimelineViewModel,
|
||||||
|
viewModel.localTimelineViewModel,
|
||||||
|
viewModel.federatedTimelineViewModel]
|
||||||
|
} else {
|
||||||
|
timelineViewModels = [
|
||||||
|
viewModel.localTimelineViewModel,
|
||||||
|
viewModel.federatedTimelineViewModel]
|
||||||
|
}
|
||||||
|
|
||||||
|
titleView = TimelinesTitleView(
|
||||||
|
timelines: viewModel.identification.identity.authenticated
|
||||||
|
? Timeline.authenticatedDefaults
|
||||||
|
: Timeline.unauthenticatedDefaults,
|
||||||
|
identification: viewModel.identification)
|
||||||
|
|
||||||
|
timelineViewControllers = timelineViewModels.map {
|
||||||
|
TableViewController(
|
||||||
|
viewModel: $0,
|
||||||
|
rootViewModel: rootViewModel,
|
||||||
|
identification: viewModel.identification)
|
||||||
|
}
|
||||||
|
|
||||||
|
super.init(transitionStyle: .scroll,
|
||||||
|
navigationOrientation: .horizontal,
|
||||||
|
options: [.interPageSpacing: CGFloat.defaultSpacing])
|
||||||
|
|
||||||
|
if let firstViewController = timelineViewControllers.first {
|
||||||
|
setViewControllers([firstViewController], direction: .forward, animated: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, unavailable)
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
dataSource = self
|
||||||
|
delegate = self
|
||||||
|
|
||||||
|
tabBarItem = UITabBarItem(
|
||||||
|
title: NSLocalizedString("main-navigation.timelines", comment: ""),
|
||||||
|
image: UIImage(systemName: "newspaper"),
|
||||||
|
selectedImage: nil)
|
||||||
|
|
||||||
|
navigationItem.titleView = titleView
|
||||||
|
|
||||||
|
navigationItem.leftBarButtonItem = UIBarButtonItem(systemItem: .close)
|
||||||
|
navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "megaphone"), primaryAction: nil)
|
||||||
|
|
||||||
|
titleView.$selectedTimeline
|
||||||
|
.compactMap { [weak self] in self?.titleView.timelines.firstIndex(of: $0) }
|
||||||
|
.sink { [weak self] index in
|
||||||
|
guard let self = self,
|
||||||
|
let currentViewController = self.viewControllers?.first as? TableViewController,
|
||||||
|
let currentIndex = self.timelineViewControllers.firstIndex(of: currentViewController),
|
||||||
|
index != currentIndex
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
self.setViewControllers(
|
||||||
|
[self.timelineViewControllers[index]],
|
||||||
|
direction: index > currentIndex ? .forward : .reverse,
|
||||||
|
animated: !UIAccessibility.isReduceMotionEnabled)
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TimelinesViewController: UIPageViewControllerDataSource {
|
||||||
|
func pageViewController(_ pageViewController: UIPageViewController,
|
||||||
|
viewControllerAfter viewController: UIViewController) -> UIViewController? {
|
||||||
|
guard
|
||||||
|
let timelineViewController = viewController as? TableViewController,
|
||||||
|
let index = timelineViewControllers.firstIndex(of: timelineViewController),
|
||||||
|
index + 1 < timelineViewControllers.count
|
||||||
|
else { return nil }
|
||||||
|
|
||||||
|
return timelineViewControllers[index + 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func pageViewController(_ pageViewController: UIPageViewController,
|
||||||
|
viewControllerBefore viewController: UIViewController) -> UIViewController? {
|
||||||
|
guard
|
||||||
|
let timelineViewController = viewController as? TableViewController,
|
||||||
|
let index = timelineViewControllers.firstIndex(of: timelineViewController),
|
||||||
|
index > 0
|
||||||
|
else { return nil }
|
||||||
|
|
||||||
|
return timelineViewControllers[index - 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TimelinesViewController: UIPageViewControllerDelegate {
|
||||||
|
func pageViewController(_ pageViewController: UIPageViewController,
|
||||||
|
didFinishAnimating finished: Bool,
|
||||||
|
previousViewControllers: [UIViewController],
|
||||||
|
transitionCompleted completed: Bool) {
|
||||||
|
guard let viewController = viewControllers?.first as? TableViewController,
|
||||||
|
let index = timelineViewControllers.firstIndex(of: viewController)
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
let timeline = titleView.timelines[index]
|
||||||
|
|
||||||
|
if titleView.selectedTimeline != timeline {
|
||||||
|
titleView.selectedTimeline = timeline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,38 +21,56 @@ public final class NavigationViewModel: ObservableObject {
|
||||||
@Published public var alertItem: AlertItem?
|
@Published public var alertItem: AlertItem?
|
||||||
public private(set) var timelineViewModel: CollectionItemsViewModel
|
public private(set) var timelineViewModel: CollectionItemsViewModel
|
||||||
|
|
||||||
public var notificationsViewModel: CollectionViewModel? {
|
public lazy var homeTimelineViewModel: CollectionViewModel? = {
|
||||||
if identification.identity.authenticated {
|
if identification.identity.authenticated {
|
||||||
if _notificationsViewModel == nil {
|
return CollectionItemsViewModel(
|
||||||
_notificationsViewModel = CollectionItemsViewModel(
|
collectionService: identification.service.service(timeline: .home),
|
||||||
|
identification: identification)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
public lazy var localTimelineViewModel: CollectionViewModel = {
|
||||||
|
CollectionItemsViewModel(
|
||||||
|
collectionService: identification.service.service(timeline: .local),
|
||||||
|
identification: identification)
|
||||||
|
}()
|
||||||
|
|
||||||
|
public lazy var federatedTimelineViewModel: CollectionViewModel = {
|
||||||
|
CollectionItemsViewModel(
|
||||||
|
collectionService: identification.service.service(timeline: .federated),
|
||||||
|
identification: identification)
|
||||||
|
}()
|
||||||
|
|
||||||
|
public lazy var notificationsViewModel: CollectionViewModel? = {
|
||||||
|
if identification.identity.authenticated {
|
||||||
|
let notificationsViewModel = CollectionItemsViewModel(
|
||||||
collectionService: identification.service.notificationsService(),
|
collectionService: identification.service.notificationsService(),
|
||||||
identification: identification)
|
identification: identification)
|
||||||
_notificationsViewModel?.request(maxId: nil, minId: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
return _notificationsViewModel
|
notificationsViewModel.request(maxId: nil, minId: nil)
|
||||||
|
|
||||||
|
return notificationsViewModel
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}()
|
||||||
|
|
||||||
public var conversationsViewModel: CollectionViewModel? {
|
public lazy var conversationsViewModel: CollectionViewModel? = {
|
||||||
if identification.identity.authenticated {
|
if identification.identity.authenticated {
|
||||||
if _conversationsViewModel == nil {
|
let conversationsViewModel = CollectionItemsViewModel(
|
||||||
_conversationsViewModel = CollectionItemsViewModel(
|
|
||||||
collectionService: identification.service.conversationsService(),
|
collectionService: identification.service.conversationsService(),
|
||||||
identification: identification)
|
identification: identification)
|
||||||
_conversationsViewModel?.request(maxId: nil, minId: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
return _conversationsViewModel
|
conversationsViewModel.request(maxId: nil, minId: nil)
|
||||||
|
|
||||||
|
return conversationsViewModel
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}()
|
||||||
|
|
||||||
private var _notificationsViewModel: CollectionViewModel?
|
|
||||||
private var _conversationsViewModel: CollectionViewModel?
|
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
public init(identification: Identification) {
|
public init(identification: Identification) {
|
||||||
|
|
32
Views/MainNavigationView.swift
Normal file
32
Views/MainNavigationView.swift
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright © 2021 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import ViewModels
|
||||||
|
|
||||||
|
struct MainNavigationView: UIViewControllerRepresentable {
|
||||||
|
let viewModelClosure: () -> NavigationViewModel
|
||||||
|
@EnvironmentObject var rootViewModel: RootViewModel
|
||||||
|
@EnvironmentObject var identification: Identification
|
||||||
|
|
||||||
|
func makeUIViewController(context: Context) -> MainNavigationViewController {
|
||||||
|
MainNavigationViewController(
|
||||||
|
viewModel: viewModelClosure(),
|
||||||
|
rootViewModel: rootViewModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIViewController(_ uiViewController: MainNavigationViewController, context: Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
import PreviewViewModels
|
||||||
|
|
||||||
|
struct MainNavigationView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
MainNavigationView { NavigationViewModel(identification: .preview) }
|
||||||
|
.environmentObject(Identification.preview)
|
||||||
|
.environmentObject(RootViewModel.preview)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -8,10 +8,11 @@ struct RootView: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if let navigationViewModel = viewModel.navigationViewModel {
|
if let navigationViewModel = viewModel.navigationViewModel {
|
||||||
TabNavigationView(viewModel: navigationViewModel)
|
MainNavigationView { navigationViewModel }
|
||||||
.id(navigationViewModel.identification.identity.id)
|
.id(navigationViewModel.identification.identity.id)
|
||||||
.environmentObject(viewModel)
|
.environmentObject(viewModel)
|
||||||
.transition(.opacity)
|
.transition(.opacity)
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
} else {
|
} else {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
AddIdentityView(viewModel: viewModel.addIdentityViewModel())
|
AddIdentityView(viewModel: viewModel.addIdentityViewModel())
|
||||||
|
|
|
@ -188,7 +188,8 @@ private extension TabNavigationView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension Timeline {
|
// TODO: move
|
||||||
|
extension Timeline {
|
||||||
var title: String {
|
var title: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .home:
|
case .home:
|
||||||
|
@ -210,10 +211,40 @@ private extension Timeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func subtitle(identification: Identification) -> String? {
|
||||||
|
switch self {
|
||||||
|
case .home:
|
||||||
|
return identification.identity.handle
|
||||||
|
default:
|
||||||
|
return identification.identity.instance?.uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func description(instanceName: String?) -> String? {
|
||||||
|
switch self {
|
||||||
|
case .home:
|
||||||
|
return NSLocalizedString("timelines.home.description", comment: "")
|
||||||
|
case .local:
|
||||||
|
guard let instanceName = instanceName else { return nil }
|
||||||
|
|
||||||
|
return String.localizedStringWithFormat(
|
||||||
|
NSLocalizedString("timelines.local.description-%@", comment: ""),
|
||||||
|
instanceName)
|
||||||
|
case .federated:
|
||||||
|
guard let instanceName = instanceName else { return nil }
|
||||||
|
|
||||||
|
return String.localizedStringWithFormat(
|
||||||
|
NSLocalizedString("timelines.federated.description-%@", comment: ""),
|
||||||
|
instanceName)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var systemImageName: String {
|
var systemImageName: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .home: return "house"
|
case .home: return "house"
|
||||||
case .local: return "person.3"
|
case .local: return "building.2.crop.circle"
|
||||||
case .federated: return "network"
|
case .federated: return "network"
|
||||||
case .list: return "scroll"
|
case .list: return "scroll"
|
||||||
case .tag: return "number"
|
case .tag: return "number"
|
||||||
|
@ -224,26 +255,6 @@ private extension Timeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NavigationViewModel.Tab {
|
|
||||||
var title: String {
|
|
||||||
switch self {
|
|
||||||
case .timelines: return "Timelines"
|
|
||||||
case .explore: return "Explore"
|
|
||||||
case .notifications: return "Notifications"
|
|
||||||
case .messages: return "Messages"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var systemImageName: String {
|
|
||||||
switch self {
|
|
||||||
case .timelines: return "newspaper"
|
|
||||||
case .explore: return "magnifyingglass"
|
|
||||||
case .notifications: return "bell"
|
|
||||||
case .messages: return "envelope"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
import PreviewViewModels
|
import PreviewViewModels
|
||||||
|
|
||||||
|
|
153
Views/TimelinesTitleView.swift
Normal file
153
Views/TimelinesTitleView.swift
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
// Copyright © 2021 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import UIKit
|
||||||
|
import ViewModels
|
||||||
|
|
||||||
|
final class TimelinesTitleView: UIControl {
|
||||||
|
let timelines: [Timeline]
|
||||||
|
private let titleLabel = UILabel()
|
||||||
|
private let subtitleLabel = UILabel()
|
||||||
|
private let imageView = UIImageView()
|
||||||
|
private let chevronImageView = UIImageView(image: TimelinesTitleView.closedImage)
|
||||||
|
private let identification: Identification
|
||||||
|
|
||||||
|
@Published var selectedTimeline: Timeline {
|
||||||
|
didSet { applyTimelineSelection() }
|
||||||
|
}
|
||||||
|
|
||||||
|
init(timelines: [Timeline], identification: Identification) {
|
||||||
|
self.timelines = timelines
|
||||||
|
self.identification = identification
|
||||||
|
|
||||||
|
guard let timeline = timelines.first else {
|
||||||
|
fatalError("TimelinesTitleView must be initialized with a non-empty timelines array")
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedTimeline = timeline
|
||||||
|
|
||||||
|
super.init(frame: .zero)
|
||||||
|
|
||||||
|
accessibilityTraits = .button
|
||||||
|
isAccessibilityElement = true
|
||||||
|
showsMenuAsPrimaryAction = true
|
||||||
|
isContextMenuInteractionEnabled = true
|
||||||
|
|
||||||
|
addSubview(imageView)
|
||||||
|
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
imageView.contentMode = .scaleAspectFit
|
||||||
|
imageView.setContentHuggingPriority(.required, for: .horizontal)
|
||||||
|
imageView.tintColor = .label
|
||||||
|
|
||||||
|
addSubview(chevronImageView)
|
||||||
|
chevronImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
chevronImageView.contentMode = .scaleAspectFit
|
||||||
|
chevronImageView.setContentHuggingPriority(.required, for: .horizontal)
|
||||||
|
|
||||||
|
addSubview(titleLabel)
|
||||||
|
titleLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
titleLabel.adjustsFontForContentSizeCategory = true
|
||||||
|
titleLabel.font = .preferredFont(forTextStyle: .headline)
|
||||||
|
titleLabel.adjustsFontSizeToFitWidth = true
|
||||||
|
titleLabel.minimumScaleFactor = 0.5
|
||||||
|
titleLabel.setContentHuggingPriority(.required, for: .horizontal)
|
||||||
|
titleLabel.setContentHuggingPriority(.required, for: .vertical)
|
||||||
|
titleLabel.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
|
|
||||||
|
addSubview(subtitleLabel)
|
||||||
|
subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
subtitleLabel.adjustsFontForContentSizeCategory = true
|
||||||
|
subtitleLabel.font = .preferredFont(forTextStyle: .caption2)
|
||||||
|
subtitleLabel.adjustsFontSizeToFitWidth = true
|
||||||
|
subtitleLabel.textAlignment = .center
|
||||||
|
subtitleLabel.minimumScaleFactor = 0.5
|
||||||
|
subtitleLabel.textColor = .secondaryLabel
|
||||||
|
subtitleLabel.setContentHuggingPriority(.required, for: .vertical)
|
||||||
|
subtitleLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||||
|
subtitleLabel.setContentCompressionResistancePriority(.justBelowMax, for: .vertical)
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
imageView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor),
|
||||||
|
imageView.topAnchor.constraint(equalTo: titleLabel.topAnchor),
|
||||||
|
imageView.bottomAnchor.constraint(equalTo: titleLabel.bottomAnchor),
|
||||||
|
titleLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
|
||||||
|
titleLabel.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: .compactSpacing),
|
||||||
|
titleLabel.topAnchor.constraint(equalTo: topAnchor),
|
||||||
|
chevronImageView.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor, constant: .defaultSpacing),
|
||||||
|
chevronImageView.topAnchor.constraint(equalTo: titleLabel.topAnchor),
|
||||||
|
chevronImageView.bottomAnchor.constraint(equalTo: titleLabel.bottomAnchor),
|
||||||
|
chevronImageView.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor),
|
||||||
|
subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor),
|
||||||
|
subtitleLabel.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||||
|
subtitleLabel.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||||
|
subtitleLabel.bottomAnchor.constraint(equalTo: bottomAnchor)
|
||||||
|
])
|
||||||
|
|
||||||
|
applyTimelineSelection()
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, unavailable)
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override var isHighlighted: Bool {
|
||||||
|
didSet {
|
||||||
|
alpha = isHighlighted ? Self.highlightedAlpha : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func menuAttachmentPoint(for configuration: UIContextMenuConfiguration) -> CGPoint {
|
||||||
|
CGPoint(x: (bounds.width - .systemMenuWidth) / 2 + .systemMenuInset, y: bounds.maxY + .compactSpacing)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func contextMenuInteraction(
|
||||||
|
_ interaction: UIContextMenuInteraction,
|
||||||
|
configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
|
||||||
|
UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { [weak self] _ in
|
||||||
|
guard let self = self else { return nil }
|
||||||
|
|
||||||
|
return UIMenu(children: self.timelines.map { timeline in
|
||||||
|
UIAction(
|
||||||
|
title: timeline.title,
|
||||||
|
image: UIImage(systemName: timeline.systemImageName),
|
||||||
|
attributes: timeline == self.selectedTimeline ? .disabled : [],
|
||||||
|
state: timeline == self.selectedTimeline ? .on : .off) { _ in
|
||||||
|
self.selectedTimeline = timeline
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func contextMenuInteraction(
|
||||||
|
_ interaction: UIContextMenuInteraction,
|
||||||
|
willDisplayMenuFor configuration: UIContextMenuConfiguration,
|
||||||
|
animator: UIContextMenuInteractionAnimating?) {
|
||||||
|
chevronImageView.image = Self.openImage
|
||||||
|
}
|
||||||
|
|
||||||
|
override func contextMenuInteraction(
|
||||||
|
_ interaction: UIContextMenuInteraction,
|
||||||
|
willEndFor configuration: UIContextMenuConfiguration,
|
||||||
|
animator: UIContextMenuInteractionAnimating?) {
|
||||||
|
chevronImageView.image = Self.closedImage
|
||||||
|
alpha = 1 // system bug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension TimelinesTitleView {
|
||||||
|
static let highlightedAlpha: CGFloat = 0.5
|
||||||
|
static let openImage = UIImage(
|
||||||
|
systemName: "chevron.compact.up",
|
||||||
|
withConfiguration: UIImage.SymbolConfiguration(scale: .small))
|
||||||
|
static let closedImage = UIImage(
|
||||||
|
systemName: "chevron.compact.down",
|
||||||
|
withConfiguration: UIImage.SymbolConfiguration(scale: .small))
|
||||||
|
func applyTimelineSelection() {
|
||||||
|
imageView.image = UIImage(
|
||||||
|
systemName: selectedTimeline.systemImageName,
|
||||||
|
withConfiguration: UIImage.SymbolConfiguration(scale: .small))
|
||||||
|
titleLabel.text = selectedTimeline.title
|
||||||
|
subtitleLabel.text = selectedTimeline.subtitle(identification: identification)
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,8 +11,10 @@ extension CGFloat {
|
||||||
static let hairline = 1 / UIScreen.main.scale
|
static let hairline = 1 / UIScreen.main.scale
|
||||||
static let minimumButtonDimension: Self = 44
|
static let minimumButtonDimension: Self = 44
|
||||||
static let barButtonItemDimension: Self = 28
|
static let barButtonItemDimension: Self = 28
|
||||||
static let newStatusButtonDimension: CGFloat = 54
|
static let newStatusButtonDimension: Self = 54
|
||||||
static let defaultShadowRadius: CGFloat = 2
|
static let defaultShadowRadius: Self = 2
|
||||||
|
static let systemMenuWidth: Self = 250
|
||||||
|
static let systemMenuInset: Self = 15
|
||||||
}
|
}
|
||||||
|
|
||||||
extension CGRect {
|
extension CGRect {
|
||||||
|
|
Loading…
Reference in a new issue