Channels layout improvements, other UI fixes

This commit is contained in:
Arkadiusz Fal
2021-08-31 23:17:50 +02:00
parent 1651110a5d
commit b00b54ad2a
28 changed files with 633 additions and 192 deletions

View File

@@ -3,20 +3,41 @@ import Defaults
import Foundation
import SwiftyJSON
struct Channel: Codable, Identifiable, Defaults.Serializable {
struct Channel: Identifiable, Hashable {
var id: String
var name: String
var subscriptionsCount: String
var videos = [Video]()
private var subscriptionsCount: Int?
private var subscriptionsText: String?
init(json: JSON) {
id = json["authorId"].stringValue
name = json["author"].stringValue
subscriptionsCount = json["subCountText"].stringValue
subscriptionsCount = json["subCount"].int
subscriptionsText = json["subCountText"].string
if let channelVideos = json.dictionaryValue["latestVideos"] {
videos = channelVideos.arrayValue.map(Video.init)
}
}
init(id: String, name: String, subscriptionsCount: String) {
init(id: String, name: String, subscriptionsCount: Int? = nil, videos: [Video] = []) {
self.id = id
self.name = name
self.subscriptionsCount = subscriptionsCount
self.videos = videos
}
var subscriptionsString: String? {
if subscriptionsCount != nil {
return subscriptionsCount!.formattedAsAbbreviation()
}
return subscriptionsText
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}

View File

@@ -75,12 +75,8 @@ final class InvidiousAPI: Service {
content.json.arrayValue.map(Channel.init)
}
configureTransformer("/channels/*", requestMethods: [.get]) { (content: Entity<JSON>) -> [Video] in
if let channelVideos = content.json.dictionaryValue["latestVideos"] {
return channelVideos.arrayValue.map(Video.init)
}
return []
configureTransformer("/channels/*", requestMethods: [.get]) { (content: Entity<JSON>) -> Channel in
Channel(json: content.json)
}
configureTransformer("/videos/*", requestMethods: [.get]) { (content: Entity<JSON>) -> Video in
@@ -112,7 +108,7 @@ final class InvidiousAPI: Service {
resource("/auth/subscriptions").child(id)
}
func channelVideos(_ id: String) -> Resource {
func channel(_ id: String) -> Resource {
resource("/channels/\(id)")
}

View File

@@ -8,9 +8,6 @@ final class NavigationState: ObservableObject {
@Published var tabSelection: TabSelection = .subscriptions
@Published var showingChannel = false
@Published var channel: Channel?
@Published var showingVideoDetails = false
@Published var showingVideo = false
@Published var video: Video?
@@ -23,15 +20,40 @@ final class NavigationState: ObservableObject {
@Published var presentingUnsubscribeAlert = false
@Published var channelToUnsubscribe: Channel!
@Published var openChannels = Set<Channel>()
@Published var isChannelOpen = false
@Published var sidebarSectionChanged = false
func openChannel(_ channel: Channel) {
returnToDetails = false
self.channel = channel
showingChannel = true
openChannels.insert(channel)
isChannelOpen = true
tabSelection = .channel(channel.id)
}
func closeChannel() {
showingChannel = false
channel = nil
func closeChannel(_ channel: Channel) {
guard openChannels.remove(channel) != nil else {
return
}
isChannelOpen = !openChannels.isEmpty
if tabSelection == .channel(channel.id) {
tabSelection = .subscriptions
}
}
func closeAllChannels() {
isChannelOpen = false
openChannels.removeAll()
}
func showOpenChannel(_ id: Channel.ID) -> Bool {
if case .channel = tabSelection {
return false
} else {
return !openChannels.contains { $0.id == id }
}
}
func openVideoDetails(_ video: Video) {
@@ -59,8 +81,10 @@ final class NavigationState: ObservableObject {
get: {
self.tabSelection
},
set: {
self.tabSelection = $0 ?? .subscriptions
set: { newValue in
if newValue != nil {
self.tabSelection = newValue!
}
}
)
}

View File

@@ -17,29 +17,30 @@ final class Subscriptions: ObservableObject {
channels.sorted { $0.name.lowercased() < $1.name.lowercased() }
}
func subscribe(_ channelID: String) {
performChannelSubscriptionRequest(channelID, method: .post)
func subscribe(_ channelID: String, onSuccess: @escaping () -> Void = {}) {
performChannelSubscriptionRequest(channelID, method: .post, onSuccess: onSuccess)
}
func unsubscribe(_ channelID: String) {
performChannelSubscriptionRequest(channelID, method: .delete)
func unsubscribe(_ channelID: String, onSuccess: @escaping () -> Void = {}) {
performChannelSubscriptionRequest(channelID, method: .delete, onSuccess: onSuccess)
}
func subscribed(_ channelID: String) -> Bool {
func isSubscribing(_ channelID: String) -> Bool {
channels.contains { $0.id == channelID }
}
fileprivate func load() {
fileprivate func load(onSuccess: @escaping () -> Void = {}) {
resource.load().onSuccess { resource in
if let channels: [Channel] = resource.typedContent() {
self.channels = channels
onSuccess()
}
}
}
fileprivate func performChannelSubscriptionRequest(_ channelID: String, method: RequestMethod) {
fileprivate func performChannelSubscriptionRequest(_ channelID: String, method: RequestMethod, onSuccess: @escaping () -> Void = {}) {
InvidiousAPI.shared.channelSubscription(channelID).request(method).onCompletion { _ in
self.load()
self.load(onSuccess: onSuccess)
}
}
}

View File

@@ -127,40 +127,15 @@ struct Video: Identifiable, Equatable {
}
var viewsCount: String? {
views != 0 ? formattedCount(views) : nil
views != 0 ? views.formattedAsAbbreviation() : nil
}
var likesCount: String? {
formattedCount(likes)
likes?.formattedAsAbbreviation()
}
var dislikesCount: String? {
formattedCount(dislikes)
}
func formattedCount(_ count: Int!) -> String? {
guard count != nil else {
return nil
}
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 1
var number: NSNumber
var unit: String
if count < 1000 {
return "\(count!)"
} else if count < 1_000_000 {
number = NSNumber(value: Double(count) / 1000.0)
unit = "K"
} else {
number = NSNumber(value: Double(count) / 1_000_000.0)
unit = "M"
}
return "\(formatter.string(from: number)!)\(unit)"
dislikes?.formattedAsAbbreviation()
}
var selectableStreams: [Stream] {