mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 13:33:42 +00:00
More uniform comments UI
This commit is contained in:
parent
008cd1553d
commit
923f0c0356
@ -8,7 +8,8 @@ final class CommentsModel: ObservableObject {
|
|||||||
@Published var nextPage: String?
|
@Published var nextPage: String?
|
||||||
@Published var firstPage = true
|
@Published var firstPage = true
|
||||||
|
|
||||||
@Published var loaded = true
|
@Published var loading = false
|
||||||
|
@Published var loaded = false
|
||||||
@Published var disabled = false
|
@Published var disabled = false
|
||||||
|
|
||||||
@Published var replies = [Comment]()
|
@Published var replies = [Comment]()
|
||||||
@ -18,16 +19,16 @@ final class CommentsModel: ObservableObject {
|
|||||||
var accounts: AccountsModel!
|
var accounts: AccountsModel!
|
||||||
var player: PlayerModel!
|
var player: PlayerModel!
|
||||||
|
|
||||||
var instance: Instance? {
|
static var instance: Instance? {
|
||||||
InstancesModel.find(Defaults[.commentsInstanceID])
|
InstancesModel.find(Defaults[.commentsInstanceID])
|
||||||
}
|
}
|
||||||
|
|
||||||
var api: VideosAPI? {
|
var api: VideosAPI? {
|
||||||
instance.isNil ? nil : PipedAPI(account: instance!.anonymousAccount)
|
Self.instance.isNil ? nil : PipedAPI(account: Self.instance!.anonymousAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
static var enabled: Bool {
|
static var enabled: Bool {
|
||||||
!Defaults[.commentsInstanceID].isNil && !Defaults[.commentsInstanceID]!.isEmpty
|
!instance.isNil
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
@ -41,13 +42,15 @@ final class CommentsModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func load(page: String? = nil) {
|
func load(page: String? = nil) {
|
||||||
guard Self.enabled else {
|
guard Self.enabled, !loading else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
reset()
|
reset()
|
||||||
|
|
||||||
guard !instance.isNil,
|
loading = true
|
||||||
|
|
||||||
|
guard !Self.instance.isNil,
|
||||||
!(player?.currentVideo.isNil ?? true)
|
!(player?.currentVideo.isNil ?? true)
|
||||||
else {
|
else {
|
||||||
return
|
return
|
||||||
@ -65,6 +68,7 @@ final class CommentsModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onCompletion { [weak self] _ in
|
.onCompletion { [weak self] _ in
|
||||||
|
self?.loading = false
|
||||||
self?.loaded = true
|
self?.loaded = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,9 +95,10 @@ final class CommentsModel: ObservableObject {
|
|||||||
.onSuccess { [weak self] response in
|
.onSuccess { [weak self] response in
|
||||||
if let page: CommentsPage = response.typedContent() {
|
if let page: CommentsPage = response.typedContent() {
|
||||||
self?.replies = page.comments
|
self?.replies = page.comments
|
||||||
|
self?.repliesLoaded = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onCompletion { [weak self] _ in
|
.onFailure { [weak self] _ in
|
||||||
self?.repliesLoaded = true
|
self?.repliesLoaded = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,6 +109,7 @@ final class CommentsModel: ObservableObject {
|
|||||||
firstPage = true
|
firstPage = true
|
||||||
nextPage = nil
|
nextPage = nil
|
||||||
loaded = false
|
loaded = false
|
||||||
|
loading = false
|
||||||
replies = []
|
replies = []
|
||||||
repliesLoaded = false
|
repliesLoaded = false
|
||||||
}
|
}
|
||||||
|
@ -1,65 +1,81 @@
|
|||||||
import SDWebImageSwiftUI
|
import SDWebImageSwiftUI
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct CommentView: View {
|
struct CommentView: View {
|
||||||
let comment: Comment
|
let comment: Comment
|
||||||
@Binding var repliesID: Comment.ID?
|
@Binding var repliesID: Comment.ID?
|
||||||
|
|
||||||
|
@State private var subscribed = false
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@Environment(\.colorScheme) private var colorScheme
|
||||||
@Environment(\.navigationStyle) private var navigationStyle
|
@Environment(\.navigationStyle) private var navigationStyle
|
||||||
|
|
||||||
@EnvironmentObject<CommentsModel> private var comments
|
@EnvironmentObject<CommentsModel> private var comments
|
||||||
@EnvironmentObject<NavigationModel> private var navigation
|
@EnvironmentObject<NavigationModel> private var navigation
|
||||||
@EnvironmentObject<PlayerModel> private var player
|
@EnvironmentObject<PlayerModel> private var player
|
||||||
@EnvironmentObject<RecentsModel> private var recents
|
@EnvironmentObject<RecentsModel> private var recents
|
||||||
|
@EnvironmentObject<SubscriptionsModel> private var subscriptions
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
HStack(alignment: .center, spacing: 10) {
|
HStack(alignment: .center, spacing: 10) {
|
||||||
authorAvatar
|
HStack(spacing: 10) {
|
||||||
|
ZStack(alignment: .bottomTrailing) {
|
||||||
|
authorAvatar
|
||||||
|
|
||||||
#if os(iOS)
|
if subscribed {
|
||||||
Group {
|
Image(systemName: "star.circle.fill")
|
||||||
if horizontalSizeClass == .regular {
|
#if os(tvOS)
|
||||||
HStack(spacing: 20) {
|
.background(Color.background(scheme: colorScheme))
|
||||||
authorAndTime
|
#else
|
||||||
|
.background(Color.background)
|
||||||
Spacer()
|
#endif
|
||||||
|
.clipShape(Circle())
|
||||||
Group {
|
.foregroundColor(.secondary)
|
||||||
statusIcons
|
|
||||||
likes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
HStack(alignment: .center, spacing: 20) {
|
|
||||||
authorAndTime
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
VStack(alignment: .trailing, spacing: 8) {
|
|
||||||
likes
|
|
||||||
statusIcons
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.font(.system(size: 15))
|
.onAppear {
|
||||||
|
subscribed = subscriptions.isSubscribing(comment.channel.id)
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
authorAndTime
|
||||||
HStack(spacing: 20) {
|
}
|
||||||
authorAndTime
|
.contextMenu {
|
||||||
|
Button(action: openChannelAction) {
|
||||||
|
Label("\(comment.channel.name) Channel", systemImage: "rectangle.stack.fill.badge.person.crop")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
|
Group {
|
||||||
|
#if os(iOS)
|
||||||
|
if horizontalSizeClass == .regular {
|
||||||
|
Group {
|
||||||
|
statusIcons
|
||||||
|
likes
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
VStack(alignment: .trailing, spacing: 8) {
|
||||||
|
likes
|
||||||
|
statusIcons
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
statusIcons
|
statusIcons
|
||||||
likes
|
likes
|
||||||
}
|
#endif
|
||||||
#endif
|
}
|
||||||
}
|
}
|
||||||
|
#if os(tvOS)
|
||||||
|
.font(.system(size: 25).bold())
|
||||||
|
#else
|
||||||
|
.font(.system(size: 15))
|
||||||
|
#endif
|
||||||
|
|
||||||
Group {
|
Group {
|
||||||
commentText
|
commentText
|
||||||
@ -94,23 +110,25 @@ struct CommentView: View {
|
|||||||
.retryOnAppear(false)
|
.retryOnAppear(false)
|
||||||
.indicator(.activity)
|
.indicator(.activity)
|
||||||
.mask(RoundedRectangle(cornerRadius: 60))
|
.mask(RoundedRectangle(cornerRadius: 60))
|
||||||
.frame(width: 45, height: 45, alignment: .leading)
|
|
||||||
.contextMenu {
|
|
||||||
Button(action: openChannelAction) {
|
|
||||||
Label("\(comment.channel.name) Channel", systemImage: "rectangle.stack.fill.badge.person.crop")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
|
.frame(width: 80, height: 80, alignment: .leading)
|
||||||
.focusable()
|
.focusable()
|
||||||
|
#else
|
||||||
|
.frame(width: 45, height: 45, alignment: .leading)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var authorAndTime: some View {
|
private var authorAndTime: some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text(comment.author)
|
Text(comment.author)
|
||||||
.fontWeight(.bold)
|
#if os(tvOS)
|
||||||
|
.font(.system(size: 30).bold())
|
||||||
|
#else
|
||||||
|
.font(.system(size: 14).bold())
|
||||||
|
#endif
|
||||||
|
|
||||||
Text(comment.time)
|
Text(comment.time)
|
||||||
|
.font(.caption2)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
@ -125,6 +143,9 @@ struct CommentView: View {
|
|||||||
Image(systemName: "heart.fill")
|
Image(systemName: "heart.fill")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !os(tvOS)
|
||||||
|
.font(.system(size: 12))
|
||||||
|
#endif
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,6 +156,9 @@ struct CommentView: View {
|
|||||||
Image(systemName: "hand.thumbsup")
|
Image(systemName: "hand.thumbsup")
|
||||||
Text("\(comment.likeCount.formattedAsAbbreviation())")
|
Text("\(comment.likeCount.formattedAsAbbreviation())")
|
||||||
}
|
}
|
||||||
|
#if !os(tvOS)
|
||||||
|
.font(.system(size: 12))
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
@ -163,6 +187,7 @@ struct CommentView: View {
|
|||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
.padding(.leading, 5)
|
.padding(.leading, 5)
|
||||||
#else
|
#else
|
||||||
|
.font(.system(size: 13))
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -181,7 +206,7 @@ struct CommentView: View {
|
|||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
0.4
|
0.4
|
||||||
#else
|
#else
|
||||||
0.8
|
0.6
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,17 +251,13 @@ struct CommentView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func openChannelAction() {
|
private func openChannelAction() {
|
||||||
player.presentingPlayer = false
|
NavigationModel.openChannel(
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
comment.channel,
|
||||||
let recent = RecentItem(from: comment.channel)
|
player: player,
|
||||||
recents.add(recent)
|
recents: recents,
|
||||||
navigation.presentingChannel = true
|
navigation: navigation,
|
||||||
|
navigationStyle: navigationStyle
|
||||||
if navigationStyle == .sidebar {
|
)
|
||||||
navigation.sidebarSectionChanged.toggle()
|
|
||||||
navigation.tabSelection = .recentlyOpened(recent.tag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,5 +268,7 @@ struct CommentView_Previews: PreviewProvider {
|
|||||||
|
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
CommentView(comment: fixture, repliesID: .constant(fixture.id))
|
CommentView(comment: fixture, repliesID: .constant(fixture.id))
|
||||||
|
.environmentObject(SubscriptionsModel())
|
||||||
|
.padding(5)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,9 @@ struct CommentsView: View {
|
|||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
} else if !comments.loaded {
|
} else if !comments.loaded {
|
||||||
progressView
|
progressView
|
||||||
|
.onAppear {
|
||||||
|
comments.load()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ScrollView(.vertical, showsIndicators: false) {
|
ScrollView(.vertical, showsIndicators: false) {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
@ -48,6 +51,7 @@ struct CommentsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.font(.system(size: 13))
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
.padding(.vertical, 8)
|
.padding(.vertical, 8)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
@ -56,11 +60,6 @@ struct CommentsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
.onAppear {
|
|
||||||
if !comments.loaded {
|
|
||||||
comments.load()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var progressView: some View {
|
private var progressView: some View {
|
||||||
|
@ -117,9 +117,18 @@ struct NowPlayingView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if sections.contains(.comments) {
|
if sections.contains(.comments) {
|
||||||
Section {
|
if !comments.loaded {
|
||||||
ForEach(comments.all) { comment in
|
VStack(alignment: .center) {
|
||||||
CommentView(comment: comment, repliesID: $repliesID)
|
progressView
|
||||||
|
.onAppear {
|
||||||
|
comments.load()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Section {
|
||||||
|
ForEach(comments.all) { comment in
|
||||||
|
CommentView(comment: comment, repliesID: $repliesID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,6 +146,19 @@ struct NowPlayingView: View {
|
|||||||
.font((inInfoViewController ? Font.system(size: 40) : .title3).bold())
|
.font((inInfoViewController ? Font.system(size: 40) : .title3).bold())
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var progressView: some View {
|
||||||
|
VStack {
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
ProgressView()
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NowPlayingView_Previews: PreviewProvider {
|
struct NowPlayingView_Previews: PreviewProvider {
|
||||||
|
Loading…
Reference in New Issue
Block a user