Refactoring the UX for digest view

This commit is contained in:
Jackson Harper
2024-04-16 13:34:12 +08:00
committed by Hongbo Wu
parent d2ea93a7dd
commit 47150c2991
13 changed files with 608 additions and 366 deletions

View File

@ -0,0 +1,303 @@
import SwiftUI
import Models
import Services
public class FullScreenDigestViewModel: ObservableObject {
@Published var isLoading = false
@Published var digest: DigestResult?
func load(dataService: DataService) async {
isLoading = true
if digest == nil {
do {
digest = try await dataService.getLatestDigest(timeoutInterval: 10)
} catch {
print("ERROR WITH DIGEST: ", error)
}
}
isLoading = false
}
}
struct DigestAudioItem: AudioItemProperties {
let audioItemType = Models.AudioItemType.digest
var itemID = ""
var title = "TITLE"
var byline: String? = "byline"
var imageURL: URL? = nil
var language: String?
var startIndex: Int = 0
var startOffset: Double = 0.0
}
@available(iOS 17.0, *)
@MainActor
struct FullScreenDigestView: View {
let viewModel: DigestViewModel = DigestViewModel()
let dataService: DataService
let audioController: AudioController
@Environment(\.dismiss) private var dismiss
let textBody = "In a significant political turn, the SOTU response faces unexpected collapse, " +
"marking a stark contrast to Trump's latest downturn, alongside an unprecedented " +
"surge in Biden's fundraising efforts as of 3/11/24, according to the TDPS Podcast. " +
"The analysis provides insights into the shifting dynamics of political support and " +
"the potential implications for future electoral strategies. Based on the information " +
"you provided, the video seems to discuss a recent event where former President " +
"Donald Trump made a controversial statement that shocked even his own audience. " +
"The video likely covers Trump's response to the State of the Union (SOTU) address " +
"and how it received negative feedback, possibly leading to a decline in his support " +
"or approval ratings. Additionally, it appears that the video touches upon a surge " +
"in fundraising for President Joe Biden's administration around March 11, 2024."
public init(dataService: DataService, audioController: AudioController) {
self.dataService = dataService
self.audioController = audioController
}
var body: some View {
// ZStack(alignment: Alignment(horizontal: .trailing, vertical: .top)) {
Group {
if viewModel.isLoading {
ProgressView()
} else {
itemBody
.task {
await viewModel.load(dataService: dataService)
}.onAppear {
self.audioController.play(itemAudioProperties: DigestAudioItem())
}
}
} .navigationTitle("Omnivore digest")
.navigationBarTitleDisplayMode(.inline)
// HStack(alignment: .top) {
// Spacer()
// closeButton
// }
// .padding(20)
// }
}
var closeButton: some View {
Button(action: {
dismiss()
}, label: {
ZStack {
Circle()
.foregroundColor(Color.appGrayText)
.frame(width: 36, height: 36)
.opacity(0.1)
Image(systemName: "xmark")
.font(.appCallout)
.frame(width: 36, height: 36)
}
})
.buttonStyle(.plain)
}
@available(iOS 17.0, *)
var itemBody: some View {
VStack {
ScrollView(.vertical) {
VStack(spacing: 20) {
Text("SOTU response collapses, Trump hits new low, Biden fundraising explodes 3/11/24 TDPS Podcast")
.font(.title)
Text(textBody)
.font(.body)
}
}
// .scrollTargetBehavior(.paging)
// .ignoresSafeArea()
MiniPlayerViewer()
.padding(.top, 10)
.padding(.bottom, 40)
.background(Color.themeTabBarColor)
.onTapGesture {
// showExpandedAudioPlayer = true
}
}
}
}
@MainActor
public class PreviewItemViewModel: ObservableObject {
let dataService: DataService
@Published var item: DigestItem
let showSwipeHint: Bool
@Published var isLoading = false
@Published var resultText: String?
@Published var promptDisplayText: String?
init(dataService: DataService, item: DigestItem, showSwipeHint: Bool) {
self.dataService = dataService
self.item = item
self.showSwipeHint = showSwipeHint
}
func loadResult() async {
// isLoading = true
// let taskId = try? await dataService.createAITask(
// extraText: extraText,
// libraryItemId: item?.id ?? "",
// promptName: "summarize-001"
// )
//
// if let taskId = taskId {
// do {
// let fetchedText = try await dataService.pollAITask(jobId: taskId, timeoutInterval: 30)
// resultText = fetchedText
// } catch {
// print("ERROR WITH RESULT TEXT: ", error)
// }
// } else {
// print("NO TASK ID: ", taskId)
// }
// isLoading = false
}
}
@MainActor
struct PreviewItemView: View {
@StateObject var viewModel: PreviewItemViewModel
var body: some View {
VStack(spacing: 10) {
HStack {
AsyncImage(url: viewModel.item.siteIcon) { phase in
if let image = phase.image {
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 20, height: 20, alignment: .center)
} else {
Color.appButtonBackground
.frame(width: 20, height: 20, alignment: .center)
}
}
Text(viewModel.item.site)
.font(Font.system(size: 14))
.frame(maxWidth: .infinity, alignment: .topLeading)
}
.padding(.top, 10)
Text(viewModel.item.title)
// .font(.body)
// .fontWeight(.semibold)
.font(Font.system(size: 18, weight: .semibold))
.frame(maxWidth: .infinity, alignment: .topLeading)
Text(viewModel.item.author)
.font(Font.system(size: 14))
.foregroundColor(Color(hex: "898989"))
.frame(maxWidth: .infinity, alignment: .topLeading)
Color(hex: "2A2A2A")
.frame(height: 1)
.frame(maxWidth: .infinity, alignment: .center)
.padding(.vertical, 20)
if viewModel.isLoading {
ProgressView()
.task {
await viewModel.loadResult()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
} else {
Text(viewModel.item.summaryText)
.font(Font.system(size: 16))
// .font(.body)
.lineSpacing(12.0)
.frame(maxWidth: .infinity, alignment: .topLeading)
HStack {
Button(action: {}, label: {
HStack(alignment: .center) {
Text("Start listening")
.font(Font.system(size: 14))
.frame(height: 42, alignment: .center)
Image(systemName: "play.fill")
.resizable()
.frame(width: 10, height: 10)
}
.padding(.horizontal, 15)
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(18)
})
Spacer()
}
.padding(.top, 20)
}
Spacer()
if viewModel.showSwipeHint {
VStack {
Image.doubleChevronUp
Text("Swipe up for next article")
.foregroundColor(Color(hex: "898989"))
}
.padding(.bottom, 50)
}
}.frame(maxWidth: .infinity, maxHeight: .infinity)
.padding(.top, 100)
.padding(.horizontal, 15)
}
}
struct RatingView: View {
@State private var rating: Int = 0
var body: some View {
VStack(spacing: 30) {
Text("Rate today's digest")
.font(.title)
.padding(.vertical, 40)
Text("I liked the stories picked for today's digest")
RatingWidget()
Text("The stories were interesting")
RatingWidget()
Text("The voices sounded good")
RatingWidget()
Text("I liked the music")
RatingWidget()
Spacer()
}.padding(.top, 60)
}
}
struct StarView: View {
var isFilled: Bool
var body: some View {
Image(systemName: isFilled ? "star.fill" : "star")
.foregroundColor(isFilled ? Color.yellow : Color.gray)
}
}
struct RatingWidget: View {
@State private var rating: Int = 0
var body: some View {
HStack {
ForEach(1...5, id: \.self) { index in
StarView(isFilled: index <= rating)
.onTapGesture {
rating = index
}
}
}
.padding()
.background(Color(hex: "313131"))
.cornerRadius(8)
// .shadow(radius: 3)
}
}

