Allog config of digest
This commit is contained in:
@ -9,7 +9,7 @@ import Transmission
|
||||
@MainActor
|
||||
public class DigestConfigViewModel: ObservableObject {
|
||||
@Published var isLoading = false
|
||||
@Published var alreadyGranted = false
|
||||
@Published var digestEnabled = false
|
||||
|
||||
@Published var isIneligible = false
|
||||
@Published var hasOptInError = false
|
||||
@ -23,7 +23,7 @@ public class DigestConfigViewModel: ObservableObject {
|
||||
func checkAlreadyOptedIn(dataService: DataService) async {
|
||||
isLoading = true
|
||||
if let user = try? await dataService.fetchViewer() {
|
||||
alreadyGranted = user.hasFeatureGranted("ai-digest")
|
||||
digestEnabled = user.hasFeatureGranted("ai-digest")
|
||||
}
|
||||
isLoading = false
|
||||
}
|
||||
@ -35,6 +35,8 @@ public class DigestConfigViewModel: ObservableObject {
|
||||
throw BasicError.message(messageText: "Could not opt into feature")
|
||||
}
|
||||
try await dataService.setupUserDigestConfig()
|
||||
try await dataService.refreshDigest()
|
||||
digestEnabled = true
|
||||
} catch {
|
||||
if error is IneligibleError {
|
||||
isIneligible = true
|
||||
@ -42,7 +44,6 @@ public class DigestConfigViewModel: ObservableObject {
|
||||
hasOptInError = true
|
||||
}
|
||||
}
|
||||
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
@ -84,7 +85,8 @@ struct DigestConfigView: View {
|
||||
ProgressView()
|
||||
Spacer()
|
||||
}
|
||||
} else if viewModel.alreadyGranted {
|
||||
.padding(.top, 50)
|
||||
} else if viewModel.digestEnabled {
|
||||
Text("You've been added to the AI Digest demo. You first issue should be ready soon.")
|
||||
.padding(15)
|
||||
} else if viewModel.isIneligible {
|
||||
@ -160,7 +162,9 @@ struct DigestConfigView: View {
|
||||
.buttonStyle(RoundedRectButtonStyle())
|
||||
|
||||
Button(action: {
|
||||
// viewModel.en
|
||||
Task {
|
||||
await viewModel.enableDigest(dataService: dataService)
|
||||
}
|
||||
}, label: { Text("Enable digest") })
|
||||
.buttonStyle(RoundedRectButtonStyle(color: Color.blue, textColor: Color.white))
|
||||
}
|
||||
|
||||
@ -38,6 +38,7 @@ func formatTimeInterval(_ time: TimeInterval) -> String? {
|
||||
@MainActor
|
||||
public class FullScreenDigestViewModel: ObservableObject {
|
||||
@Published var isLoading = false
|
||||
@Published var hasError = false
|
||||
@Published var digest: DigestResult?
|
||||
@Published var chapterInfo: [(DigestChapter, DigestChapterData)]?
|
||||
@Published var presentedLibraryItem: String?
|
||||
@ -46,6 +47,9 @@ public class FullScreenDigestViewModel: ObservableObject {
|
||||
@AppStorage(UserDefaultKey.lastVisitedDigestId.rawValue) var lastVisitedDigestId = ""
|
||||
|
||||
func load(dataService: DataService, audioController: AudioController) async {
|
||||
hasError = false
|
||||
isLoading = true
|
||||
|
||||
if !dataService.digestNeedsRefresh() {
|
||||
if let digest = dataService.loadStoredDigest() {
|
||||
self.digest = digest
|
||||
@ -72,6 +76,8 @@ public class FullScreenDigestViewModel: ObservableObject {
|
||||
let chapterData = self.chapterInfo?.map { $0.1 }
|
||||
audioController.play(itemAudioProperties: DigestAudioItem(digest: digest, chapters: chapterData ?? []))
|
||||
}
|
||||
} else {
|
||||
hasError = true
|
||||
}
|
||||
|
||||
isLoading = false
|
||||
@ -164,6 +170,19 @@ struct FullScreenDigestView: View {
|
||||
ProgressView()
|
||||
Spacer()
|
||||
}
|
||||
} else if viewModel.hasError {
|
||||
VStack {
|
||||
Spacer()
|
||||
Text("There was an error loading your digest.")
|
||||
Button(action: {
|
||||
Task {
|
||||
await viewModel.load(dataService: dataService, audioController: audioController)
|
||||
}
|
||||
}, label: { Text("Try again") })
|
||||
.buttonStyle(RoundedRectButtonStyle(color: Color.blue, textColor: Color.white))
|
||||
|
||||
Spacer()
|
||||
}
|
||||
} else {
|
||||
itemBody
|
||||
}
|
||||
|
||||
@ -5866,6 +5866,68 @@ extension Selection where TypeLock == Never, Type == Never {
|
||||
typealias DeviceTokensSuccess<T> = Selection<T, Objects.DeviceTokensSuccess>
|
||||
}
|
||||
|
||||
extension Objects {
|
||||
struct DigestConfig {
|
||||
let __typename: TypeName = .digestConfig
|
||||
let channels: [String: [String?]]
|
||||
|
||||
enum TypeName: String, Codable {
|
||||
case digestConfig = "DigestConfig"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Objects.DigestConfig: Decodable {
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
|
||||
|
||||
var map = HashMap()
|
||||
for codingKey in container.allKeys {
|
||||
if codingKey.isTypenameKey { continue }
|
||||
|
||||
let alias = codingKey.stringValue
|
||||
let field = GraphQLField.getFieldNameFromAlias(alias)
|
||||
|
||||
switch field {
|
||||
case "channels":
|
||||
if let value = try container.decode([String?]?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
default:
|
||||
throw DecodingError.dataCorrupted(
|
||||
DecodingError.Context(
|
||||
codingPath: decoder.codingPath,
|
||||
debugDescription: "Unknown key \(field)."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
channels = map["channels"]
|
||||
}
|
||||
}
|
||||
|
||||
extension Fields where TypeLock == Objects.DigestConfig {
|
||||
func channels() throws -> [String?]? {
|
||||
let field = GraphQLField.leaf(
|
||||
name: "channels",
|
||||
arguments: []
|
||||
)
|
||||
select(field)
|
||||
|
||||
switch response {
|
||||
case let .decoding(data):
|
||||
return data.channels[field.alias!]
|
||||
case .mocking:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Selection where TypeLock == Never, Type == Never {
|
||||
typealias DigestConfig<T> = Selection<T, Objects.DigestConfig>
|
||||
}
|
||||
|
||||
extension Objects {
|
||||
struct DiscoverFeed {
|
||||
let __typename: TypeName = .discoverFeed
|
||||
@ -28003,7 +28065,7 @@ extension Selection where TypeLock == Never, Type == Never {
|
||||
extension Objects {
|
||||
struct UserPersonalization {
|
||||
let __typename: TypeName = .userPersonalization
|
||||
let digestConfig: [String: String]
|
||||
let digestConfig: [String: Objects.DigestConfig]
|
||||
let fields: [String: String]
|
||||
let fontFamily: [String: String]
|
||||
let fontSize: [String: Int]
|
||||
@ -28036,7 +28098,7 @@ extension Objects.UserPersonalization: Decodable {
|
||||
|
||||
switch field {
|
||||
case "digestConfig":
|
||||
if let value = try container.decode(String?.self, forKey: codingKey) {
|
||||
if let value = try container.decode(Objects.DigestConfig?.self, forKey: codingKey) {
|
||||
map.set(key: field, hash: alias, value: value as Any)
|
||||
}
|
||||
case "fields":
|
||||
@ -28114,18 +28176,19 @@ extension Objects.UserPersonalization: Decodable {
|
||||
}
|
||||
|
||||
extension Fields where TypeLock == Objects.UserPersonalization {
|
||||
func digestConfig() throws -> String? {
|
||||
let field = GraphQLField.leaf(
|
||||
func digestConfig<Type>(selection: Selection<Type, Objects.DigestConfig?>) throws -> Type {
|
||||
let field = GraphQLField.composite(
|
||||
name: "digestConfig",
|
||||
arguments: []
|
||||
arguments: [],
|
||||
selection: selection.selection
|
||||
)
|
||||
select(field)
|
||||
|
||||
switch response {
|
||||
case let .decoding(data):
|
||||
return data.digestConfig[field.alias!]
|
||||
return try selection.decode(data: data.digestConfig[field.alias!])
|
||||
case .mocking:
|
||||
return nil
|
||||
return selection.mock()
|
||||
}
|
||||
}
|
||||
|
||||
@ -39401,6 +39464,21 @@ extension InputObjects {
|
||||
}
|
||||
}
|
||||
|
||||
extension InputObjects {
|
||||
struct DigestConfigInput: Encodable, Hashable {
|
||||
var channels: OptionalArgument<[OptionalArgument<String>]> = .absent()
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
if channels.hasValue { try container.encode(channels, forKey: .channels) }
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case channels
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension InputObjects {
|
||||
struct EditDiscoverFeedInput: Encodable, Hashable {
|
||||
var feedId: String
|
||||
@ -40379,7 +40457,7 @@ extension InputObjects {
|
||||
|
||||
extension InputObjects {
|
||||
struct SetUserPersonalizationInput: Encodable, Hashable {
|
||||
var digestConfig: OptionalArgument<String> = .absent()
|
||||
var digestConfig: OptionalArgument<InputObjects.DigestConfigInput> = .absent()
|
||||
|
||||
var fields: OptionalArgument<String> = .absent()
|
||||
|
||||
|
||||
@ -40,7 +40,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@available(iOS 16.0, *)
|
||||
struct LibraryItemEntity: AppEntity {
|
||||
static var defaultQuery = LibraryItemQuery()
|
||||
|
||||
@ -27,7 +27,9 @@ export async function updateDigestConfigMutation(
|
||||
}
|
||||
... on SetUserPersonalizationSuccess {
|
||||
updatedUserPersonalization {
|
||||
digestConfig
|
||||
digestConfig {
|
||||
channels
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,7 +58,9 @@ export function useGetUserPersonalization(): UserPersonalizationResult {
|
||||
getUserPersonalization {
|
||||
... on GetUserPersonalizationSuccess {
|
||||
userPersonalization {
|
||||
digestConfig
|
||||
digestConfig {
|
||||
channels
|
||||
}
|
||||
}
|
||||
}
|
||||
... on GetUserPersonalizationError {
|
||||
|
||||
Reference in New Issue
Block a user