Start adding following modal, use system-ui for the font
|
|
@ -7,7 +7,7 @@ import com.google.gson.Gson
|
|||
|
||||
enum class WebFont(val displayText: String, val rawValue: String) {
|
||||
INTER("Inter", "Inter"),
|
||||
SYSTEM("System Default", "unset"),
|
||||
SYSTEM("System Default", "system-ui"),
|
||||
OPEN_DYSLEXIC("Open Dyslexic", "OpenDyslexic"),
|
||||
MERRIWEATHER("Merriweather", "Merriweather"),
|
||||
LORA("Lora", "Lora"),
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import Views
|
|||
|
||||
@AppStorage(UserDefaultKey.lastSelectedFeaturedItemFilter.rawValue) var featureFilter = FeaturedItemFilter.continueReading.rawValue
|
||||
|
||||
var limit = 10
|
||||
var limit = 6
|
||||
var cursor: String?
|
||||
var totalCount: Int?
|
||||
|
||||
|
|
|
|||
|
|
@ -147,7 +147,6 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
@State var isListScrolled = false
|
||||
@State var listTitle = ""
|
||||
@State var isEditMode: EditMode = .inactive
|
||||
@State var showOpenAIVoices = false
|
||||
@State var showExpandedAudioPlayer = false
|
||||
|
||||
@EnvironmentObject var dataService: DataService
|
||||
|
|
@ -155,7 +154,6 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
@Environment(\.horizontalSizeClass) var horizontalSizeClass
|
||||
|
||||
@AppStorage(UserDefaultKey.homeFeedlayoutPreference.rawValue) var prefersListLayout = true
|
||||
@AppStorage(UserDefaultKey.openAIPrimerDisplayed.rawValue) var openAIPrimerDisplayed = false
|
||||
|
||||
@ObservedObject var viewModel: HomeFeedViewModel
|
||||
@State private var selection = Set<String>()
|
||||
|
|
@ -249,6 +247,11 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
}, toastOperationHandler: nil)
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showAddLinkView) {
|
||||
NavigationView {
|
||||
LibraryAddLinkView()
|
||||
}
|
||||
}
|
||||
.fullScreenCover(isPresented: $showExpandedAudioPlayer) {
|
||||
ExpandedAudioPlayer(
|
||||
delete: {
|
||||
|
|
@ -287,11 +290,6 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
.fullScreenCover(isPresented: $searchPresented) {
|
||||
LibrarySearchView(homeFeedViewModel: self.viewModel)
|
||||
}
|
||||
.sheet(isPresented: $showAddLinkView) {
|
||||
NavigationView {
|
||||
LibraryAddLinkView()
|
||||
}
|
||||
}
|
||||
.task {
|
||||
await viewModel.loadFilters(dataService: dataService)
|
||||
if viewModel.appliedFilter == nil {
|
||||
|
|
|
|||
|
|
@ -1,135 +0,0 @@
|
|||
// swiftlint:disable line_length
|
||||
#if os(iOS)
|
||||
import Foundation
|
||||
import Models
|
||||
import Services
|
||||
import SwiftUI
|
||||
import Views
|
||||
|
||||
struct OpenAIVoiceItem {
|
||||
let name: String
|
||||
let key: String
|
||||
}
|
||||
|
||||
public struct OpenAIVoicesModal: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
let audioController: AudioController
|
||||
|
||||
let message: String = """
|
||||
We've added six new voices powered by OpenAI and enabled them for all users. If you are already using our Ultra Realistic voices, don't worry, trying these voices will not remove you from the ultra realistic beta.
|
||||
|
||||
[Tell your friends about Omnivore](https://omnivore.app)
|
||||
"""
|
||||
|
||||
@State var playbackSample: String?
|
||||
|
||||
let voices = [
|
||||
OpenAIVoiceItem(name: "Alloy", key: "openai-alloy"),
|
||||
OpenAIVoiceItem(name: "Echo", key: "openai-echo"),
|
||||
OpenAIVoiceItem(name: "Fable", key: "openai-fable"),
|
||||
OpenAIVoiceItem(name: "Onyx", key: "openai-onyx"),
|
||||
OpenAIVoiceItem(name: "Nova", key: "openai-nova"),
|
||||
OpenAIVoiceItem(name: "Shimmer", key: "openai-shimmer")
|
||||
]
|
||||
|
||||
var closeButton: some View {
|
||||
Button(action: {
|
||||
dismiss()
|
||||
}, label: {
|
||||
ZStack {
|
||||
Circle()
|
||||
.foregroundColor(Color.circleButtonBackground)
|
||||
.frame(width: 30, height: 30)
|
||||
|
||||
Image(systemName: "xmark")
|
||||
.resizable(resizingMode: Image.ResizingMode.stretch)
|
||||
.foregroundColor(Color.circleButtonForeground)
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.font(Font.title.weight(.bold))
|
||||
.frame(width: 12, height: 12)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
HStack {
|
||||
Text("New voices powered by OpenAI")
|
||||
.font(Font.system(size: 20, weight: .bold))
|
||||
Spacer()
|
||||
closeButton
|
||||
}
|
||||
.padding(.top, 16)
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
List {
|
||||
Section {
|
||||
let parsedMessage = try? AttributedString(markdown: message,
|
||||
options: .init(interpretedSyntax: .inlineOnly))
|
||||
Text(parsedMessage ?? "")
|
||||
.multilineTextAlignment(.leading)
|
||||
.foregroundColor(Color.appGrayTextContrast)
|
||||
.accentColor(.blue)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.top, 16)
|
||||
}
|
||||
|
||||
Section {
|
||||
ForEach(voices, id: \.self.name) { voice in
|
||||
voiceRow(for: voice)
|
||||
}
|
||||
}
|
||||
}
|
||||
.environmentObject(audioController)
|
||||
}
|
||||
|
||||
func voiceRow(for voice: OpenAIVoiceItem) -> some View {
|
||||
Button(action: {
|
||||
if audioController.isPlayingSample(voice: voice.key) {
|
||||
playbackSample = nil
|
||||
audioController.stopVoiceSample()
|
||||
}
|
||||
playbackSample = voice.key
|
||||
audioController.currentVoice = voice.key
|
||||
audioController.playVoiceSample(voice: voice.key)
|
||||
Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { timer in
|
||||
let playing = audioController.isPlayingSample(voice: voice.key)
|
||||
if playing {
|
||||
playbackSample = voice.key
|
||||
} else if !playing {
|
||||
// If the playback sample is something else, its taken ownership
|
||||
// of the value so we just ignore it and shut down our timer.
|
||||
if playbackSample == voice.key {
|
||||
playbackSample = nil
|
||||
}
|
||||
timer.invalidate()
|
||||
}
|
||||
}
|
||||
}, label: {
|
||||
HStack {
|
||||
if playbackSample == voice.key {
|
||||
Image(systemName: "stop.circle")
|
||||
.font(.appTitleTwo)
|
||||
.padding(.trailing, 16)
|
||||
} else {
|
||||
Image(systemName: "play.circle")
|
||||
.font(.appTitleTwo)
|
||||
.padding(.trailing, 16)
|
||||
}
|
||||
Text(voice.name)
|
||||
Spacer()
|
||||
|
||||
if audioController.currentVoice == voice.key {
|
||||
if audioController.isPlaying, audioController.isLoading {
|
||||
ProgressView()
|
||||
} else {
|
||||
Image(systemName: "checkmark")
|
||||
}
|
||||
}
|
||||
}.contentShape(Rectangle())
|
||||
})
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -21,7 +21,9 @@ struct LibraryTabView: View {
|
|||
|
||||
@AppStorage("LibraryTabView::hideFollowingTab") var hideFollowingTab = false
|
||||
@AppStorage(UserDefaultKey.lastSelectedTabItem.rawValue) var selectedTab = "inbox"
|
||||
@AppStorage(UserDefaultKey.followingPrimerDisplayed.rawValue) var followingPrimerDisplayed = false
|
||||
|
||||
@State var showFollowingPrimer = true
|
||||
@State var showExpandedAudioPlayer = false
|
||||
|
||||
private let syncManager = LibrarySyncManager()
|
||||
|
|
@ -72,6 +74,12 @@ struct LibraryTabView: View {
|
|||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
PresentationLink(transition: UIDevice.isIPad ? .popover : .sheet(detents: [.medium]), isPresented: $showFollowingPrimer) {
|
||||
FollowingViewModal()
|
||||
} label: {
|
||||
EmptyView()
|
||||
}
|
||||
|
||||
TabView(selection: $selectedTab) {
|
||||
if !hideFollowingTab {
|
||||
NavigationView {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public enum UserDefaultKey: String {
|
|||
case recentSearchTerms
|
||||
case audioPlayerExpanded
|
||||
case themeName
|
||||
case openAIPrimerDisplayed
|
||||
case followingPrimerDisplayed
|
||||
case notificationsEnabled
|
||||
case deviceTokenID
|
||||
case userWordsPerMinute
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import Utils
|
|||
|
||||
public enum WebFont: String, CaseIterable {
|
||||
case inter = "Inter"
|
||||
case system = "unset"
|
||||
case system = "system-ui"
|
||||
case merriweather = "Merriweather"
|
||||
case lora = "Lora"
|
||||
case opensans = "Open Sans"
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 844 B |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 7.3 KiB |