Skip to content

Commit

Permalink
Merge pull request #400 from mastodon/feature-local-timeline
Browse files Browse the repository at this point in the history
Add Community tab into discovery scene
  • Loading branch information
MainasuK authored Apr 29, 2022
2 parents dff4a5f + 60a69cf commit e7739eb
Show file tree
Hide file tree
Showing 26 changed files with 899 additions and 268 deletions.
5 changes: 1 addition & 4 deletions AppShared/AppSecret.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@ import Foundation
import CryptoKit
import KeychainAccess
import Keys

enum AppName {
public static let groupID = "group.org.joinmastodon.app"
}
import MastodonCommon

public final class AppSecret {

Expand Down
340 changes: 132 additions & 208 deletions Mastodon.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1250"
LastUpgradeVersion = "1330"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1250"
LastUpgradeVersion = "1330"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1250"
LastUpgradeVersion = "1330"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>Mastodon - RTL.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>19</integer>
<integer>6</integer>
</dict>
<key>Mastodon - Release.xcscheme_^#shared#^_</key>
<dict>
Expand Down Expand Up @@ -109,7 +109,7 @@
<key>MastodonIntent.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>24</integer>
<integer>21</integer>
</dict>
<key>MastodonIntents.xcscheme_^#shared#^_</key>
<dict>
Expand All @@ -124,12 +124,12 @@
<key>NotificationService.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>22</integer>
<integer>19</integer>
</dict>
<key>ShareActionExtension.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>23</integer>
<integer>20</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
Expand Down
19 changes: 11 additions & 8 deletions Mastodon/Generated/AutoGenerateTableViewDelegate.generated.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
// Generated using Sourcery 1.6.1 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT








// sourcery:inline:UserTimelineViewController.AutoGenerateTableViewDelegate
// sourcery:inline:DiscoveryCommunityViewController.AutoGenerateTableViewDelegate

// Generated using Sourcery
// DO NOT EDIT
Expand All @@ -33,3 +26,13 @@ func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith con
}
// sourcery:end











64 changes: 64 additions & 0 deletions Mastodon/Scene/Discovery/Community/CommunityViewViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// DiscoveryCommunityViewViewModel.swift
// Mastodon
//
// Created by MainasuK on 2022-4-29.
//

import os.log
import UIKit
import Combine
import GameplayKit
import CoreData
import CoreDataStack
import MastodonSDK