View File

@ -1,267 +0,0 @@
import SwiftUI
import Models
import Services
struct DigestItem {
let id: String
let site: String
let siteIcon: URL?
let author: String
let title: String
let summaryText: String
let keyPointsText: String
let highlightsText: String
}
@available(iOS 17.0, *)
@MainActor
struct LibraryDigestView: View {
let dataService: DataService
// @State private var currentIndex = 0
@State private var items: [DigestItem]
// @State private var preloadedItems: [Int: String] = [:]
@Environment(\.dismiss) private var dismiss
// let itemCount = 10 // Number of items to initially load
// let prefetchCount = 2 // Number of items to prefetch
public init(dataService: DataService) {
self.dataService = dataService
self.items = [
DigestItem(
id: "1468AFAA-88sdfsdfC-4546-BE02-EACF385288FC",
site: "CNBC.com",
siteIcon: URL(string: "https://www.cnbc.com/favicon.ico"),
author: "Kif Leswing",
title: "Apple shares just had their best day since last May",
summaryText: "In a significant political turn, the SOTU response faces unexpected collapse, marking a stark contrast to Trump's latest" +
" downturn, alongside an unprecedented surge in Biden's fundraising efforts as of 3/11/24, according to the TDPS Podcast. " +
"The analysis provides insights into the shifting dynamics of political support and the potential implications for future " +
"electoral strategies. ",
keyPointsText: "Key points from the article:",
highlightsText: "Highlights from the article:"
),
DigestItem(
id: "1468AFAA-8sdfsdffsdf-4546-BE02-EACF385288FC",
site: "CNBC.com",
siteIcon: URL(string: "https://www.cnbc.com/favicon.ico"),
author: "Kif Leswing",
title: "Apple shares just had their best day since last May",
summaryText: "In a significant political turn, the SOTU response faces unexpected collapse, marking a stark contrast to Trump's latest" +
" downturn, alongside an unprecedented surge in Biden's fundraising efforts as of 3/11/24, according to the TDPS Podcast. " +
"The analysis provides insights into the shifting dynamics of political support and the potential implications for future " +
"electoral strategies. ",
keyPointsText: "Key points from the article:",
highlightsText: "Highlights from the article:"
),
DigestItem(
id: "1468AFAA-882C-asdadfsa85288FC",
site: "CNBC.com",
siteIcon: URL(string: "https://www.cnbc.com/favicon.ico"),
author: "Kif Leswing",
title: "Apple shares just had their best day since last May",
summaryText: "In a significant political turn, the SOTU response faces unexpected collapse, marking a stark contrast to Trump's latest" +
" downturn, alongside an unprecedented surge in Biden's fundraising efforts as of 3/11/24, according to the TDPS Podcast. " +
"The analysis provides insights into the shifting dynamics of political support and the potential implications for future " +
"electoral strategies. ",
keyPointsText: "Key points from the article:",
highlightsText: "Highlights from the article:"
)
]
// currentIndex = 0
// _preloadedItems = [Int:String]
}
var body: some View {
itemBody
}
@available(iOS 17.0, *)
var itemBody: some View {
ScrollView(.vertical) {
LazyVStack(spacing: 0) {
ForEach(Array(self.items.enumerated()), id: \.1.id) { idx, item in
PreviewItemView(
viewModel: PreviewItemViewModel(dataService: dataService, item: item, showSwipeHint: idx == 0)
)
.containerRelativeFrame([.horizontal, .vertical])
}
}
.scrollTargetLayout()
}
.scrollTargetBehavior(.paging)
.ignoresSafeArea()
// ScrollView(.horizontal, showsIndicators: false) {
// HStack(spacing: 0) {
// ForEach(items.indices, id: \.self) { index in
// if let item = preloadedItems[index] {
// ItemView(content: item)
// .onAppear {
// if index == items.count - prefetchCount {
// fetchItems()
// }
// }
// }
// }
// }
// }
// .frame(maxWidth: .infinity, maxHeight: .infinity)
.onAppear {
// fetchItems()
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in
// Pause any background tasks if necessary
}
}
// private func fetchItems() {
// // Simulate fetching items from an API
// for idx in currentIndex..<currentIndex + itemCount {
// fetchItem(index: idx)
// }
// currentIndex += itemCount
// }
//
// private func fetchItem(index: Int) {
// // Simulate fetching item content from an API
// DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// items.append("Item \(index + 1)")
// preloadNextItemIfNeeded(index: index)
// }
// }
//
// private func preloadNextItemIfNeeded(index: Int) {
// let nextIndex = index + 1
// if nextIndex < currentIndex + prefetchCount {
// fetchItem(index: nextIndex)
// }
// }
}
@MainActor
public class PreviewItemViewModel: ObservableObject {
let dataService: DataService
@Published var item: DigestItem
let showSwipeHint: Bool
@Published var isLoading = false
@Published var resultText: String?
@Published var promptDisplayText: String?
init(dataService: DataService, item: DigestItem, showSwipeHint: Bool) {
self.dataService = dataService
self.item = item
self.showSwipeHint = showSwipeHint
}
func loadResult() async {
// isLoading = true
// let taskId = try? await dataService.createAITask(
// extraText: extraText,
// libraryItemId: item?.id ?? "",
// promptName: "summarize-001"
// )
//
// if let taskId = taskId {
// do {
// let fetchedText = try await dataService.pollAITask(jobId: taskId, timeoutInterval: 30)
// resultText = fetchedText
// } catch {
// print("ERROR WITH RESULT TEXT: ", error)
// }
// } else {
// print("NO TASK ID: ", taskId)
// }
// isLoading = false
}
}
@MainActor
struct PreviewItemView: View {
@StateObject var viewModel: PreviewItemViewModel
var body: some View {
VStack(spacing: 10) {
HStack {
AsyncImage(url: viewModel.item.siteIcon) { phase in
if let image = phase.image {
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 20, height: 20, alignment: .center)
} else {
Color.appButtonBackground
.frame(width: 20, height: 20, alignment: .center)
}
}
Text(viewModel.item.site)
.font(Font.system(size: 14))
.frame(maxWidth: .infinity, alignment: .topLeading)
}
.padding(.top, 10)
Text(viewModel.item.title)
// .font(.body)
// .fontWeight(.semibold)
.font(Font.system(size: 18, weight: .semibold))
.frame(maxWidth: .infinity, alignment: .topLeading)
Text(viewModel.item.author)
.font(Font.system(size: 14))
.foregroundColor(Color(hex: "898989"))
.frame(maxWidth: .infinity, alignment: .topLeading)
Color(hex: "2A2A2A")
.frame(height: 1)
.frame(maxWidth: .infinity, alignment: .center)
.padding(.vertical, 20)
if viewModel.isLoading {
ProgressView()
.task {
await viewModel.loadResult()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
} else {
Text(viewModel.item.summaryText)
.font(Font.system(size: 16))
// .font(.body)
.lineSpacing(12.0)
.frame(maxWidth: .infinity, alignment: .topLeading)
HStack {
Button(action: {}, label: {
HStack(alignment: .center) {
Text("Start listening")
.font(Font.system(size: 14))
.frame(height: 42, alignment: .center)
Image(systemName: "play.fill")
.resizable()
.frame(width: 10, height: 10)
}
.padding(.horizontal, 15)
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(18)
})
Spacer()
}
.padding(.top, 20)
}
Spacer()
if viewModel.showSwipeHint {
VStack {
Image.doubleChevronUp
Text("Swipe up for next article")
.foregroundColor(Color(hex: "898989"))
}
.padding(.bottom, 50)
}
}.frame(maxWidth: .infinity, maxHeight: .infinity)
.padding(.top, 100)
.padding(.horizontal, 15)
}
}

