mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 13:33:42 +00:00
Add pull to refresh for Subscriptions, Popular and Trending (fixes #31)
This commit is contained in:
parent
1db4a3197d
commit
363424fa74
@ -27,11 +27,17 @@ struct FavoritesView: View {
|
||||
FavoriteItemView(item: item, dragging: $dragging)
|
||||
}
|
||||
#else
|
||||
#if os(iOS)
|
||||
let first = favorites.first
|
||||
#endif
|
||||
ForEach(favorites) { item in
|
||||
FavoriteItemView(item: item, dragging: $dragging)
|
||||
#if os(macOS)
|
||||
.workaroundForVerticalScrollingBug()
|
||||
#endif
|
||||
#if os(iOS)
|
||||
.padding(.top, item == first && RefreshControl.navigationBarTitleDisplayMode == .inline ? 10 : 0)
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -54,6 +60,9 @@ struct FavoritesView: View {
|
||||
.background(Color.secondaryBackground)
|
||||
.frame(minWidth: 360)
|
||||
#endif
|
||||
#if os(iOS)
|
||||
.navigationBarTitleDisplayMode(RefreshControl.navigationBarTitleDisplayMode)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,6 +127,9 @@ struct PlaylistsView: View {
|
||||
.onChange(of: accounts.current) { _ in
|
||||
model.load(force: true)
|
||||
}
|
||||
#if os(iOS)
|
||||
.navigationBarTitleDisplayMode(RefreshControl.navigationBarTitleDisplayMode)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if os(tvOS)
|
||||
|
@ -48,6 +48,69 @@ struct TrendingView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
#if os(macOS)
|
||||
ToolbarItemGroup {
|
||||
if let favoriteItem = favoriteItem {
|
||||
FavoriteButton(item: favoriteItem)
|
||||
.id(favoriteItem.id)
|
||||
}
|
||||
|
||||
if accounts.app.supportsTrendingCategories {
|
||||
categoryButton
|
||||
}
|
||||
countryButton
|
||||
}
|
||||
#elseif os(iOS)
|
||||
ToolbarItemGroup(placement: .bottomBar) {
|
||||
Group {
|
||||
if accounts.app.supportsTrendingCategories {
|
||||
HStack {
|
||||
Text("Category")
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
categoryButton
|
||||
// only way to disable Menu animation is to
|
||||
// force redraw of the view when it changes
|
||||
.id(UUID())
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
|
||||
if let favoriteItem = favoriteItem {
|
||||
FavoriteButton(item: favoriteItem)
|
||||
.id(favoriteItem.id)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text("Country")
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
countryButton
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
.onChange(of: resource) { _ in
|
||||
resource.load()
|
||||
updateFavoriteItem()
|
||||
}
|
||||
.onAppear {
|
||||
if videos.isEmpty {
|
||||
resource.addObserver(store)
|
||||
resource.loadIfNeeded()
|
||||
} else {
|
||||
store.replace(videos)
|
||||
}
|
||||
|
||||
updateFavoriteItem()
|
||||
}
|
||||
|
||||
#if os(tvOS)
|
||||
.fullScreenCover(isPresented: $presentingCountrySelection) {
|
||||
TrendingCountry(selectedCountry: $country)
|
||||
@ -61,67 +124,14 @@ struct TrendingView: View {
|
||||
}
|
||||
.navigationTitle("Trending")
|
||||
#endif
|
||||
.toolbar {
|
||||
#if os(macOS)
|
||||
ToolbarItemGroup {
|
||||
if let favoriteItem = favoriteItem {
|
||||
FavoriteButton(item: favoriteItem)
|
||||
.id(favoriteItem.id)
|
||||
}
|
||||
|
||||
if accounts.app.supportsTrendingCategories {
|
||||
categoryButton
|
||||
}
|
||||
countryButton
|
||||
}
|
||||
#elseif os(iOS)
|
||||
ToolbarItemGroup(placement: .bottomBar) {
|
||||
Group {
|
||||
HStack {
|
||||
if accounts.app.supportsTrendingCategories {
|
||||
Text("Category")
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
categoryButton
|
||||
// only way to disable Menu animation is to
|
||||
// force redraw of the view when it changes
|
||||
.id(UUID())
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
if let favoriteItem = favoriteItem {
|
||||
FavoriteButton(item: favoriteItem)
|
||||
.id(favoriteItem.id)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text("Country")
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
countryButton
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
.onChange(of: resource) { _ in
|
||||
resource.load()
|
||||
updateFavoriteItem()
|
||||
}
|
||||
.onAppear {
|
||||
if videos.isEmpty {
|
||||
resource.addObserver(store)
|
||||
resource.loadIfNeeded()
|
||||
} else {
|
||||
store.replace(videos)
|
||||
}
|
||||
|
||||
updateFavoriteItem()
|
||||
}
|
||||
#if os(iOS)
|
||||
.refreshControl { refreshControl in
|
||||
resource.load().onCompletion { _ in
|
||||
refreshControl.endRefreshing()
|
||||
}
|
||||
}
|
||||
.navigationBarTitleDisplayMode(RefreshControl.navigationBarTitleDisplayMode)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if os(tvOS)
|
||||
|
@ -30,5 +30,13 @@ struct PopularView: View {
|
||||
FavoriteButton(item: FavoriteItem(section: .popular))
|
||||
}
|
||||
}
|
||||
#if os(iOS)
|
||||
.refreshControl { refreshControl in
|
||||
resource?.load().onCompletion { _ in
|
||||
refreshControl.endRefreshing()
|
||||
}
|
||||
}
|
||||
.navigationBarTitleDisplayMode(RefreshControl.navigationBarTitleDisplayMode)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,13 @@ struct SubscriptionsView: View {
|
||||
.onChange(of: accounts.current) { _ in
|
||||
loadResources(force: true)
|
||||
}
|
||||
#if os(iOS)
|
||||
.refreshControl { refreshControl in
|
||||
loadResources(force: true) {
|
||||
refreshControl.endRefreshing()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
.toolbar {
|
||||
@ -31,26 +38,35 @@ struct SubscriptionsView: View {
|
||||
FavoriteButton(item: FavoriteItem(section: .subscriptions))
|
||||
}
|
||||
}
|
||||
#if os(iOS)
|
||||
.navigationBarTitleDisplayMode(RefreshControl.navigationBarTitleDisplayMode)
|
||||
#endif
|
||||
}
|
||||
|
||||
fileprivate func loadResources(force: Bool = false) {
|
||||
private func loadResources(force: Bool = false, onCompletion: @escaping () -> Void = {}) {
|
||||
feed?.addObserver(store)
|
||||
|
||||
if accounts.app == .invidious {
|
||||
// Invidious for some reason won't refresh feed until homepage is loaded
|
||||
if let request = force ? accounts.api.home?.load() : accounts.api.home?.loadIfNeeded() {
|
||||
request.onSuccess { _ in
|
||||
loadFeed(force: force)
|
||||
loadFeed(force: force, onCompletion: onCompletion)
|
||||
}
|
||||
} else {
|
||||
loadFeed(force: force)
|
||||
loadFeed(force: force, onCompletion: onCompletion)
|
||||
}
|
||||
} else {
|
||||
loadFeed(force: force)
|
||||
loadFeed(force: force, onCompletion: onCompletion)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func loadFeed(force: Bool = false) {
|
||||
_ = force ? feed?.load() : feed?.loadIfNeeded()
|
||||
private func loadFeed(force: Bool = false, onCompletion: @escaping () -> Void = {}) {
|
||||
if let request = force ? feed?.load() : feed?.loadIfNeeded() {
|
||||
request.onCompletion { _ in
|
||||
onCompletion()
|
||||
}
|
||||
} else {
|
||||
onCompletion()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
15
Vendor/RefreshControl/Extensions/UIResponder+Extensions.swift
vendored
Normal file
15
Vendor/RefreshControl/Extensions/UIResponder+Extensions.swift
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// UIResponder+Extensions.swift
|
||||
// SwiftUI_Pull_to_Refresh
|
||||
//
|
||||
// Created by Geri Borbás on 21/09/2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
extension UIResponder {
|
||||
var parentViewController: UIViewController? {
|
||||
next as? UIViewController ?? next?.parentViewController
|
||||
}
|
||||
}
|
70
Vendor/RefreshControl/Extensions/UIView+Extensions.swift
vendored
Normal file
70
Vendor/RefreshControl/Extensions/UIView+Extensions.swift
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
//
|
||||
// UIView+Extensions.swift
|
||||
// SwiftUI_Pull_to_Refresh
|
||||
//
|
||||
// Created by Geri Borbás on 19/09/2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
/// Returs frame in screen coordinates.
|
||||
var globalFrame: CGRect {
|
||||
if let window = window {
|
||||
return convert(bounds, to: window.screen.coordinateSpace)
|
||||
} else {
|
||||
return .zero
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns with all the instances of the given view type in the view hierarchy.
|
||||
func viewsInHierarchy<ViewType: UIView>() -> [ViewType]? {
|
||||
var views: [ViewType] = []
|
||||
viewsInHierarchy(views: &views)
|
||||
return views.isEmpty ? nil : views
|
||||
}
|
||||
|
||||
private func viewsInHierarchy<ViewType: UIView>(views: inout [ViewType]) {
|
||||
subviews.forEach { eachSubView in
|
||||
if let matchingView = eachSubView as? ViewType {
|
||||
views.append(matchingView)
|
||||
}
|
||||
eachSubView.viewsInHierarchy(views: &views)
|
||||
}
|
||||
}
|
||||
|
||||
/// Search ancestral view hierarcy for the given view type.
|
||||
func searchViewAnchestorsFor<ViewType: UIView>(
|
||||
_ onViewFound: (ViewType) -> Void
|
||||
) {
|
||||
if let matchingView = superview as? ViewType {
|
||||
onViewFound(matchingView)
|
||||
} else {
|
||||
superview?.searchViewAnchestorsFor(onViewFound)
|
||||
}
|
||||
}
|
||||
|
||||
/// Search ancestral view hierarcy for the given view type.
|
||||
func searchViewAnchestorsFor<ViewType: UIView>() -> ViewType? {
|
||||
if let matchingView = superview as? ViewType {
|
||||
return matchingView
|
||||
} else {
|
||||
return superview?.searchViewAnchestorsFor()
|
||||
}
|
||||
}
|
||||
|
||||
func printViewHierarchyInformation(_ level: Int = 0) {
|
||||
printViewInformation(level)
|
||||
subviews.forEach { $0.printViewHierarchyInformation(level + 1) }
|
||||
}
|
||||
|
||||
func printViewInformation(_ level: Int) {
|
||||
let leadingWhitespace = String(repeating: " ", count: level)
|
||||
let className = "\(Self.self)"
|
||||
let superclassName = "\(superclass!)"
|
||||
let frame = "\(self.frame)"
|
||||
let id = (accessibilityIdentifier == nil) ? "" : " `\(accessibilityIdentifier!)`"
|
||||
print("\(leadingWhitespace)\(className): \(superclassName)\(id) \(frame)")
|
||||
}
|
||||
}
|
47
Vendor/RefreshControl/README
vendored
Normal file
47
Vendor/RefreshControl/README
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
https://github.com/Geri-Borbas/iOS.Blog.SwiftUI_Pull_to_Refresh
|
||||
|
||||
# SwiftUI Pull to Refresh
|
||||
⇣ SwiftUI Pull to Refresh (for iOS 13 and iOS 14) condensed into a single modifier.
|
||||
|
||||
|
||||
Complementary repository for article [**SwiftUI Pull to Refresh**] (in progress). See [`ContentView.swift`] for usage, and [`RefreshControlModifier.swift`] for the source. Designed to work with **multiple scroll views** on the same screen.
|
||||
|
||||
```Swift
|
||||
struct ContentView: View {
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
HStack {
|
||||
List {
|
||||
ForEach(1...100, id: \.self) { eachRowIndex in
|
||||
Text("Left \(eachRowIndex)")
|
||||
}
|
||||
}
|
||||
.refreshControl { refreshControl in
|
||||
Network.refresh {
|
||||
refreshControl.endRefreshing()
|
||||
}
|
||||
}
|
||||
List {
|
||||
ForEach(1...100, id: \.self) { eachRowIndex in
|
||||
Text("Right \(eachRowIndex)")
|
||||
}
|
||||
}
|
||||
.refreshControl { refreshControl in
|
||||
Network.refresh {
|
||||
refreshControl.endRefreshing()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## License
|
||||
|
||||
> Licensed under the [**MIT License**](https://en.wikipedia.org/wiki/MIT_License).
|
||||
|
||||
[`ContentView.swift`]: SwiftUI_Pull_to_Refresh/Views/ContentView.swift
|
||||
[`RefreshControl.swift`]: SwiftUI_Pull_to_Refresh/Views/RefreshControl.swift
|
56
Vendor/RefreshControl/RefreshControl.swift
vendored
Normal file
56
Vendor/RefreshControl/RefreshControl.swift
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
//
|
||||
// RefreshControl.swift
|
||||
// SwiftUI_Pull_to_Refresh
|
||||
//
|
||||
// Created by Geri Borbás on 18/09/2021.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
final class RefreshControl: ObservableObject {
|
||||
static var navigationBarTitleDisplayMode: NavigationBarItem.TitleDisplayMode {
|
||||
if #available(iOS 15.0, *) {
|
||||
return .automatic
|
||||
}
|
||||
|
||||
return .inline
|
||||
}
|
||||
|
||||
let onValueChanged: (_ refreshControl: UIRefreshControl) -> Void
|
||||
|
||||
internal init(onValueChanged: @escaping ((UIRefreshControl) -> Void)) {
|
||||
self.onValueChanged = onValueChanged
|
||||
}
|
||||
|
||||
/// Adds a `UIRefreshControl` to the `UIScrollView` provided.
|
||||
func add(to scrollView: UIScrollView) {
|
||||
scrollView.refreshControl = UIRefreshControl().withTarget(
|
||||
self,
|
||||
action: #selector(onValueChangedAction),
|
||||
for: .valueChanged
|
||||
)
|
||||
.testable(as: "RefreshControl")
|
||||
}
|
||||
|
||||
@objc private func onValueChangedAction(sender: UIRefreshControl) {
|
||||
onValueChanged(sender)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIRefreshControl {
|
||||
/// Convinience method to assign target action inline.
|
||||
func withTarget(_ target: Any?, action: Selector, for controlEvents: UIControl.Event) -> UIRefreshControl {
|
||||
addTarget(target, action: action, for: controlEvents)
|
||||
return self
|
||||
}
|
||||
|
||||
/// Convinience method to match refresh control for UI testing.
|
||||
func testable(as id: String) -> UIRefreshControl {
|
||||
isAccessibilityElement = true
|
||||
accessibilityIdentifier = id
|
||||
return self
|
||||
}
|
||||
}
|
42
Vendor/RefreshControl/RefreshControlModifier.swift
vendored
Normal file
42
Vendor/RefreshControl/RefreshControlModifier.swift
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
//
|
||||
// RefreshControlModifier.swift
|
||||
// SwiftUI_Pull_to_Refresh
|
||||
//
|
||||
// Created by Geri Borbás on 18/09/2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct RefreshControlModifier: ViewModifier {
|
||||
@State private var geometryReaderFrame: CGRect = .zero
|
||||
let refreshControl: RefreshControl
|
||||
|
||||
internal init(onValueChanged: @escaping (UIRefreshControl) -> Void) {
|
||||
refreshControl = RefreshControl(onValueChanged: onValueChanged)
|
||||
}
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.background(
|
||||
GeometryReader { geometry in
|
||||
ScrollViewMatcher(
|
||||
onResolve: { scrollView in
|
||||
refreshControl.add(to: scrollView)
|
||||
},
|
||||
geometryReaderFrame: $geometryReaderFrame
|
||||
)
|
||||
.preference(key: FramePreferenceKey.self, value: geometry.frame(in: .global))
|
||||
.onPreferenceChange(FramePreferenceKey.self) { frame in
|
||||
self.geometryReaderFrame = frame
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func refreshControl(onValueChanged: @escaping (_ refreshControl: UIRefreshControl) -> Void) -> some View {
|
||||
modifier(RefreshControlModifier(onValueChanged: onValueChanged))
|
||||
}
|
||||
}
|
18
Vendor/RefreshControl/ScrollViewMatcher/FramePreferenceKey.swift
vendored
Normal file
18
Vendor/RefreshControl/ScrollViewMatcher/FramePreferenceKey.swift
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
//
|
||||
// FramePreferenceKey.swift
|
||||
// SwiftUI_Pull_to_Refresh
|
||||
//
|
||||
// Created by Geri Borbás on 21/09/2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct FramePreferenceKey: PreferenceKey {
|
||||
typealias Value = CGRect
|
||||
static var defaultValue = CGRect.zero
|
||||
|
||||
static func reduce(value: inout CGRect, nextValue: () -> CGRect) {
|
||||
value = nextValue()
|
||||
}
|
||||
}
|
106
Vendor/RefreshControl/ScrollViewMatcher/ScrollViewMatcher.swift
vendored
Normal file
106
Vendor/RefreshControl/ScrollViewMatcher/ScrollViewMatcher.swift
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
//
|
||||
// ScrollViewMatcher.swift
|
||||
// SwiftUI_Pull_to_Refresh
|
||||
//
|
||||
// Created by Geri Borbás on 17/09/2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
final class ScrollViewMatcher: UIViewControllerRepresentable {
|
||||
let onMatch: (UIScrollView) -> Void
|
||||
@Binding var geometryReaderFrame: CGRect
|
||||
|
||||
init(onResolve: @escaping (UIScrollView) -> Void, geometryReaderFrame: Binding<CGRect>) {
|
||||
onMatch = onResolve
|
||||
_geometryReaderFrame = geometryReaderFrame
|
||||
}
|
||||
|
||||
func makeUIViewController(context _: Context) -> ScrollViewMatcherViewController {
|
||||
ScrollViewMatcherViewController(onResolve: onMatch, geometryReaderFrame: geometryReaderFrame)
|
||||
}
|
||||
|
||||
func updateUIViewController(_ viewController: ScrollViewMatcherViewController, context _: Context) {
|
||||
viewController.geometryReaderFrame = geometryReaderFrame
|
||||
}
|
||||
}
|
||||
|
||||
final class ScrollViewMatcherViewController: UIViewController {
|
||||
let onMatch: (UIScrollView) -> Void
|
||||
private var scrollView: UIScrollView? {
|
||||
didSet {
|
||||
if oldValue != scrollView,
|
||||
let scrollView = scrollView
|
||||
{
|
||||
onMatch(scrollView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var geometryReaderFrame: CGRect {
|
||||
didSet {
|
||||
match()
|
||||
}
|
||||
}
|
||||
|
||||
init(onResolve: @escaping (UIScrollView) -> Void, geometryReaderFrame: CGRect, debug _: Bool = false) {
|
||||
onMatch = onResolve
|
||||
self.geometryReaderFrame = geometryReaderFrame
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("Use init(onMatch:) to instantiate ScrollViewMatcherViewController.")
|
||||
}
|
||||
|
||||
func match() {
|
||||
// matchUsingHierarchy()
|
||||
matchUsingGeometry()
|
||||
}
|
||||
|
||||
func matchUsingHierarchy() {
|
||||
if parent != nil {
|
||||
// Lookup view ancestry for any `UIScrollView`.
|
||||
view.searchViewAnchestorsFor { (scrollView: UIScrollView) in
|
||||
self.scrollView = scrollView
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func matchUsingGeometry() {
|
||||
if let parent = parent {
|
||||
if let scrollViewsInHierarchy: [UIScrollView] = parent.view.viewsInHierarchy() {
|
||||
// Return first match if only a single scroll view is found in the hierarchy.
|
||||
if scrollViewsInHierarchy.count == 1,
|
||||
let firstScrollViewInHierarchy = scrollViewsInHierarchy.first
|
||||
{
|
||||
scrollView = firstScrollViewInHierarchy
|
||||
|
||||
// Filter by frame origins if multiple matches found.
|
||||
} else {
|
||||
if let firstMatchingFrameOrigin = scrollViewsInHierarchy.filter({
|
||||
$0.globalFrame.origin.close(to: geometryReaderFrame.origin)
|
||||
}).first {
|
||||
scrollView = firstMatchingFrameOrigin
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func didMove(toParent parent: UIViewController?) {
|
||||
super.didMove(toParent: parent)
|
||||
match()
|
||||
}
|
||||
}
|
||||
|
||||
extension CGPoint {
|
||||
/// Returns `true` if this point is close the other point (considering a ~1 pt tolerance).
|
||||
func close(to point: CGPoint) -> Bool {
|
||||
let inset = Double(1)
|
||||
let rect = CGRect(x: x - inset, y: y - inset, width: inset * 2, height: inset * 2)
|
||||
return rect.contains(point)
|
||||
}
|
||||
}
|
@ -515,6 +515,14 @@
|
||||
37DD9DA32785BBC900539416 /* NoCommentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD9DA22785BBC900539416 /* NoCommentsView.swift */; };
|
||||
37DD9DA42785BBC900539416 /* NoCommentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD9DA22785BBC900539416 /* NoCommentsView.swift */; };
|
||||
37DD9DA52785BBC900539416 /* NoCommentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD9DA22785BBC900539416 /* NoCommentsView.swift */; };
|
||||
37DD9DB12785D58D00539416 /* RefreshControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD9DAF2785D58D00539416 /* RefreshControl.swift */; };
|
||||
37DD9DB42785D58D00539416 /* RefreshControlModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD9DB02785D58D00539416 /* RefreshControlModifier.swift */; };
|
||||
37DD9DBA2785D60300539416 /* FramePreferenceKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD9DB82785D60200539416 /* FramePreferenceKey.swift */; };
|
||||
37DD9DBB2785D60300539416 /* FramePreferenceKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD9DB82785D60200539416 /* FramePreferenceKey.swift */; };
|
||||
37DD9DBC2785D60300539416 /* FramePreferenceKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD9DB82785D60200539416 /* FramePreferenceKey.swift */; };
|
||||
37DD9DBD2785D60300539416 /* ScrollViewMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD9DB92785D60200539416 /* ScrollViewMatcher.swift */; };
|
||||
37DD9DC62785D63A00539416 /* UIResponder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD9DC22785D63A00539416 /* UIResponder+Extensions.swift */; };
|
||||
37DD9DCB2785E28C00539416 /* UIView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD9DC12785D63A00539416 /* UIView+Extensions.swift */; };
|
||||
37E04C0F275940FB00172673 /* VerticalScrollingFix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E04C0E275940FB00172673 /* VerticalScrollingFix.swift */; };
|
||||
37E084AC2753D95F00039B7D /* AccountsNavigationLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */; };
|
||||
37E084AD2753D95F00039B7D /* AccountsNavigationLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */; };
|
||||
@ -778,6 +786,12 @@
|
||||
37D9169A27388A81002B1BAA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||
37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerStreams.swift; sourceTree = "<group>"; };
|
||||
37DD9DA22785BBC900539416 /* NoCommentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoCommentsView.swift; sourceTree = "<group>"; };
|
||||
37DD9DAF2785D58D00539416 /* RefreshControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefreshControl.swift; sourceTree = "<group>"; };
|
||||
37DD9DB02785D58D00539416 /* RefreshControlModifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefreshControlModifier.swift; sourceTree = "<group>"; };
|
||||
37DD9DB82785D60200539416 /* FramePreferenceKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FramePreferenceKey.swift; sourceTree = "<group>"; };
|
||||
37DD9DB92785D60200539416 /* ScrollViewMatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollViewMatcher.swift; sourceTree = "<group>"; };
|
||||
37DD9DC12785D63A00539416 /* UIView+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Extensions.swift"; sourceTree = "<group>"; };
|
||||
37DD9DC22785D63A00539416 /* UIResponder+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIResponder+Extensions.swift"; sourceTree = "<group>"; };
|
||||
37E04C0E275940FB00172673 /* VerticalScrollingFix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerticalScrollingFix.swift; sourceTree = "<group>"; };
|
||||
37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsNavigationLink.swift; sourceTree = "<group>"; };
|
||||
37E2EEAA270656EC00170416 /* PlayerControlsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerControlsView.swift; sourceTree = "<group>"; };
|
||||
@ -1204,6 +1218,7 @@
|
||||
3722AEBA274DA312005EA4D6 /* Backports */,
|
||||
37D4B1B72672CFE300C925CA /* Model */,
|
||||
37C7A9022679058300E721B4 /* Extensions */,
|
||||
37DD9DCC2785EE6F00539416 /* Vendor */,
|
||||
3748186426A762300084E870 /* Fixtures */,
|
||||
37A3B15827255E7F000FB5EE /* Open in Yattee */,
|
||||
377FC7D1267A080300A6BBAF /* Frameworks */,
|
||||
@ -1346,6 +1361,43 @@
|
||||
path = Gestures;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37DD9DAE2785D58D00539416 /* RefreshControl */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37DD9DC02785D63A00539416 /* Extensions */,
|
||||
37DD9DB72785D60200539416 /* ScrollViewMatcher */,
|
||||
37DD9DAF2785D58D00539416 /* RefreshControl.swift */,
|
||||
37DD9DB02785D58D00539416 /* RefreshControlModifier.swift */,
|
||||
);
|
||||
path = RefreshControl;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37DD9DB72785D60200539416 /* ScrollViewMatcher */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37DD9DB82785D60200539416 /* FramePreferenceKey.swift */,
|
||||
37DD9DB92785D60200539416 /* ScrollViewMatcher.swift */,
|
||||
);
|
||||
path = ScrollViewMatcher;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37DD9DC02785D63A00539416 /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37DD9DC22785D63A00539416 /* UIResponder+Extensions.swift */,
|
||||
37DD9DC12785D63A00539416 /* UIView+Extensions.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37DD9DCC2785EE6F00539416 /* Vendor */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37DD9DAE2785D58D00539416 /* RefreshControl */,
|
||||
);
|
||||
path = Vendor;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37FB283F2721B20800A57617 /* Search */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1846,6 +1898,7 @@
|
||||
37130A5B277657090033018A /* Yattee.xcdatamodeld in Sources */,
|
||||
37152EEA26EFEB95004FB96D /* LazyView.swift in Sources */,
|
||||
3761ABFD26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */,
|
||||
37DD9DCB2785E28C00539416 /* UIView+Extensions.swift in Sources */,
|
||||
3782B94F27553A6700990149 /* SearchSuggestions.swift in Sources */,
|
||||
378E50FF26FE8EEE00F49626 /* AccountsMenuView.swift in Sources */,
|
||||
37169AA62729E2CC0011DE61 /* AccountsBridge.swift in Sources */,
|
||||
@ -1864,6 +1917,7 @@
|
||||
37B81AF926D2C9A700675966 /* VideoPlayerSizeModifier.swift in Sources */,
|
||||
37C0698227260B2100F7F6CB /* ThumbnailsModel.swift in Sources */,
|
||||
37BC50A82778A84700510953 /* HistorySettings.swift in Sources */,
|
||||
37DD9DB12785D58D00539416 /* RefreshControl.swift in Sources */,
|
||||
37B4E805277D0AB4004BF56A /* Orientation.swift in Sources */,
|
||||
37DD87C7271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */,
|
||||
371B7E662759786B00D21217 /* Comment+Fixtures.swift in Sources */,
|
||||
@ -1881,6 +1935,7 @@
|
||||
3751B4B227836902000B7DF4 /* SearchPage.swift in Sources */,
|
||||
37CC3F4C270CFE1700608308 /* PlayerQueueView.swift in Sources */,
|
||||
37FFC440272734C3009FFD26 /* Throttle.swift in Sources */,
|
||||
37DD9DB42785D58D00539416 /* RefreshControlModifier.swift in Sources */,
|
||||
3705B182267B4E4900704544 /* TrendingCategory.swift in Sources */,
|
||||
378AE940274EDFB5006A4EE1 /* Tint+Backport.swift in Sources */,
|
||||
376BE50927347B5F009AD608 /* SettingsHeader.swift in Sources */,
|
||||
@ -1909,6 +1964,7 @@
|
||||
37BA794326DBA973002A0235 /* PlaylistsModel.swift in Sources */,
|
||||
37BC50AC2778BCBA00510953 /* HistoryModel.swift in Sources */,
|
||||
37AAF29026740715007FC770 /* Channel.swift in Sources */,
|
||||
37DD9DBA2785D60300539416 /* FramePreferenceKey.swift in Sources */,
|
||||
3748186A26A764FB0084E870 /* Thumbnail+Fixtures.swift in Sources */,
|
||||
37B81AFF26D2CA3700675966 /* VideoDetails.swift in Sources */,
|
||||
377FC7E5267A084E00A6BBAF /* SearchView.swift in Sources */,
|
||||
@ -1939,9 +1995,11 @@
|
||||
37A9965A26D6F8CA006E3224 /* HorizontalCells.swift in Sources */,
|
||||
37D526DE2720AC4400ED2F5E /* VideosAPI.swift in Sources */,
|
||||
37484C2526FC83E000287258 /* InstanceForm.swift in Sources */,
|
||||
37DD9DBD2785D60300539416 /* ScrollViewMatcher.swift in Sources */,
|
||||
37B767DB2677C3CA0098BAA8 /* PlayerModel.swift in Sources */,
|
||||
3788AC2726F6840700F6BAA9 /* FavoriteItemView.swift in Sources */,
|
||||
375DFB5826F9DA010013F468 /* InstancesModel.swift in Sources */,
|
||||
37DD9DC62785D63A00539416 /* UIResponder+Extensions.swift in Sources */,
|
||||
37C3A24927235FAA0087A57A /* ChannelPlaylistCell.swift in Sources */,
|
||||
373CFACB26966264003CB2C6 /* SearchQuery.swift in Sources */,
|
||||
37141673267A8E10006CA35D /* Country.swift in Sources */,
|
||||
@ -2012,6 +2070,7 @@
|
||||
374C053C2724614F009BDDBE /* PlayerTVMenu.swift in Sources */,
|
||||
377FC7DD267A081A00A6BBAF /* PopularView.swift in Sources */,
|
||||
3784CDE327772EE40055BBF2 /* Watch.swift in Sources */,
|
||||
37DD9DBB2785D60300539416 /* FramePreferenceKey.swift in Sources */,
|
||||
375DFB5926F9DA010013F468 /* InstancesModel.swift in Sources */,
|
||||
3705B183267B4E4900704544 /* TrendingCategory.swift in Sources */,
|
||||
37FB285F272225E800A57617 /* ContentItemView.swift in Sources */,
|
||||
@ -2216,6 +2275,7 @@
|
||||
files = (
|
||||
37EAD871267B9ED100D9E01B /* Segment.swift in Sources */,
|
||||
373C8FE6275B955100CB5936 /* CommentsPage.swift in Sources */,
|
||||
37DD9DBC2785D60300539416 /* FramePreferenceKey.swift in Sources */,
|
||||
37CC3F52270D010D00608308 /* VideoBanner.swift in Sources */,
|
||||
37F49BA526CAA59B00304AC0 /* Playlist+Fixtures.swift in Sources */,
|
||||
376CD21826FBE18D001E1AC1 /* Instance+Fixtures.swift in Sources */,
|
||||
|
Loading…
Reference in New Issue
Block a user