final class DiscoveryCommunityViewViewModel {

let logger = Logger(subsystem: "DiscoveryCommunityViewViewModel", category: "ViewModel")

var disposeBag = Set<AnyCancellable>()

// input
let context: AppContext
let viewDidAppeared = PassthroughSubject<Void, Never>()
let statusFetchedResultsController: StatusFetchedResultsController

// output
var diffableDataSource: UITableViewDiffableDataSource<StatusSection, StatusItem>?
private(set) lazy var stateMachine: GKStateMachine = {
let stateMachine = GKStateMachine(states: [
State.Initial(viewModel: self),
State.Reloading(viewModel: self),
State.Fail(viewModel: self),
State.Idle(viewModel: self),
State.Loading(viewModel: self),
State.NoMore(viewModel: self),
])
stateMachine.enter(State.Initial.self)
return stateMachine
}()

let didLoadLatest = PassthroughSubject<Void, Never>()

init(context: AppContext) {
self.context = context
self.statusFetchedResultsController = StatusFetchedResultsController(
managedObjectContext: context.managedObjectContext,
domain: nil,
additionalTweetPredicate: nil
)
// end init

context.authenticationService.activeMastodonAuthentication
.map { $0?.domain }
.assign(to: \.value, on: statusFetchedResultsController.domain)
.store(in: &disposeBag)

}

deinit {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// DiscoveryCommunityViewController+DataSourceProvider.swift
// Mastodon
//
// Created by MainasuK on 2022-4-29.
//

import UIKit

extension DiscoveryCommunityViewController: DataSourceProvider {
func item(from source: DataSourceItem.Source) async -> DataSourceItem? {
var _indexPath = source.indexPath
if _indexPath == nil, let cell = source.tableViewCell {
_indexPath = await self.indexPath(for: cell)
}
guard let indexPath = _indexPath else { return nil }

guard let item = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else {
return nil
}

switch item {
case .status(let record):
return .status(record: record)
default:
return nil
}
}

@MainActor
private func indexPath(for cell: UITableViewCell) async -> IndexPath? {
return tableView.indexPath(for: cell)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
//
// DiscoveryCommunityViewController.swift
// Mastodon
//
// Created by MainasuK on 2022-4-29.
//

import os.log
import UIKit
import Combine
import MastodonUI

// Local Timeline
final class DiscoveryCommunityViewController: UIViewController, NeedsDependency, MediaPreviewableViewController {

let logger = Logger(subsystem: "DiscoveryCommunityViewController", category: "ViewController")

weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }

var disposeBag = Set<AnyCancellable>()
var viewModel: DiscoveryCommunityViewModel!

let mediaPreviewTransitionController = MediaPreviewTransitionController()

lazy var tableView: UITableView = {
let tableView = UITableView()
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 100
tableView.separatorStyle = .none
tableView.backgroundColor = .clear
return tableView
}()

let refreshControl = UIRefreshControl()

deinit {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
}

}

extension DiscoveryCommunityViewController {

override func viewDidLoad() {
super.viewDidLoad()

view.backgroundColor = ThemeService.shared.currentTheme.value.secondarySystemBackgroundColor
ThemeService.shared.currentTheme
.receive(on: DispatchQueue.main)
.sink { [weak self] theme in
guard let self = self else { return }
self.view.backgroundColor = theme.secondarySystemBackgroundColor
}
.store(in: &disposeBag)

tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])

tableView.refreshControl = refreshControl
refreshControl.addTarget(self, action: #selector(DiscoveryCommunityViewController.refreshControlValueChanged(_:)), for: .valueChanged)
viewModel.didLoadLatest
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
guard let self = self else { return }
self.refreshControl.endRefreshing()
}
.store(in: &disposeBag)

tableView.delegate = self
viewModel.setupDiffableDataSource(
tableView: tableView,
statusTableViewCellDelegate: self
)

// setup batch fetch
viewModel.listBatchFetchViewModel.setup(scrollView: tableView)
viewModel.listBatchFetchViewModel.shouldFetch
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
guard let self = self else { return }
guard self.view.window != nil else { return }
self.viewModel.stateMachine.enter(DiscoveryCommunityViewModel.State.Loading.self)
}
.store(in: &disposeBag)
}

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)

tableView.deselectRow(with: transitionCoordinator, animated: animated)
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

viewModel.viewDidAppeared.send()
}

}

extension DiscoveryCommunityViewController {

@objc private func refreshControlValueChanged(_ sender: UIRefreshControl) {
if !viewModel.stateMachine.enter(DiscoveryCommunityViewModel.State.Reloading.self) {
refreshControl.endRefreshing()
}
}

}

// MARK: - UITableViewDelegate
extension DiscoveryCommunityViewController: UITableViewDelegate, AutoGenerateTableViewDelegate {
// sourcery:inline:CommunityViewController.AutoGenerateTableViewDelegate

// Generated using Sourcery
// DO NOT EDIT
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
aspectTableView(tableView, didSelectRowAt: indexPath)
}

func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
return aspectTableView(tableView, contextMenuConfigurationForRowAt: indexPath, point: point)
}

func tableView(_ tableView: UITableView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
return aspectTableView(tableView, previewForHighlightingContextMenuWithConfiguration: configuration)
}

func tableView(_ tableView: UITableView, previewForDismissingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
return aspectTableView(tableView, previewForDismissingContextMenuWithConfiguration: configuration)
}

func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
aspectTableView(tableView, willPerformPreviewActionForMenuWith: configuration, animator: animator)
}
// sourcery:end
}

// MARK: - StatusTableViewCellDelegate
extension DiscoveryCommunityViewController: StatusTableViewCellDelegate { }

// MARK: ScrollViewContainer
extension DiscoveryCommunityViewController: ScrollViewContainer {
var scrollView: UIScrollView? {
tableView
}
}
Loading

0 comments on commit e7739eb

Please sign in to comment.