Skip to content

Commit

Permalink
Merge pull request #22 from shinrenpan/develop
Browse files Browse the repository at this point in the history
v0.9.7
  • Loading branch information
shinrenpan authored Jul 22, 2024
2 parents 8369d90 + 15050d8 commit 9072a5e
Show file tree
Hide file tree
Showing 32 changed files with 675 additions and 405 deletions.
40 changes: 37 additions & 3 deletions Sources/App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@ import UIKit
@main
class AppDelegate: UIResponder {
var window: UIWindow?

override init() {
super.init()
setupBinding()
setupAppearance()
}
}

// MARK: - UIApplicationDelegate

extension AppDelegate: UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
setupAppearance()

let bounds = UIScreen.main.bounds
let window = UIWindow(frame: bounds)
window.backgroundColor = .white
Expand All @@ -30,9 +34,23 @@ extension AppDelegate: UIApplicationDelegate {

// MARK: - Private

extension AppDelegate {
private extension AppDelegate {
// MARK: Setup Something

func setupBinding() {
NotificationCenter.default.addObserver(forName: .showLoading, object: nil, queue: .main) { [weak self] notify in
LoadingView.hide()

guard let self else { return }

guard let loadingView = notify.object as? LoadingView else {
return
}

showLoadingView(view: loadingView)
}
}

func setupAppearance() {
let navAppearance = UINavigationBarAppearance()
navAppearance.configureWithDefaultBackground()
Expand All @@ -47,6 +65,22 @@ extension AppDelegate {
UITabBar.appearance().scrollEdgeAppearance = barAppearance.copy()
}

// MARK: - Show Something

func showLoadingView(view: LoadingView) {
guard let window else { return }

view.translatesAutoresizingMaskIntoConstraints = false
window.addSubview(view)

NSLayoutConstraint.activate([
view.topAnchor.constraint(equalTo: window.topAnchor),
view.leadingAnchor.constraint(equalTo: window.leadingAnchor),
view.trailingAnchor.constraint(equalTo: window.trailingAnchor),
view.bottomAnchor.constraint(equalTo: window.bottomAnchor),
])
}

// MARK: - Make Something

func makeRootVC() -> UIViewController {
Expand Down
12 changes: 12 additions & 0 deletions Sources/Extensions/NotificationName+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// NotificationName+Extensions.swift
//
// Created by Shinren Pan on 2024/7/8.
//

import UIKit

extension Notification.Name {
static let showLoading = Notification.Name("showLoading")
static let hideLoading = Notification.Name("hideLoading")
}
2 changes: 1 addition & 1 deletion Sources/Extensions/ParserConfiguration+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import UIKit
import WebParser

extension ParserConfiguration {
static func makeParseDetail(_ comic: Comic) -> Self {
static func detail(comic: Comic) -> Self {
let uri = "https://m.manhuagui.com/comic/" + comic.id
let urlComponents = URLComponents(string: uri)!

Expand Down
8 changes: 8 additions & 0 deletions Sources/Extensions/UICollectionView+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,12 @@ extension UICollectionView {
func reuseCell<T: UICollectionViewCell>(_ type: T.Type, for indexPath: IndexPath) -> T {
dequeueReusableCell(withReuseIdentifier: "\(T.self)", for: indexPath) as! T
}

func registerHeader<T: UICollectionReusableView>(_ type: T.Type) {
register(type, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "\(T.self)")
}

func reuseHeader<T: UICollectionReusableView>(_ type: T.Type, for indexPath: IndexPath) -> T {
dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "\(T.self)", for: indexPath) as! T
}
}
50 changes: 19 additions & 31 deletions Sources/Extensions/UIViewController+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,31 @@
import UIKit

extension UIViewController {
func showEmptyUI(isEmpty: Bool) {
switch isEmpty {
case true:
var config = UIContentUnavailableConfiguration.empty()
config.background.backgroundColor = .white
config.text = "空空如也"
config.textProperties.font = .preferredFont(forTextStyle: .title1)
config.textProperties.color = .lightGray
contentUnavailableConfiguration = config
case false:
contentUnavailableConfiguration = nil
}
}

func showLoadingUI() {
var config = UIContentUnavailableConfiguration.loading()
config.text = ""
static func makeEmpty(text: String = "空空如也") -> UIContentUnavailableConfiguration {
var result = UIContentUnavailableConfiguration.empty()
result.background.backgroundColor = .white
result.text = text
result.textProperties.font = .preferredFont(forTextStyle: .title1)
result.textProperties.color = .lightGray

contentUnavailableConfiguration = config
return result
}

func showErrorUI(reload: UIAction?) {
var config = UIContentUnavailableConfiguration.empty()
config.background.backgroundColor = .white
config.image = UIImage(systemName: "exclamationmark.circle.fill")
config.text = "Something went wrong."
config.textProperties.font = .preferredFont(forTextStyle: .title1)
config.textProperties.color = .lightGray
config.secondaryText = "Please try again later."
config.secondaryTextProperties.font = .preferredFont(forTextStyle: .headline)
config.secondaryTextProperties.color = .lightGray
static func makeError() -> UIContentUnavailableConfiguration {
var result = UIContentUnavailableConfiguration.empty()
result.background.backgroundColor = .white
result.image = UIImage(systemName: "exclamationmark.circle.fill")
result.text = "發生錯誤."
result.textProperties.font = .preferredFont(forTextStyle: .title1)
result.textProperties.color = .lightGray
result.secondaryText = "重新載入?"
result.secondaryTextProperties.font = .preferredFont(forTextStyle: .headline)
result.secondaryTextProperties.color = .lightGray

var button = UIButton.Configuration.borderless()
button.image = UIImage(systemName: "arrow.clockwise.circle.fill")
config.button = button
config.buttonProperties.primaryAction = reload
result.button = button

contentUnavailableConfiguration = config
return result
}
}
40 changes: 26 additions & 14 deletions Sources/MVVVR/Detail/DetailModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@
import UIKit
import WebParser

enum DetailModels {}
enum DetailModels {
typealias DataSource = UICollectionViewDiffableDataSource<Section, DisplayEpisode>
typealias Snapshot = NSDiffableDataSourceSnapshot<Section, DisplayEpisode>
typealias CellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, DisplayEpisode>
}

// MARK: - Action

extension DetailModels {
enum Action {
case loadData
case updateFavorite
case loadCache
case loadRemote
case tapFavorite
}
}

Expand All @@ -23,31 +28,38 @@ extension DetailModels {
extension DetailModels {
enum State {
case none
case dataLoaded
case cacheLoaded(episodes: [DisplayEpisode])
case remoteLoaded(episodes: [DisplayEpisode])
case favoriteUpdated
}
}

// MARK: - Other Model for DisplayModel

extension DetailModels {}
extension DetailModels {
enum Section {
case main
}

final class DisplayEpisode: NSObject {
let data: Comic.Episode
let selected: Bool

init(data: Comic.Episode, selected: Bool) {
self.data = data
self.selected = selected
}
}
}

// MARK: - Display Model for ViewModel

extension DetailModels {
final class DisplayModel {
let comic: Comic
let parserSetting: ParserConfiguration
// SwiftData 存的 Array 無排序, 所以再用一個排序
var episodes: [Comic.Episode]

init(comic: Comic) {
self.comic = comic
self.episodes = comic.episodes?.sorted(by: { $0.index < $1.index }) ?? []
self.parserSetting = .makeParseDetail(comic)
}

func reloadEpisodes() {
episodes = comic.episodes?.sorted(by: { $0.index < $1.index }) ?? []
}
}
}
Loading

0 comments on commit 9072a5e

Please sign in to comment.