View File

@ -45,7 +45,7 @@
if audioController.playbackError {
return AnyView(Color.clear)
}
if let itemID = audioController.itemAudioProperties?.itemID, audioController.isLoadingItem(itemID: itemID) {
if audioController.isLoadingItem(audioController.itemAudioProperties) {
return AnyView(ProgressView())
} else {
return AnyView(Button(
@ -98,11 +98,11 @@
}
).padding(.trailing, 5)
if !(audioController.itemAudioProperties?.isArchived ?? false) {
if !((audioController.itemAudioProperties as? LinkedItemAudioProperties)?.isArchived ?? false) {
Button(
action: { performArchive() },
label: {
if audioController.itemAudioProperties?.isArchived ?? false {
if (audioController.itemAudioProperties as? LinkedItemAudioProperties)?.isArchived ?? false {
Image
.toolbarUnarchive
.foregroundColor(Color.toolbarItemForeground)
@ -130,20 +130,20 @@
}
}
func performViewArticle() {
if let objectID = audioController.itemAudioProperties?.objectID {
viewArticle(objectID)
}
}
// func performViewArticle() {
// if let objectID = audioController.itemAudioProperties?.objectID {
// viewArticle(objectID)
// }
// }
func performDelete() {
if let objectID = audioController.itemAudioProperties?.objectID {
if let objectID = (audioController.itemAudioProperties as? LinkedItemAudioProperties)?.objectID {
delete(objectID)
}
}
func performArchive() {
if let objectID = audioController.itemAudioProperties?.objectID {
if let objectID = (audioController.itemAudioProperties as? LinkedItemAudioProperties)?.objectID {
archive(objectID)
}
}
@ -467,7 +467,7 @@
}
public var innerBody: some View {
if let itemAudioProperties = self.audioController.itemAudioProperties {
if let itemAudioProperties = self.audioController.itemAudioProperties as? LinkedItemAudioProperties {
return AnyView(
playerContent(itemAudioProperties)
.tint(.appGrayTextContrast)

View File

@ -23,14 +23,14 @@
public var body: some View {
ZStack(alignment: .center) {
presentingView
if let itemAudioProperties = self.audioController.itemAudioProperties {
if self.audioController.itemAudioProperties != nil {
ZStack(alignment: .bottom) {
Color.systemBackground.edgesIgnoringSafeArea(.bottom)
.frame(height: expanded ? 0 : 110, alignment: .bottom)
VStack {
Spacer(minLength: 0)
MiniPlayerViewer(itemAudioProperties: itemAudioProperties)
MiniPlayerViewer()
}
}
}

View File

@ -13,8 +13,6 @@
@State var expanded = true
let itemAudioProperties: LinkedItemAudioProperties
var playPauseButtonImage: String {
switch audioController.state {
case .playing:
@ -32,7 +30,7 @@
if audioController.playbackError {
return AnyView(Color.clear)
}
if let itemID = audioController.itemAudioProperties?.itemID, audioController.isLoadingItem(itemID: itemID) {
if audioController.isLoadingItem(audioController.itemAudioProperties) {
return AnyView(ProgressView())
} else {
return AnyView(Button(
@ -84,8 +82,8 @@
.buttonStyle(PlainButtonStyle())
}
func artwork(_ itemAudioProperties: LinkedItemAudioProperties, forDimensions dim: Double) -> some View {
if let imageURL = itemAudioProperties.imageURL {
func artwork(_ itemAudioProperties: AudioItemProperties?, forDimensions dim: Double) -> some View {
if let imageURL = itemAudioProperties?.imageURL {
return AnyView(AsyncImage(url: imageURL) { phase in
if let image = phase.image {
image
@ -125,9 +123,9 @@
Text("There was an error playing back your audio.").foregroundColor(Color.red).font(.footnote)
Spacer(minLength: 0)
} else {
artwork(itemAudioProperties, forDimensions: 50)
artwork(audioController.itemAudioProperties, forDimensions: 50)
Text(itemAudioProperties.title)
Text(audioController.itemAudioProperties?.title ?? "")
.font(Font.system(size: 17, weight: .medium))
.fixedSize(horizontal: false, vertical: true)
.lineLimit(2)

View File

@ -204,6 +204,9 @@ struct AnimatingCellHeight: AnimatableModifier {
@ObservedObject var viewModel: HomeFeedViewModel
@State private var selection = Set<String>()
@AppStorage("LibraryList::digestEnabled") var digestEnabled = false
@AppStorage("LibraryList::hasCheckedForDigestFeature") var hasCheckedForDigestFeature = false
init(viewModel: HomeFeedViewModel, isEditMode: Binding<EditMode>) {
_viewModel = ObservedObject(wrappedValue: viewModel)
_isEditMode = isEditMode
@ -225,7 +228,7 @@ struct AnimatingCellHeight: AnimatableModifier {
}
var body: some View {
ZStack {
ZStack {
HomeFeedView(
listTitle: $listTitle,
isListScrolled: $isListScrolled,
@ -265,8 +268,8 @@ struct AnimatingCellHeight: AnimatableModifier {
VStack(spacing: 0) {
Spacer()
if let audioProperties = audioController.itemAudioProperties {
MiniPlayerViewer(itemAudioProperties: audioProperties)
if audioController.itemAudioProperties != nil {
MiniPlayerViewer()
.padding(.top, 10)
.padding(.bottom, 20)
.background(Color.themeTabBarColor)
@ -317,6 +320,15 @@ struct AnimatingCellHeight: AnimatableModifier {
}
)
}
.fullScreenCover(isPresented: $showLibraryDigest) {
if #available(iOS 17.0, *) {
NavigationView {
FullScreenDigestView(dataService: dataService, audioController: audioController)
}
} else {
Text("Sorry digest is only available on iOS 17 and above")
}
}
.toolbar {
toolbarItems
}
@ -339,6 +351,20 @@ struct AnimatingCellHeight: AnimatableModifier {
viewModel.stopUsingFollowingPrimer = true
}
}
.task {
do {
if let viewer = try await dataService.fetchViewer() {
digestEnabled = viewer.digestEnabled ?? false
if !hasCheckedForDigestFeature {
hasCheckedForDigestFeature = true
// selectedTab = "digest"
}
}
} catch {
print("ERROR FETCHING VIEWER: ", error)
print("")
}
}
.environment(\.editMode, self.$isEditMode)
.navigationBarTitleDisplayMode(.inline)
}
@ -383,15 +409,14 @@ struct AnimatingCellHeight: AnimatableModifier {
if isEditMode == .active {
Button(action: { isEditMode = .inactive }, label: { Text("Cancel") })
} else {
// if #available(iOS 17.0, *) {
// Button(
// action: { showLibraryDigest = true },
// label: { Image(systemName: "sparkles") }
// )
// .buttonStyle(.plain)
// .padding(.trailing, 4)
// }
if #available(iOS 17.0, *) {
Button(
action: { showLibraryDigest = true },
label: { Image.tabDigestSelected }
)
.buttonStyle(.plain)
.padding(.trailing, 4)
}
if prefersListLayout {
Button(
action: { isEditMode = isEditMode == .active ? .inactive : .active },
@ -481,12 +506,12 @@ struct AnimatingCellHeight: AnimatableModifier {
let showFeatureCards: Bool
var slideTransition: PresentationLinkTransition {
PresentationLinkTransition.slide(
options: PresentationLinkTransition.SlideTransitionOptions(edge: .trailing,
options:
PresentationLinkTransition.Options(
modalPresentationCapturesStatusBarAppearance: true
)
))
options: PresentationLinkTransition.SlideTransitionOptions(
edge: .trailing,
options: PresentationLinkTransition.Options(
modalPresentationCapturesStatusBarAppearance: true
)
))
}
var body: some View {
@ -494,12 +519,12 @@ struct AnimatingCellHeight: AnimatableModifier {
if let linkRequest = viewModel.linkRequest, viewModel.currentListConfig?.hasReadNowSection ?? false {
PresentationLink(
transition: PresentationLinkTransition.slide(
options: PresentationLinkTransition.SlideTransitionOptions(edge: .trailing,
options:
PresentationLinkTransition.Options(
modalPresentationCapturesStatusBarAppearance: true,
preferredPresentationBackgroundColor: ThemeManager.currentBgColor
))),
options: PresentationLinkTransition.SlideTransitionOptions(
edge: .trailing,
options: PresentationLinkTransition.Options(
modalPresentationCapturesStatusBarAppearance: true,
preferredPresentationBackgroundColor: ThemeManager.currentBgColor
))),
isPresented: $viewModel.presentWebContainer,
destination: {
WebReaderLoadingContainer(requestID: linkRequest.serverID)

View File

@ -1,10 +1,3 @@
//
// File.swift
//
//
// Created by Jackson Harper on 6/29/23.
//
import Foundation
import Models
import Services
@ -21,9 +14,6 @@ struct LibraryTabView: View {
@AppStorage("LibraryTabView::hideFollowingTab") var hideFollowingTab = false
@AppStorage(UserDefaultKey.lastSelectedTabItem.rawValue) var selectedTab = "inbox"
@AppStorage("LibraryTabView::digestEnabled") var digestEnabled = false
@AppStorage("LibraryTabView::hasCheckedForDigestFeature") var hasCheckedForDigestFeature = false
@State var isEditMode: EditMode = .inactive
@State var showExpandedAudioPlayer = false
@State var presentPushContainer = true
@ -79,6 +69,8 @@ struct LibraryTabView: View {
@State var operationStatus: OperationStatus = .none
@State var operationMessage: String?
@State var digestEnabled = false
var showDigest: Bool {
if digestEnabled, #available(iOS 17.0, *) {
return true
@ -143,7 +135,7 @@ struct LibraryTabView: View {
if showDigest, #available(iOS 17.0, *) {
NavigationView {
LibraryDigestView(dataService: dataService)
DigestView(dataService: dataService)
.navigationBarTitleDisplayMode(.inline)
.navigationViewStyle(.stack)
}.tag("digest")
@ -165,8 +157,8 @@ struct LibraryTabView: View {
}
}
if let audioProperties = audioController.itemAudioProperties {
MiniPlayerViewer(itemAudioProperties: audioProperties)
if audioController.itemAudioProperties != nil {
MiniPlayerViewer()
.onTapGesture {
showExpandedAudioPlayer = true
}
@ -236,19 +228,5 @@ struct LibraryTabView: View {
}
selectedTab = "inbox"
}
.task {
do {
if let viewer = try await dataService.fetchViewer() {
digestEnabled = viewer.digestEnabled ?? false
if !hasCheckedForDigestFeature {
hasCheckedForDigestFeature = true
selectedTab = "digest"
}
}
} catch {
print("ERROR FETCHING VIEWER: ", error)
print("")
}
}
}
}

View File

@ -52,7 +52,7 @@ import Transmission
return AnyView(splitView)
#endif
}
func startTimer(amount: Int) {
self.snackbarTimer = Timer.scheduledTimer(withTimeInterval: TimeInterval(amount / 1000), repeats: false) { _ in
DispatchQueue.main.async {

View File

@ -133,7 +133,7 @@ struct WebReaderContainerView: View {
#if os(iOS)
var audioNavbarItem: some View {
if !audioController.playbackError, audioController.isLoadingItem(itemID: item.unwrappedID) {
if audioController.isLoadingItem(audioController.itemAudioProperties) {
return AnyView(ProgressView()
.padding(.horizontal))
} else {
@ -500,20 +500,6 @@ struct WebReaderContainerView: View {
.formSheet(isPresented: $showOpenArchiveSheet) {
OpenArchiveTodayView(item: item)
}
.formSheet(isPresented: $showExplainSheet) {
ExplainView(
viewModel: ExplainViewModel(
dataService: dataService,
item: self.item,
promptName: "explain-text-001",
extraText: viewModel.explainText
),
dismissAction: {
viewModel.explainText = nil
showExplainSheet = false
}
)
}
#endif
.sheet(isPresented: $showHighlightAnnotationModal) {
NavigationView {
@ -629,8 +615,8 @@ struct WebReaderContainerView: View {
.offset(y: navBarVisible ? 0 : -150)
Spacer()
if let audioProperties = audioController.itemAudioProperties {
MiniPlayerViewer(itemAudioProperties: audioProperties)
if audioController.itemAudioProperties != nil {
MiniPlayerViewer()
.padding(.top, 10)
.padding(.bottom, showBottomBar ? 10 : 40)
.background(Color.themeTabBarColor)