diff --git a/FAWidget/Assets.xcassets/AccentColor.colorset/Contents.json b/FAWidget/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/FAWidget/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FAWidget/Assets.xcassets/AppIcon.appiconset/Contents.json b/FAWidget/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..2305880 --- /dev/null +++ b/FAWidget/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FAWidget/Assets.xcassets/Contents.json b/FAWidget/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/FAWidget/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FAWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json b/FAWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/FAWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FAWidget/FAWidget.swift b/FAWidget/FAWidget.swift new file mode 100644 index 0000000..d7d230f --- /dev/null +++ b/FAWidget/FAWidget.swift @@ -0,0 +1,84 @@ +// +// FAWidget.swift +// FAWidget +// +// Created by 湖北秦九 on 2025/11/10. +// + +import WidgetKit +import SwiftUI + +struct Provider: TimelineProvider { + func placeholder(in context: Context) -> SimpleEntry { + SimpleEntry(date: Date(), emoji: "😀") + } + + func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { + let entry = SimpleEntry(date: Date(), emoji: "😀") + completion(entry) + } + + func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) { + var entries: [SimpleEntry] = [] + + // Generate a timeline consisting of five entries an hour apart, starting from the current date. + let currentDate = Date() + for hourOffset in 0 ..< 5 { + let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! + let entry = SimpleEntry(date: entryDate, emoji: "😀") + entries.append(entry) + } + + let timeline = Timeline(entries: entries, policy: .atEnd) + completion(timeline) + } + +// func relevances() async -> WidgetRelevances { +// // Generate a list containing the contexts this widget is relevant in. +// } +} + +struct SimpleEntry: TimelineEntry { + let date: Date + let emoji: String +} + +struct FAWidgetEntryView : View { + var entry: Provider.Entry + + var body: some View { + VStack { + Text("Time:") + Text(entry.date, style: .time) + + Text("Emoji:") + Text(entry.emoji) + } + } +} + +struct FAWidget: Widget { + let kind: String = "FAWidget" + + var body: some WidgetConfiguration { + StaticConfiguration(kind: kind, provider: Provider()) { entry in + if #available(iOS 17.0, *) { + FAWidgetEntryView(entry: entry) + .containerBackground(.fill.tertiary, for: .widget) + } else { + FAWidgetEntryView(entry: entry) + .padding() + .background() + } + } + .configurationDisplayName("My Widget") + .description("This is an example widget.") + } +} + +//#Preview(as: .systemSmall) { +// FAWidget() +//} timeline: { +// SimpleEntry(date: .now, emoji: "😀") +// SimpleEntry(date: .now, emoji: "🤩") +//} diff --git a/FAWidget/FAWidgetBundle.swift b/FAWidget/FAWidgetBundle.swift new file mode 100644 index 0000000..aef11d9 --- /dev/null +++ b/FAWidget/FAWidgetBundle.swift @@ -0,0 +1,17 @@ +// +// FAWidgetBundle.swift +// FAWidget +// +// Created by 湖北秦九 on 2025/11/10. +// + +import WidgetKit +import SwiftUI + +@main +struct FAWidgetBundle: WidgetBundle { + var body: some Widget { +// FAWidget() + FAWidgetLiveActivity() + } +} diff --git a/FAWidget/FAWidgetLiveActivity.swift b/FAWidget/FAWidgetLiveActivity.swift new file mode 100644 index 0000000..6febf22 --- /dev/null +++ b/FAWidget/FAWidgetLiveActivity.swift @@ -0,0 +1,129 @@ +// +// FAWidgetLiveActivity.swift +// FAWidget +// +// Created by 湖北秦九 on 2025/11/10. +// + +import ActivityKit +import WidgetKit +import SwiftUI + + +struct FAWidgetLiveActivity: Widget { + var body: some WidgetConfiguration { + ActivityConfiguration(for: ActivityManagerAttributes.self) { context in + // 🔹 Live Activity UI(锁屏 & Banner) + HStack(spacing: 16) { + // 🔹 网络图片 + if let imageData = try? Data(contentsOf: URL(string: context.state.videoCoverPath)!), + let uiImage = UIImage(data: imageData) { + Image(uiImage: uiImage) + .resizable() + .frame(width: 108, height: 146) + .cornerRadius(8) + .clipped() + + + } else { + Image("logo100") + .frame(width: 100, height: 100) + .cornerRadius(8) + .clipped() + + } + + // 🔹 文本内容 + VStack(alignment: .leading, spacing: 4) { + VStack(alignment: .leading, spacing: 4) { + HStack { + Text("Playing") + .font(.caption) + .foregroundColor(.gray) + Spacer() + Image("logo40") + } + + Text(context.state.videoTitle) + .font(.headline) + .foregroundColor(.black) + + Text("Ep.\(context.state.videoEpisode)") + .font(.subheadline) + .foregroundColor(.gray) + } + + Spacer() + + // 🔹 按钮 + Button(action: { print("Watch Now") }) { + HStack { + Spacer() + Image(systemName: "play.fill") + Text("Watch Now") + Spacer() + } + .padding(.horizontal, 14) + .padding(.vertical, 8) + .background( + LinearGradient(colors: [Color.red, Color.orange], startPoint: .leading, endPoint: .trailing) + ) + .foregroundColor(.white) + .cornerRadius(20) + } + } + } + .padding() + .background(RoundedRectangle(cornerRadius: 8).fill(Color.white)) + .activityBackgroundTint(Color.white) + .activitySystemActionForegroundColor(.primary) + .widgetURL(URL(string: "fableonapp://liveActivity?short_play_id=\(context.state.videoId)")) + + } dynamicIsland: { context in + DynamicIsland { + // 🔹 Expanded UI + DynamicIslandExpandedRegion(.leading) { + VStack (alignment: .center) { + Spacer() + Image("logo40") + .resizable() + .frame(width: 40, height: 40) + .cornerRadius(5) + Spacer() + } + } + + DynamicIslandExpandedRegion(.center) { + VStack (alignment: .center) { + Spacer() + Text(context.state.videoTitle) + .font(.headline) + .foregroundColor(.primary) + Spacer() + + } + } + + DynamicIslandExpandedRegion(.trailing) { + VStack (alignment: .center) { + Spacer() + Text("Ep.\(context.state.videoEpisode)") + .font(.subheadline) + .foregroundColor(.secondary) + Spacer() + } + } + } compactLeading: { + Image("logo40") + } compactTrailing: { + Text("Ep.\(context.state.videoEpisode)") + .font(.caption2) + } minimal: { + Image("logo40") + } + .widgetURL(URL(string: "fableonapp://liveActivity?short_play_id=\(context.state.videoId)")) + .keylineTint(Color.red) + } + } +} + diff --git a/FAWidget/Info.plist b/FAWidget/Info.plist new file mode 100644 index 0000000..0f118fb --- /dev/null +++ b/FAWidget/Info.plist @@ -0,0 +1,11 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/FAWidgetExtension.entitlements b/FAWidgetExtension.entitlements new file mode 100644 index 0000000..06a809b --- /dev/null +++ b/FAWidgetExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.hn.qinjiu.fableon + + + diff --git a/Fableon.xcodeproj/project.pbxproj b/Fableon.xcodeproj/project.pbxproj index 4a14494..02cc1c1 100644 --- a/Fableon.xcodeproj/project.pbxproj +++ b/Fableon.xcodeproj/project.pbxproj @@ -227,6 +227,21 @@ 03E9A74E2EB5E0F7000D1067 /* FALanguageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A74D2EB5E0F7000D1067 /* FALanguageModel.swift */; }; 03E9A7502EB5EAC0000D1067 /* FALanguageDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A74F2EB5EAC0000D1067 /* FALanguageDataModel.swift */; }; 03E9A7522EB83A58000D1067 /* FAAppStartViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7512EB83A58000D1067 /* FAAppStartViewController.swift */; }; + 03E9A75E2EC19101000D1067 /* NotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 03E9A7572EC19101000D1067 /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 03E9A7682EC19110000D1067 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7652EC19110000D1067 /* NotificationService.swift */; }; + 03E9A76F2EC1950F000D1067 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03E9A76E2EC1950F000D1067 /* WidgetKit.framework */; }; + 03E9A7712EC1950F000D1067 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03E9A7702EC1950F000D1067 /* SwiftUI.framework */; }; + 03E9A77E2EC19510000D1067 /* FAWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 03E9A76D2EC1950F000D1067 /* FAWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 03E9A7892EC19516000D1067 /* FAWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7842EC19516000D1067 /* FAWidget.swift */; }; + 03E9A78A2EC19516000D1067 /* FAWidgetBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7852EC19516000D1067 /* FAWidgetBundle.swift */; }; + 03E9A78B2EC19516000D1067 /* FAWidgetLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7862EC19516000D1067 /* FAWidgetLiveActivity.swift */; }; + 03E9A78C2EC19516000D1067 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 03E9A7832EC19516000D1067 /* Assets.xcassets */; }; + 03E9A7902EC1B007000D1067 /* FAActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A78F2EC1B007000D1067 /* FAActivityManager.swift */; }; + 03E9A7912EC1B007000D1067 /* FAActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A78F2EC1B007000D1067 /* FAActivityManager.swift */; }; + 03E9A7942EC1B24E000D1067 /* UIImage+FAAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7932EC1B24E000D1067 /* UIImage+FAAdd.swift */; }; + 03E9A7972EC2C7DF000D1067 /* FAHomeNewTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7962EC2C7DF000D1067 /* FAHomeNewTransformer.swift */; }; + 03E9A79A2EC2C8FD000D1067 /* NSNumber+FAAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7992EC2C8F3000D1067 /* NSNumber+FAAdd.swift */; }; + 03E9A79C2EC31AD0000D1067 /* FAFeedbackCountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A79B2EC31AD0000D1067 /* FAFeedbackCountModel.swift */; }; B86XD3O90WO2R4725L084287 /* Pods_Fableon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C4THBP0A1283PFXW440071Q5 /* Pods_Fableon.framework */; }; F3019606DA7P36H41G408X13 /* ZStreamCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F38E33739F391364D0151P7Z /* ZStreamCell.swift */; }; F30470W590T8274E1642349G /* CControlCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32MR5F8X6Q3HSZ560BD0159 /* CControlCell.swift */; }; @@ -333,6 +348,38 @@ F3ZT3I4VAGB5405FWL36UW12 /* ZFGEtworkCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3342A2631Z4ZPCT74M73CK1 /* ZFGEtworkCell.swift */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 03E9A75C2EC19101000D1067 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F3LK276P33H73Y39H9X6VTDI /* Project object */; + proxyType = 1; + remoteGlobalIDString = 03E9A7562EC19101000D1067; + remoteInfo = NotificationService; + }; + 03E9A77C2EC19510000D1067 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F3LK276P33H73Y39H9X6VTDI /* Project object */; + proxyType = 1; + remoteGlobalIDString = 03E9A76C2EC1950F000D1067; + remoteInfo = FAWidgetExtension; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 03E9A7632EC19101000D1067 /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 03E9A75E2EC19101000D1067 /* NotificationService.appex in Embed Foundation Extensions */, + 03E9A77E2EC19510000D1067 /* FAWidgetExtension.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 031FDEAB2EAF05FB00F4CAC7 /* FAStoreVipCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreVipCell.swift; sourceTree = ""; }; 031FDEAD2EB093B100F4CAC7 /* FABuyRecordsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FABuyRecordsModel.swift; sourceTree = ""; }; @@ -552,6 +599,23 @@ 03E9A74D2EB5E0F7000D1067 /* FALanguageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FALanguageModel.swift; sourceTree = ""; }; 03E9A74F2EB5EAC0000D1067 /* FALanguageDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FALanguageDataModel.swift; sourceTree = ""; }; 03E9A7512EB83A58000D1067 /* FAAppStartViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAAppStartViewController.swift; sourceTree = ""; }; + 03E9A7572EC19101000D1067 /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 03E9A7642EC19110000D1067 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 03E9A7652EC19110000D1067 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; + 03E9A76D2EC1950F000D1067 /* FAWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = FAWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 03E9A76E2EC1950F000D1067 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; + 03E9A7702EC1950F000D1067 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; + 03E9A7832EC19516000D1067 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 03E9A7842EC19516000D1067 /* FAWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAWidget.swift; sourceTree = ""; }; + 03E9A7852EC19516000D1067 /* FAWidgetBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAWidgetBundle.swift; sourceTree = ""; }; + 03E9A7862EC19516000D1067 /* FAWidgetLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAWidgetLiveActivity.swift; sourceTree = ""; }; + 03E9A7872EC19516000D1067 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 03E9A78F2EC1B007000D1067 /* FAActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAActivityManager.swift; sourceTree = ""; }; + 03E9A7922EC1B0BC000D1067 /* FAWidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FAWidgetExtension.entitlements; sourceTree = ""; }; + 03E9A7932EC1B24E000D1067 /* UIImage+FAAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+FAAdd.swift"; sourceTree = ""; }; + 03E9A7962EC2C7DF000D1067 /* FAHomeNewTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAHomeNewTransformer.swift; sourceTree = ""; }; + 03E9A7992EC2C8F3000D1067 /* NSNumber+FAAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSNumber+FAAdd.swift"; sourceTree = ""; }; + 03E9A79B2EC31AD0000D1067 /* FAFeedbackCountModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAFeedbackCountModel.swift; sourceTree = ""; }; 19196I43BR665O55RD205171 /* Pods-Fableon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Fableon.debug.xcconfig"; path = "Target Support Files/Pods-Fableon/Pods-Fableon.debug.xcconfig"; sourceTree = ""; }; C4THBP0A1283PFXW440071Q5 /* Pods_Fableon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Fableon.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DCD59738B6J31K33W4Z524S0 /* Pods-Fableon.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Fableon.release.xcconfig"; path = "Target Support Files/Pods-Fableon/Pods-Fableon.release.xcconfig"; sourceTree = ""; }; @@ -663,6 +727,22 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 03E9A7542EC19101000D1067 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 03E9A76A2EC1950F000D1067 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 03E9A7712EC1950F000D1067 /* SwiftUI.framework in Frameworks */, + 03E9A76F2EC1950F000D1067 /* WidgetKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F37182X6857153T873504S9J /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -809,6 +889,8 @@ 03E239772EAA1A29004A8CEC /* UIStackView+FAAdd.swift */, 03E239782EAA1A29004A8CEC /* UIView+FAAdd.swift */, 03E239792EAA1A29004A8CEC /* UserDefaults+FAAdd.swift */, + 03E9A7932EC1B24E000D1067 /* UIImage+FAAdd.swift */, + 03E9A7992EC2C8F3000D1067 /* NSNumber+FAAdd.swift */, ); path = Extension; sourceTree = ""; @@ -897,6 +979,7 @@ 03E239BD2EAA1A4E004A8CEC /* FAHomeItem.swift */, 03E239BE2EAA1A4E004A8CEC /* FAHomeModuleItem.swift */, 03E239BF2EAA1A4E004A8CEC /* FAPopularModel.swift */, + 03E9A7962EC2C7DF000D1067 /* FAHomeNewTransformer.swift */, ); path = M; sourceTree = ""; @@ -988,6 +1071,7 @@ children = ( 03E239ED2EAA1A4E004A8CEC /* FAMeItemModel.swift */, 03E9A7412EB4A603000D1067 /* FAVersionUpdateModel.swift */, + 03E9A79B2EC31AD0000D1067 /* FAFeedbackCountModel.swift */, ); path = M; sourceTree = ""; @@ -1212,6 +1296,7 @@ 03E23A962EAA1A65004A8CEC /* Libs */ = { isa = PBXGroup; children = ( + 03E9A78E2EC1AFEA000D1067 /* ActivityManager */, 031FDEE22EB3487700F4CAC7 /* Alert */, 039CE6122EAB0DE1007B5EED /* FAIap */, 039CE60F2EAB0D2D007B5EED /* JXIAPManager */, @@ -1302,10 +1387,41 @@ path = WaterfallFlowLayout; sourceTree = ""; }; + 03E9A7662EC19110000D1067 /* NotificationService */ = { + isa = PBXGroup; + children = ( + 03E9A7642EC19110000D1067 /* Info.plist */, + 03E9A7652EC19110000D1067 /* NotificationService.swift */, + ); + path = NotificationService; + sourceTree = ""; + }; + 03E9A7882EC19516000D1067 /* FAWidget */ = { + isa = PBXGroup; + children = ( + 03E9A7832EC19516000D1067 /* Assets.xcassets */, + 03E9A7842EC19516000D1067 /* FAWidget.swift */, + 03E9A7852EC19516000D1067 /* FAWidgetBundle.swift */, + 03E9A7862EC19516000D1067 /* FAWidgetLiveActivity.swift */, + 03E9A7872EC19516000D1067 /* Info.plist */, + ); + path = FAWidget; + sourceTree = ""; + }; + 03E9A78E2EC1AFEA000D1067 /* ActivityManager */ = { + isa = PBXGroup; + children = ( + 03E9A78F2EC1B007000D1067 /* FAActivityManager.swift */, + ); + path = ActivityManager; + sourceTree = ""; + }; 612P1QES0606A2642109V515 /* Frameworks */ = { isa = PBXGroup; children = ( C4THBP0A1283PFXW440071Q5 /* Pods_Fableon.framework */, + 03E9A76E2EC1950F000D1067 /* WidgetKit.framework */, + 03E9A7702EC1950F000D1067 /* SwiftUI.framework */, ); name = Frameworks; sourceTree = ""; @@ -1343,7 +1459,10 @@ F31ABI705806054356280I22 = { isa = PBXGroup; children = ( + 03E9A7922EC1B0BC000D1067 /* FAWidgetExtension.entitlements */, F3YR32SS1P8731297KD665X5 /* Fableon */, + 03E9A7662EC19110000D1067 /* NotificationService */, + 03E9A7882EC19516000D1067 /* FAWidget */, F3B536I6734E05L319J6654P /* Products */, A9370738344P8616G77589I7 /* Pods */, 612P1QES0606A2642109V515 /* Frameworks */, @@ -1634,6 +1753,8 @@ isa = PBXGroup; children = ( F36676EN936M89B15P51177W /* Fableon.app */, + 03E9A7572EC19101000D1067 /* NotificationService.appex */, + 03E9A76D2EC1950F000D1067 /* FAWidgetExtension.appex */, ); name = Products; sourceTree = ""; @@ -1788,6 +1909,40 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 03E9A7562EC19101000D1067 /* NotificationService */ = { + isa = PBXNativeTarget; + buildConfigurationList = 03E9A7602EC19101000D1067 /* Build configuration list for PBXNativeTarget "NotificationService" */; + buildPhases = ( + 03E9A7532EC19101000D1067 /* Sources */, + 03E9A7542EC19101000D1067 /* Frameworks */, + 03E9A7552EC19101000D1067 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = NotificationService; + productName = NotificationService; + productReference = 03E9A7572EC19101000D1067 /* NotificationService.appex */; + productType = "com.apple.product-type.app-extension"; + }; + 03E9A76C2EC1950F000D1067 /* FAWidgetExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 03E9A7802EC19510000D1067 /* Build configuration list for PBXNativeTarget "FAWidgetExtension" */; + buildPhases = ( + 03E9A7692EC1950F000D1067 /* Sources */, + 03E9A76A2EC1950F000D1067 /* Frameworks */, + 03E9A76B2EC1950F000D1067 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FAWidgetExtension; + productName = FAWidgetExtension; + productReference = 03E9A76D2EC1950F000D1067 /* FAWidgetExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; F3F6B3Q1BI331E859340M109 /* Fableon */ = { isa = PBXNativeTarget; buildConfigurationList = F3511CV60KB1225F8K46932G /* Build configuration list for PBXNativeTarget "Fableon" */; @@ -1797,10 +1952,13 @@ F37182X6857153T873504S9J /* Frameworks */, F3T938414J234X46539JR019 /* Resources */, 4809W21R638Z15866LWB2041 /* [CP] Embed Pods Frameworks */, + 03E9A7632EC19101000D1067 /* Embed Foundation Extensions */, ); buildRules = ( ); dependencies = ( + 03E9A75D2EC19101000D1067 /* PBXTargetDependency */, + 03E9A77D2EC19510000D1067 /* PBXTargetDependency */, ); name = Fableon; productName = Fableon; @@ -1814,9 +1972,15 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1640; + LastSwiftUpdateCheck = 2600; LastUpgradeCheck = 1640; TargetAttributes = { + 03E9A7562EC19101000D1067 = { + CreatedOnToolsVersion = 26.0.1; + }; + 03E9A76C2EC1950F000D1067 = { + CreatedOnToolsVersion = 26.0.1; + }; F3F6B3Q1BI331E859340M109 = { CreatedOnToolsVersion = 16.4; LastSwiftMigration = 1640; @@ -1842,11 +2006,28 @@ projectRoot = ""; targets = ( F3F6B3Q1BI331E859340M109 /* Fableon */, + 03E9A7562EC19101000D1067 /* NotificationService */, + 03E9A76C2EC1950F000D1067 /* FAWidgetExtension */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 03E9A7552EC19101000D1067 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 03E9A76B2EC1950F000D1067 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 03E9A78C2EC19516000D1067 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F3T938414J234X46539JR019 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1937,12 +2118,32 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 03E9A7532EC19101000D1067 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 03E9A7682EC19110000D1067 /* NotificationService.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 03E9A7692EC1950F000D1067 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 03E9A7892EC19516000D1067 /* FAWidget.swift in Sources */, + 03E9A78A2EC19516000D1067 /* FAWidgetBundle.swift in Sources */, + 03E9A7912EC1B007000D1067 /* FAActivityManager.swift in Sources */, + 03E9A78B2EC19516000D1067 /* FAWidgetLiveActivity.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F3A707N33187J9XN3E985764 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( F39C9Y266RT4H0109L9T1807 /* LKVRecommendedHomeView.swift in Sources */, F37S2W333F5881I12F59EXJ0 /* KCFAlignment.swift in Sources */, + 03E9A79C2EC31AD0000D1067 /* FAFeedbackCountModel.swift in Sources */, F34Y5K0497T0B7D95Q1F4304 /* VRResult.swift in Sources */, F3Q1B8O8164TYQ9724T34V29 /* OACAbleView.swift in Sources */, F36MZ39RQ295569BV1915908 /* FOMptyView.swift in Sources */, @@ -1984,6 +2185,7 @@ F3362Y34741Z8AE0YP4S2Y54 /* YORecommended.swift in Sources */, 03E239632EAA1945004A8CEC /* AppDelegate+FAConfig.swift in Sources */, 03E239642EAA1945004A8CEC /* AppDelegate.swift in Sources */, + 03E9A7972EC2C7DF000D1067 /* FAHomeNewTransformer.swift in Sources */, 03E239652EAA1945004A8CEC /* SceneDelegate.swift in Sources */, 039CE6092EAA2F71007B5EED /* FAAdjustStateManager.swift in Sources */, F333U95746V7VK13QI9275B3 /* UMenuTransformerCell.swift in Sources */, @@ -2089,6 +2291,7 @@ 03E23A5C2EAA1A4E004A8CEC /* FAGenresListViewController.swift in Sources */, 03E23A5D2EAA1A4E004A8CEC /* FARecommendViewModel.swift in Sources */, 03E23A5E2EAA1A4E004A8CEC /* FAHomeMustSeeContentView.swift in Sources */, + 03E9A7902EC1B007000D1067 /* FAActivityManager.swift in Sources */, 03E23A5F2EAA1A4E004A8CEC /* FAEpSelectorCell.swift in Sources */, 031FDEEC2EB35DF600F4CAC7 /* FACoinsPackAlert.swift in Sources */, 03E23A602EAA1A4E004A8CEC /* FASearchResultView.swift in Sources */, @@ -2111,6 +2314,7 @@ 03E23A6E2EAA1A4E004A8CEC /* FARankingListCell.swift in Sources */, 031FDED22EB2F69200F4CAC7 /* FALoginView.swift in Sources */, 03E23A6F2EAA1A4E004A8CEC /* FAHomeBannerCell.swift in Sources */, + 03E9A79A2EC2C8FD000D1067 /* NSNumber+FAAdd.swift in Sources */, 03E23A702EAA1A4E004A8CEC /* FAGenresCell.swift in Sources */, 03E23A712EAA1A4E004A8CEC /* FAHomeSectionTitleView.swift in Sources */, 03E23A722EAA1A4E004A8CEC /* FASettingViewController.swift in Sources */, @@ -2206,6 +2410,7 @@ 03E239AF2EAA1A29004A8CEC /* FAAPI.swift in Sources */, 031FDECA2EB1F8F200F4CAC7 /* FACoinsPackReceiveModel.swift in Sources */, 03E239B02EAA1A29004A8CEC /* FAWebView.swift in Sources */, + 03E9A7942EC1B24E000D1067 /* UIImage+FAAdd.swift in Sources */, 039CE61C2EAB0F29007B5EED /* FAIapOrderModel.swift in Sources */, 03E9A7522EB83A58000D1067 /* FAAppStartViewController.swift in Sources */, 03E239B12EAA1A29004A8CEC /* FANetworkMonitor.swift in Sources */, @@ -2232,6 +2437,19 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 03E9A75D2EC19101000D1067 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 03E9A7562EC19101000D1067 /* NotificationService */; + targetProxy = 03E9A75C2EC19101000D1067 /* PBXContainerItemProxy */; + }; + 03E9A77D2EC19510000D1067 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 03E9A76C2EC1950F000D1067 /* FAWidgetExtension */; + targetProxy = 03E9A77C2EC19510000D1067 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ F3A0557617AU32W3L218F159 /* Localizable.strings */ = { isa = PBXVariantGroup; @@ -2252,6 +2470,132 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 03E9A7612EC19101000D1067 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 6XALB8RSYF; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = NotificationService/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = NotificationService; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.hn.qinjiu.fableon.NotificationService; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 03E9A7622EC19101000D1067 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 6XALB8RSYF; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = NotificationService/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = NotificationService; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.hn.qinjiu.fableon.NotificationService; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 03E9A7812EC19510000D1067 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CODE_SIGN_ENTITLEMENTS = FAWidgetExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 6XALB8RSYF; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = FAWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = FAWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.hn.qinjiu.fableon.FAWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 03E9A7822EC19510000D1067 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CODE_SIGN_ENTITLEMENTS = FAWidgetExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 6XALB8RSYF; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = FAWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = FAWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.hn.qinjiu.fableon.FAWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; F33W2S46100182427H2605Y0 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2391,6 +2735,7 @@ INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_NSCameraUsageDescription = "The APP needs to access your album to provide screenshots for feedback."; INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "The APP needs to access your album to provide screenshots for feedback."; + INFOPLIST_KEY_NSSupportsLiveActivities = YES; INFOPLIST_KEY_NSUserTrackingUsageDescription = "We will use your advertising identifier (IDFA) to provide a personalized advertising experience."; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; @@ -2402,7 +2747,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 1.0.4; PRODUCT_BUNDLE_IDENTIFIER = com.hn.qinjiu.fableon; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -2432,6 +2777,7 @@ INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_NSCameraUsageDescription = "The APP needs to access your album to provide screenshots for feedback."; INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "The APP needs to access your album to provide screenshots for feedback."; + INFOPLIST_KEY_NSSupportsLiveActivities = YES; INFOPLIST_KEY_NSUserTrackingUsageDescription = "We will use your advertising identifier (IDFA) to provide a personalized advertising experience."; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; @@ -2443,7 +2789,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 1.0.4; PRODUCT_BUNDLE_IDENTIFIER = com.hn.qinjiu.fableon; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -2458,6 +2804,24 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 03E9A7602EC19101000D1067 /* Build configuration list for PBXNativeTarget "NotificationService" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 03E9A7612EC19101000D1067 /* Debug */, + 03E9A7622EC19101000D1067 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 03E9A7802EC19510000D1067 /* Build configuration list for PBXNativeTarget "FAWidgetExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 03E9A7812EC19510000D1067 /* Debug */, + 03E9A7822EC19510000D1067 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; F3511CV60KB1225F8K46932G /* Build configuration list for PBXNativeTarget "Fableon" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Fableon/App/AppDelegate+FAAdjust.swift b/Fableon/App/AppDelegate+FAAdjust.swift index 38d1e0a..d5d878e 100644 --- a/Fableon/App/AppDelegate+FAAdjust.swift +++ b/Fableon/App/AppDelegate+FAAdjust.swift @@ -42,7 +42,8 @@ extension SceneDelegate { guard FANetworkMonitor.manager.isReachable == true, manager.isOpenApp, - manager.idfaAuthorizationFinish //idfa授权完成 + manager.idfaAuthorizationFinish, //idfa授权完成 + manager.upgradeAlertFinish else { if let webpageURL = webpageURL { manager.webpageURL = webpageURL diff --git a/Fableon/App/AppDelegate+FAApns.swift b/Fableon/App/AppDelegate+FAApns.swift index 24e06f7..bdf9648 100644 --- a/Fableon/App/AppDelegate+FAApns.swift +++ b/Fableon/App/AppDelegate+FAApns.swift @@ -22,16 +22,16 @@ extension SceneDelegate { center.requestAuthorization(options: [.badge, .sound, .alert]) { grant, error in if !grant { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { self.showApnsAlert() } + } else { + FAAdjustStateManager.manager.apnsFinish() + } - FAAdjustStateManager.manager.apnsAuthorizationFinish = true - - FATool.sceneDelegate?.retryHandleOpenAppMessage() - FATool.requestIDFAAuthorization(nil) + FAStatAPI.uploadApnsAuthorizationStatus() } UIApplication.shared.registerForRemoteNotifications() diff --git a/Fableon/App/SceneDelegate.swift b/Fableon/App/SceneDelegate.swift index 89f4e4e..940346b 100644 --- a/Fableon/App/SceneDelegate.swift +++ b/Fableon/App/SceneDelegate.swift @@ -8,6 +8,7 @@ import UIKit import YYText import MJRefresh +import Kingfisher class SceneDelegate: UIResponder, UIWindowSceneDelegate { @@ -62,6 +63,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func sceneDidEnterBackground(_ scene: UIScene) { FAAdjustStateManager.manager.allowOpenMessage = true + if #available(iOS 16.1, *) { + startWidgeLiveActivity() + } } @@ -123,10 +127,18 @@ extension SceneDelegate { } @objc private func networkStatusDidChangeNotification() { + let localizedData = FALanguageManager.manager.languageData ?? [:] + if FANetworkMonitor.manager.isReachable == true { + handleOnLine() FATool.requestIDFAAuthorization(nil) self.retryHandleOpenAppMessage() FAIapManager.manager.preloadingProducts() + + ///缺少语言数据,重新获取语言数据 + if localizedData.isEmpty { + self.startApp() + } } } @@ -134,4 +146,35 @@ extension SceneDelegate { MJRefreshConfig.default.languageCode = FALanguageManager.manager.mjLanguageKey setTabBarController() } + + @available(iOS 16.1, *) + private func startWidgeLiveActivity() { + + guard let model = FATool.widgetLiveActivityModel, let imageUrl = URL(string: model.image_url ?? "") else { return } + + KingfisherManager.shared.retrieveImage(with: imageUrl) { result in + switch result { + case .success(let imageResult): + let image = imageResult.image + + guard let data = image.compressImageSize(toByte: 9 * 1024) else { return } + guard let filePath = FAActivityManager.coverFileUrl else { return } + + do { + try data.write(to: filePath, options: .atomic) + + FAActivityManager.liveActivity(with: model.name ?? "", + videoId: model.short_play_id ?? "", + videoEpisode: model.current_episode ?? "") + } catch { + + } + + default: + break + } + } + + + } } diff --git a/Fableon/Fableon.entitlements b/Fableon/Fableon.entitlements index 56de1c5..39eb610 100644 --- a/Fableon/Fableon.entitlements +++ b/Fableon/Fableon.entitlements @@ -12,6 +12,11 @@ applinks:fableon.go.link applinks:kuzt.adj.st + applinks:www.hbqinjiu.com + + com.apple.security.application-groups + + group.com.hn.qinjiu.fableon keychain-access-groups diff --git a/Fableon/Object/Base/Controller/FANavigationController.swift b/Fableon/Object/Base/Controller/FANavigationController.swift index 46ecbe1..03982c2 100644 --- a/Fableon/Object/Base/Controller/FANavigationController.swift +++ b/Fableon/Object/Base/Controller/FANavigationController.swift @@ -12,6 +12,10 @@ class FANavigationController: UINavigationController { override func viewDidLoad() { super.viewDidLoad() + if #available(iOS 26.0, *) { + self.interactiveContentPopGestureRecognizer?.isEnabled = false + } + fd_fullscreenPopGestureRecognizer.isEnabled = true } diff --git a/Fableon/Object/Base/Extension/NSNumber+FAAdd.swift b/Fableon/Object/Base/Extension/NSNumber+FAAdd.swift new file mode 100644 index 0000000..122c1d2 --- /dev/null +++ b/Fableon/Object/Base/Extension/NSNumber+FAAdd.swift @@ -0,0 +1,25 @@ +// +// NSNumber+FAAdd.swift +// Fableon +// +// Created by 湖北秦九 on 2025/11/11. +// + +import UIKit + +extension NSNumber { + + func toString(maximumFractionDigits: Int = 10, minimumFractionDigits: Int? = nil, roundingMode: NumberFormatter.RoundingMode? = nil) -> String { + let formatter = NumberFormatter() + formatter.minimumIntegerDigits = 1 + formatter.maximumFractionDigits = maximumFractionDigits + if let minimumFractionDigits = minimumFractionDigits { + formatter.minimumFractionDigits = minimumFractionDigits + } + if let roundingMode = roundingMode { + formatter.roundingMode = roundingMode + } + formatter.numberStyle = .none + return formatter.string(from: self) ?? "0" + } +} diff --git a/Fableon/Object/Base/Extension/UIImage+FAAdd.swift b/Fableon/Object/Base/Extension/UIImage+FAAdd.swift new file mode 100644 index 0000000..bf43683 --- /dev/null +++ b/Fableon/Object/Base/Extension/UIImage+FAAdd.swift @@ -0,0 +1,68 @@ +// +// UIImage+FAAdd.swift +// Fableon +// +// Created by 湖北秦九 on 2025/11/10. +// + +import UIKit + +extension UIImage { + /// 根据图片大小计算压缩率 + static func getCompressRate(imageSize: CGFloat, targetSize: CGFloat) -> CGFloat { + var rate = Int(imageSize / targetSize) + rate = (rate == 0) ? 1 : rate + + // 默认压缩范围 + let maxCompressRate: CGFloat = 0.8 + let minCompressRate: CGFloat = 0.2 + + // 反比例压缩函数 + var compressRate = 0.8 / CGFloat(rate) + + // 限制范围 + compressRate = min(max(compressRate, minCompressRate), maxCompressRate) + return compressRate + } + + /// 压缩图片到指定字节大小(JPEG格式) + func compressImageSize(toByte maxLength: Int) -> Data? { + let image = self + + // 初次压缩(质量) + guard var data = image.jpegData(compressionQuality: 1.0) else { return nil } + if data.count < maxLength { + return data + } + + var compressRate = UIImage.getCompressRate(imageSize: CGFloat(data.count), targetSize: CGFloat(maxLength)) + data = image.jpegData(compressionQuality: compressRate) ?? data + if data.count < maxLength { + return data + } + + // 再次通过缩放降低体积 + var resultImage = UIImage(data: data) ?? image + var lastDataLength = 0 + + while data.count > maxLength && data.count != lastDataLength { + lastDataLength = data.count + let ratio = CGFloat(maxLength) / CGFloat(data.count) + let newWidth = max(Int(resultImage.size.width * sqrt(ratio)), 10) + let newHeight = max(Int(resultImage.size.height * sqrt(ratio)), 10) + let newSize = CGSize(width: newWidth, height: newHeight) + + if newSize.width < 10 || newSize.height < 10 { break } + + // 绘制新尺寸图片 + UIGraphicsBeginImageContext(newSize) + resultImage.draw(in: CGRect(origin: .zero, size: newSize)) + resultImage = UIGraphicsGetImageFromCurrentImageContext() ?? resultImage + UIGraphicsEndImageContext() + + data = resultImage.jpegData(compressionQuality: compressRate) ?? data + } + + return data + } +} diff --git a/Fableon/Object/Base/Extension/UIScrollView+FARefresh.swift b/Fableon/Object/Base/Extension/UIScrollView+FARefresh.swift index 2c03c5f..a784330 100644 --- a/Fableon/Object/Base/Extension/UIScrollView+FARefresh.swift +++ b/Fableon/Object/Base/Extension/UIScrollView+FARefresh.swift @@ -18,12 +18,11 @@ extension UIScrollView { } func fa_addRefreshFooter(insetBottom: CGFloat = 0, block: (() -> Void)?) { - let footer = MJRefreshAutoNormalFooter(refreshingBlock: { + self.mj_footer = MJRefreshBackNormalFooter(refreshingBlock: { block?() }) - footer.ignoredScrollViewContentInsetBottom = insetBottom - self.mj_footer = footer + self.mj_footer?.ignoredScrollViewContentInsetBottom = insetBottom } @@ -54,17 +53,17 @@ extension UIScrollView { } func fa_updateNoMoreDataState(_ hasNextPage: Bool?) { - if hasNextPage == false { - self.fa_endRefreshingWithNoMoreData() - } else { - self.fa_resetNoMoreData() - } - - if self.mj_totalDataCount() == 0 { - self.mj_footer?.isHidden = true - } else { - self.mj_footer?.isHidden = false - } +// if hasNextPage == false { +// self.fa_endRefreshingWithNoMoreData() +// } else { +// self.fa_resetNoMoreData() +// } +// +// if self.mj_totalDataCount() == 0 { +// self.mj_footer?.isHidden = true +// } else { +// self.mj_footer?.isHidden = false +// } } } diff --git a/Fableon/Object/Base/Request/FAAPI/FAAPI.swift b/Fableon/Object/Base/Request/FAAPI/FAAPI.swift index 71ffed0..8cd1083 100644 --- a/Fableon/Object/Base/Request/FAAPI/FAAPI.swift +++ b/Fableon/Object/Base/Request/FAAPI/FAAPI.swift @@ -65,6 +65,9 @@ struct FAAPI { ] FANetworkManager.manager.request(FABaseURL + "/myHistorys", method: .get, parameters: parameters, isToast: false) { (response: FANetworkManager.Response>) in + if page == 1, let model = response.data?.list?.first { + FATool.widgetLiveActivityModel = model + } completer?(response.data) } } @@ -103,6 +106,7 @@ struct FAAPI { parameters: parameters, isLoding: true) { (response: FANetworkManager.Response) in if response.isSuccess { + FAToast.show(text: "fableo_success".localized) success?() NotificationCenter.default.post(name: FAAPI.updateShortCollectStateNotification, object: nil, userInfo: [ "state" : isCollect, @@ -301,6 +305,19 @@ struct FAAPI { } } + ///获取反馈红点数量 + static func requestFeedbackRedCount(completer: ((_ model: FAFeedbackCountModel?) -> Void)?) { + + FANetworkManager.manager.request(FABaseURL + "/noticeNum", + method: .post, + parameters: nil, + isLoding: false, + isToast: true) { (response: FANetworkManager.Response) in + completer?(response.data) + } + + } + } diff --git a/Fableon/Object/Base/Request/FANetworkManager.swift b/Fableon/Object/Base/Request/FANetworkManager.swift index a0510a4..d65f2cb 100644 --- a/Fableon/Object/Base/Request/FANetworkManager.swift +++ b/Fableon/Object/Base/Request/FANetworkManager.swift @@ -70,6 +70,10 @@ class FANetworkManager { response.code = -1 completion?(response) } else { + if code == 402, isToast { + FAToast.show(text: "fableon_kick_out_login".localized) + } + self.requestUserToken { if FALogin.manager.token != nil { FALogin.manager.requestUserInfo(completer: nil) @@ -173,8 +177,10 @@ extension FANetworkManager { } private var headers: HTTPHeaders { + + let token = FALogin.manager.token?.token ?? "" - let dic = [ + var dic = [ "authorization" : token, "system-version" : UIDevice.current.systemVersion, "lang-key" : FALanguageManager.manager.currentLanguageKey, @@ -187,8 +193,11 @@ extension FANetworkManager { "idfa" : ASIdentifierManager.shared().advertisingIdentifier.uuidString, "device-id" : FADeviceIDManager.shared.id, //设备id "device-gaid" : UIDevice.current.identifierForVendor?.uuidString ?? "", - "product-prefix" : FAIapManager.IAPPrefix + "product-prefix" : FAIapManager.IAPPrefix, ] +#if DEBUG + dic["security"] = "false" +#endif return HTTPHeaders(dic) } } diff --git a/Fableon/Object/Base/WebView/FABaseWebViewController+Script.swift b/Fableon/Object/Base/WebView/FABaseWebViewController+Script.swift index f7d4150..8fce970 100644 --- a/Fableon/Object/Base/WebView/FABaseWebViewController+Script.swift +++ b/Fableon/Object/Base/WebView/FABaseWebViewController+Script.swift @@ -74,7 +74,7 @@ extension FABaseWebViewController { case kFAWebMessageAccountDeletionFinish: self.navigationController?.popToRootViewController(animated: true) - + NotificationCenter.default.post(name: FALogin.loginStatusChangeNotification, object: nil) default: break diff --git a/Fableon/Object/Class/Home/C/FAGenresListViewController.swift b/Fableon/Object/Class/Home/C/FAGenresListViewController.swift index 7d3457f..28c1fd3 100644 --- a/Fableon/Object/Class/Home/C/FAGenresListViewController.swift +++ b/Fableon/Object/Class/Home/C/FAGenresListViewController.swift @@ -30,12 +30,13 @@ class FAGenresListViewController: FAViewController { let collectionView = FACollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) collectionView.delegate = self collectionView.dataSource = self + collectionView.ly_emptyView = FAEmpty.fa_emptyView() collectionView.contentInset = .init(top: 20, left: 0, bottom: UIScreen.safeBottom + 10, right: 0) collectionView.register(UINib(nibName: "FAGenresListCell", bundle: nil), forCellWithReuseIdentifier: "cell") collectionView.fa_addRefreshHeader(insetTop: collectionView.contentInset.top) { [weak self] in self?.handleHeaderRefresh(nil) } - collectionView.fa_addRefreshFooter(insetBottom: 0) { [weak self] in + collectionView.fa_addRefreshFooter(insetBottom: collectionView.contentInset.bottom) { [weak self] in self?.handleFooterRefresh(nil) } return collectionView diff --git a/Fableon/Object/Class/Home/C/FAGenresViewController.swift b/Fableon/Object/Class/Home/C/FAGenresViewController.swift index 4b09cec..d9f8914 100644 --- a/Fableon/Object/Class/Home/C/FAGenresViewController.swift +++ b/Fableon/Object/Class/Home/C/FAGenresViewController.swift @@ -31,6 +31,7 @@ class FAGenresViewController: FAViewController { let collectionView = FACollectionView(frame: .zero, collectionViewLayout: self.collectionViewLayout) collectionView.delegate = self collectionView.dataSource = self + collectionView.ly_emptyView = FAEmpty.fa_emptyView() collectionView.contentInset = .init(top: 24, left: 0, bottom: UIScreen.safeBottom + 10, right: 0) collectionView.register(UINib(nibName: "FAGenresCell", bundle: nil), forCellWithReuseIdentifier: "cell") return collectionView @@ -38,7 +39,7 @@ class FAGenresViewController: FAViewController { override func viewDidLoad() { super.viewDidLoad() - self.title = "Genres".localized + self.title = "fableon_genres".localized fa_setupLayout() diff --git a/Fableon/Object/Class/Home/C/FAHomeViewController.swift b/Fableon/Object/Class/Home/C/FAHomeViewController.swift index 9370ec2..69be5a1 100644 --- a/Fableon/Object/Class/Home/C/FAHomeViewController.swift +++ b/Fableon/Object/Class/Home/C/FAHomeViewController.swift @@ -8,13 +8,21 @@ import UIKit import SwiftUI import SnapKit +import LYEmptyView class FAHomeViewController: FAViewController { private var viewModel = FAHomeViewModel() - + private lazy var notNetworkEmptyView: LYEmptyView = { + let view = FAEmpty.fa_notNetworkEmptyView { [weak self] in + self?.requestAllData(completer: nil) + self?.fa_setupLayout() + } + view.autoShowEmptyView = false + return view + }() private lazy var cvLayout: FAWaterfallFlowLayout = { let layout = FAWaterfallFlowLayout() @@ -80,6 +88,7 @@ class FAHomeViewController: FAViewController { override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(networkStatusDidChangeNotification), name: FANetworkMonitor.networkStatusDidChangeNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(loginStatusChangeNotification), name: FALogin.loginStatusChangeNotification, object: nil) fa_setupLayout() requestAllData(completer: nil) @@ -107,12 +116,27 @@ class FAHomeViewController: FAViewController { if self.viewModel.dataArr.isEmpty, FANetworkMonitor.manager.isReachable == true { requestAllData(completer: nil) } + self.fa_setupLayout() + } + + @objc private func loginStatusChangeNotification() { + requestAllData(completer: nil) } } extension FAHomeViewController { private func fa_setupLayout() { + if FANetworkMonitor.manager.isReachable == false, collectionView.superview == nil { + view.ly_emptyView = notNetworkEmptyView + view.ly_showEmpty() + return + } + if collectionView.superview != nil { + return + } + view.ly_hideEmpty() + view.addSubview(titleView) view.addSubview(searchButton) view.addSubview(collectionView) @@ -182,7 +206,7 @@ extension FAHomeViewController: UICollectionViewDelegate, UICollectionViewDataSo func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { if kind == UICollectionView.elementKindSectionHeader { - let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "FAHomeSectionTitleView", for: indexPath) + let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "FAHomeSectionTitleView", for: indexPath) as! FAHomeSectionTitleView return view } return collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "footer", for: indexPath) @@ -295,6 +319,8 @@ extension FAHomeViewController { if let playHistory = self.viewModel.playHistory { self.playHistoryView.model = playHistory self.playHistoryView.isHidden = false + } else { + self.playHistoryView.isHidden = true } } } diff --git a/Fableon/Object/Class/Home/C/FAPopularListViewController.swift b/Fableon/Object/Class/Home/C/FAPopularListViewController.swift index d3f27a9..d8c862b 100644 --- a/Fableon/Object/Class/Home/C/FAPopularListViewController.swift +++ b/Fableon/Object/Class/Home/C/FAPopularListViewController.swift @@ -29,6 +29,7 @@ class FAPopularListViewController: FAViewController { collectionView.delegate = self collectionView.dataSource = self collectionView.contentInset = .init(top: 20, left: 0, bottom: UIScreen.safeBottom + 10, right: 0) + collectionView.ly_emptyView = FAEmpty.fa_emptyView() collectionView.register(UINib(nibName: "FAGenresListCell", bundle: nil), forCellWithReuseIdentifier: "cell") collectionView.fa_addRefreshHeader(insetTop: collectionView.contentInset.top) { [weak self] in self?.handleHeaderRefresh(nil) diff --git a/Fableon/Object/Class/Home/C/FARankingListViewController.swift b/Fableon/Object/Class/Home/C/FARankingListViewController.swift index 50d960f..1a8d254 100644 --- a/Fableon/Object/Class/Home/C/FARankingListViewController.swift +++ b/Fableon/Object/Class/Home/C/FARankingListViewController.swift @@ -36,6 +36,7 @@ class FARankingListViewController: FAViewController { collectionView.dataSource = self collectionView.showsVerticalScrollIndicator = false collectionView.showsHorizontalScrollIndicator = false + collectionView.ly_emptyView = FAEmpty.fa_emptyView() collectionView.contentInset = .init(top: 15, left: 0, bottom: UIScreen.safeBottom + 10, right: 0) collectionView.register(UINib(nibName: "FARankingListCell", bundle: nil), forCellWithReuseIdentifier: "cell") return collectionView @@ -43,7 +44,7 @@ class FARankingListViewController: FAViewController { override func viewDidLoad() { super.viewDidLoad() - self.title = "Rankings".localized + self.title = "fableon_rabkings".localized fa_setupLayout() diff --git a/Fableon/Object/Class/Home/M/FACategoryModel.swift b/Fableon/Object/Class/Home/M/FACategoryModel.swift index 83ac296..17c4733 100644 --- a/Fableon/Object/Class/Home/M/FACategoryModel.swift +++ b/Fableon/Object/Class/Home/M/FACategoryModel.swift @@ -13,4 +13,10 @@ struct FACategoryModel: SmartCodable { var id: String? var category_name: String? var short_play_list: [FAShortPlayModel]? + + static func mappingForKey() -> [SmartKeyTransformer]? { + return [ + CodingKeys.category_name <--- ["category_name", "name"], + ] + } } diff --git a/Fableon/Object/Class/Home/M/FAHomeNewTransformer.swift b/Fableon/Object/Class/Home/M/FAHomeNewTransformer.swift new file mode 100644 index 0000000..512b894 --- /dev/null +++ b/Fableon/Object/Class/Home/M/FAHomeNewTransformer.swift @@ -0,0 +1,17 @@ +// +// FAHomeNewTransformer.swift +// Fableon +// +// Created by 湖北秦九 on 2025/11/11. +// + +import UIKit +import FSPagerView + +class FAHomeNewTransformer: FSPagerViewTransformer { + + + override func proposedInteritemSpacing() -> CGFloat { + return -50 + } +} diff --git a/Fableon/Object/Class/Home/UI/FAHomeMustSeeView.swift b/Fableon/Object/Class/Home/UI/FAHomeMustSeeView.swift index 259b9f1..473de9d 100644 --- a/Fableon/Object/Class/Home/UI/FAHomeMustSeeView.swift +++ b/Fableon/Object/Class/Home/UI/FAHomeMustSeeView.swift @@ -61,7 +61,7 @@ struct FAHomeMustSeeView: View { } HStack { - Text("selection".localized) + Text("fableon_selection".localized) .font(Font.font(size: 12, weight: .medium)) .foregroundStyle(textColor) .padding(.leading, 8) diff --git a/Fableon/Object/Class/Home/UI/FAHomeNewView.swift b/Fableon/Object/Class/Home/UI/FAHomeNewView.swift index c36809d..a4b81ac 100644 --- a/Fableon/Object/Class/Home/UI/FAHomeNewView.swift +++ b/Fableon/Object/Class/Home/UI/FAHomeNewView.swift @@ -14,7 +14,7 @@ struct FAHomeNewView: View { @ObservedObject var viewModel: FAHomeViewModel @State private var transformer: FSPagerViewTransformer = { - let transformer = FSPagerViewTransformer(type: .overlap) + let transformer = FAHomeNewTransformer(type: .overlap) transformer.minimumScale = 0.9 transformer.minimumAlpha = 1 return transformer diff --git a/Fableon/Object/Class/Home/V/FAGenresCell.swift b/Fableon/Object/Class/Home/V/FAGenresCell.swift index 7734823..4b5f65c 100644 --- a/Fableon/Object/Class/Home/V/FAGenresCell.swift +++ b/Fableon/Object/Class/Home/V/FAGenresCell.swift @@ -14,14 +14,22 @@ class FAGenresCell: UICollectionViewCell { var list = model?.short_play_list let firstModel = list?.removeFirst() - hotCountLabel.text = "\(firstModel?.watch_total ?? 0)" + let count = firstModel?.watch_total ?? 0 + var countStr = "\(count)" + if count > 1000 { + countStr = NSNumber(value: CGFloat(count) / 1000).toString(maximumFractionDigits: 1) + "k" + } + hotCountLabel.text = countStr + nameLabel.text = model?.category_name coverImageView.fa_setImage(firstModel?.image_url) list?.enumerated().forEach { let i = $0 - let imageView = smallImageViewArr[i] + let viewCount = smallImageViewArr.count + if i > viewCount { return } + let imageView = smallImageViewArr[viewCount - i - 1] imageView.fa_setImage($1.image_url) } } diff --git a/Fableon/Object/Class/Home/V/FAHomeBannerCell.swift b/Fableon/Object/Class/Home/V/FAHomeBannerCell.swift index f4c94ce..333ce0f 100644 --- a/Fableon/Object/Class/Home/V/FAHomeBannerCell.swift +++ b/Fableon/Object/Class/Home/V/FAHomeBannerCell.swift @@ -12,7 +12,11 @@ class FAHomeBannerCell: FSPagerViewCell { var model: FAShortPlayModel? { didSet { - coverImageView.fa_setImage(model?.horizontally_img) + if let image = model?.horizontally_img, image.count > 0 { + coverImageView.fa_setImage(image) + } else { + coverImageView.fa_setImage(model?.image_url) + } } } diff --git a/Fableon/Object/Class/Home/V/FAHomeBannerContentCell.swift b/Fableon/Object/Class/Home/V/FAHomeBannerContentCell.swift index 940f6c2..d8f6dd0 100644 --- a/Fableon/Object/Class/Home/V/FAHomeBannerContentCell.swift +++ b/Fableon/Object/Class/Home/V/FAHomeBannerContentCell.swift @@ -26,6 +26,7 @@ class FAHomeBannerContentCell: UICollectionViewCell { let view = FSPagerView() + view.automaticSlidingInterval = 5 view.itemSize = .init(width: 282, height: 146) view.transformer = transformer view.delegate = self diff --git a/Fableon/Object/Class/Home/V/FAHomeSectionTitleView.swift b/Fableon/Object/Class/Home/V/FAHomeSectionTitleView.swift index 570c361..1ad7001 100644 --- a/Fableon/Object/Class/Home/V/FAHomeSectionTitleView.swift +++ b/Fableon/Object/Class/Home/V/FAHomeSectionTitleView.swift @@ -9,9 +9,12 @@ import UIKit class FAHomeSectionTitleView: UICollectionReusableView { + + @IBOutlet weak var titleLabel: UILabel! + override func awakeFromNib() { super.awakeFromNib() - // Initialization code + self.titleLabel.text = "fableon_recommended_for_you".localized } } diff --git a/Fableon/Object/Class/Home/V/FAHomeSectionTitleView.xib b/Fableon/Object/Class/Home/V/FAHomeSectionTitleView.xib index 50796c9..c40d079 100644 --- a/Fableon/Object/Class/Home/V/FAHomeSectionTitleView.xib +++ b/Fableon/Object/Class/Home/V/FAHomeSectionTitleView.xib @@ -1,9 +1,9 @@ - + - + @@ -11,7 +11,7 @@ - + @@ -27,6 +27,9 @@ + + + diff --git a/Fableon/Object/Class/Home/V/FARankingListCell.swift b/Fableon/Object/Class/Home/V/FARankingListCell.swift index b215904..271e464 100644 --- a/Fableon/Object/Class/Home/V/FARankingListCell.swift +++ b/Fableon/Object/Class/Home/V/FARankingListCell.swift @@ -14,7 +14,14 @@ class FARankingListCell: UICollectionViewCell { coverImageView.fa_setImage(model?.image_url) nameLabel.text = model?.name epLabel.text = "fableon_episode_set".localizedReplace(text: "\(model?.episode_total ?? 0)") - countLabel.text = "\(model?.watch_total ?? 0)" + + + let count = model?.watch_total ?? 0 + var countStr = "\(count)" + if count > 1000 { + countStr = NSNumber(value: CGFloat(count) / 1000).toString(maximumFractionDigits: 1) + "k" + } + countLabel.text = countStr } } diff --git a/Fableon/Object/Class/Home/V/FASearchRecommendCell.swift b/Fableon/Object/Class/Home/V/FASearchRecommendCell.swift index 3070c93..a1585b6 100644 --- a/Fableon/Object/Class/Home/V/FASearchRecommendCell.swift +++ b/Fableon/Object/Class/Home/V/FASearchRecommendCell.swift @@ -14,8 +14,14 @@ class FASearchRecommendCell: UICollectionViewCell { didSet { coverImageView.fa_setImage(model?.image_url) titleLabel.text = model?.name - countLabel.text = "\(model?.watch_total ?? 0)" epLabel.text = "fableon_episode_set".localizedReplace(text: "\(model?.episode_total ?? 0)") + + let count = model?.watch_total ?? 0 + var countStr = "\(count)" + if count > 1000 { + countStr = NSNumber(value: CGFloat(count) / 1000).toString(maximumFractionDigits: 1) + "k" + } + countLabel.text = countStr } } diff --git a/Fableon/Object/Class/Home/V/FASearchResultCell.swift b/Fableon/Object/Class/Home/V/FASearchResultCell.swift index 69f8a5e..16a63af 100644 --- a/Fableon/Object/Class/Home/V/FASearchResultCell.swift +++ b/Fableon/Object/Class/Home/V/FASearchResultCell.swift @@ -19,7 +19,7 @@ class FASearchResultCell: UICollectionViewCell { titleLabel.text = model?.name coverImageView.fa_setImage(model?.image_url) - if let category = model?.category?.first, !category.isEmpty { + if let category = model?.categoryList?.first?.category_name, !category.isEmpty { categoryView.isHidden = false categoryLabel.text = category } else { diff --git a/Fableon/Object/Class/Home/VM/FAHomeViewModel.swift b/Fableon/Object/Class/Home/VM/FAHomeViewModel.swift index f536dfa..b9296c9 100644 --- a/Fableon/Object/Class/Home/VM/FAHomeViewModel.swift +++ b/Fableon/Object/Class/Home/VM/FAHomeViewModel.swift @@ -41,10 +41,10 @@ class FAHomeViewModel: ObservableObject { $0.title = "fableon_popular".localized popularItem = $0 } else if $0.module_key == .week_ranking { - $0.title = "Rankings".localized + $0.title = "fableon_rabkings".localized rankingsItem = $0 } else if $0.module_key == .cagetory_recommand, genresItem == nil { - $0.title = "Genres".localized + $0.title = "fableon_genres".localized genresItem = $0 } else if $0.module_key == .new_recommand { $0.title = "fableon_new".localized @@ -109,6 +109,8 @@ class FAHomeViewModel: ObservableObject { guard let self = self else { return } if let model = listModel?.list?.first { self.playHistory = model + } else { + self.playHistory = nil } completer?() } diff --git a/Fableon/Object/Class/Me/C/FACoinPackViewController.swift b/Fableon/Object/Class/Me/C/FACoinPackViewController.swift index 867f9bc..766055a 100644 --- a/Fableon/Object/Class/Me/C/FACoinPackViewController.swift +++ b/Fableon/Object/Class/Me/C/FACoinPackViewController.swift @@ -101,6 +101,8 @@ class FACoinPackViewController: FAViewController { return label }() + private weak var recommandView: UIView? + private lazy var tipTextLabel: UILabel = { let att = NSMutableAttributedString(string: "coins_pack_tips".localized) att.yy_lineSpacing = 3 @@ -210,7 +212,34 @@ extension FACoinPackViewController { FAStoreAPI.requestReceiveCoinsPackCoins(id: id) { [weak self] finish in guard let self = self else { return } self.requestCoinsPackData() + if finish { + self.showVideoRecommand() + } } } + ///展示视频推荐 + private func showVideoRecommand() { + guard self.recommandView == nil else { return } + + FAAPI.requestDetailRecommendVideo { [weak self] list in + guard let self = self else { return } + guard let list = list else { return } + guard self.recommandView == nil else { return } + + let view = FADetailRecommendView() + view.dataArr = list + view.didSelectedVideo = { [weak self] model in + guard let self = self else { return } + let vc = FAPlayerDetailViewController() + vc.shortPlayId = model.short_play_id + self.navigationController?.pushViewController(vc, animated: true) + } + view.show(in: FATool.keyWindow) + self.recommandView = view + } + + + } + } diff --git a/Fableon/Object/Class/Me/C/FAFeedbackViewController.swift b/Fableon/Object/Class/Me/C/FAFeedbackViewController.swift index b9cc413..bb9552c 100644 --- a/Fableon/Object/Class/Me/C/FAFeedbackViewController.swift +++ b/Fableon/Object/Class/Me/C/FAFeedbackViewController.swift @@ -8,12 +8,60 @@ import UIKit class FAFeedbackViewController: FAAppWebViewController { + + + private lazy var rightButton: UIButton = { + let button = UIButton(type: .custom) + button.setImage(UIImage(named: "Frame 2085663258"), for: .normal) + button.addTarget(self, action: #selector(handleRightBarButton), for: .touchUpInside) + return button + }() + + private lazy var redView: UIView = { + let view = UIView() + view.backgroundColor = .FF_313_B + view.layer.cornerRadius = 8 + view.isHidden = true + return view + }() + + private lazy var redLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .bold) + label.textColor = .FFFFFF + return label + }() override func viewDidLoad() { self.webUrl = kFAFeedBackHomeWebUrl super.viewDidLoad() - self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "Frame 2085663258"), style: .plain, target: self, action: #selector(handleRightBarButton)) +// self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "Frame 2085663258"), style: .plain, target: self, action: #selector(handleRightBarButton)) + + self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: rightButton) + + + rightButton.addSubview(redView) + redView.addSubview(redLabel) + + redView.snp.makeConstraints { make in + make.height.equalTo(16) + make.width.greaterThanOrEqualTo(16) + make.top.equalToSuperview().offset(-8) + make.right.equalToSuperview().offset(8) + } + + redLabel.snp.makeConstraints { make in + make.center.equalToSuperview() +// make.left.equalToSuperview().offset(5) + make.left.greaterThanOrEqualToSuperview().offset(3) + } + + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.requestRedCount() } @@ -23,7 +71,23 @@ class FAFeedbackViewController: FAAppWebViewController { vc.webUrl = kFAFeedBackListWebUrl self.navigationController?.pushViewController(vc, animated: true) - } } + +extension FAFeedbackViewController { + + private func requestRedCount() { + FAAPI.requestFeedbackRedCount { [weak self] model in + guard let self = self else { return } + if let count = model?.feedback_notice_num, count > 0 { + self.redView.isHidden = false + self.redLabel.text = "\(count)" + } else { + self.redView.isHidden = true + } + } + + } + +} diff --git a/Fableon/Object/Class/Me/C/FAMeListViewController.swift b/Fableon/Object/Class/Me/C/FAMeListViewController.swift index 410a2c4..3531f8c 100644 --- a/Fableon/Object/Class/Me/C/FAMeListViewController.swift +++ b/Fableon/Object/Class/Me/C/FAMeListViewController.swift @@ -12,22 +12,7 @@ class FAMeListViewController: FAViewController, JXPagingViewListViewDelegate { var scrollCallback: ((_ : UIScrollView) -> Void)? - private lazy var dataArr: [FAMeItemModel] = { - let arr = [ - FAMeItemModel(type: .language, name: "fableon_language".localized, icon: UIImage(named: "icon_language")), - FAMeItemModel(type: .feedback, name: "fableon_feedback".localized, icon: UIImage(named: "icon_feedback")), - FAMeItemModel(type: .about, name: "fableon_about_us".localized, icon: UIImage(named: "icon_about")), - - FAMeItemModel(type: .privacyPolicy, name: "fableon_privacy_policy".localized, icon: UIImage(named: "icon_privacy")), - FAMeItemModel(type: .userAgreement, name: "fableon_user_agreement".localized, icon: UIImage(named: "icon_user")), -// FAMeItemModel(type: .visitWebsite, name: "fableon_visit_website".localized, icon: UIImage(named: "icon_visit")), - - FAMeItemModel(type: .setting, name: "fableon_settings".localized, icon: UIImage(named: "icon_setting")) - ] - return arr - }() - - + private lazy var dataArr: [FAMeItemModel] = [] private lazy var tableViewHeaderView: FAMeTableViewHeaderView = { let view = FAMeTableViewHeaderView(frame: .init(x: 0, y: 0, width: UIScreen.width, height: 124)) @@ -49,7 +34,6 @@ class FAMeListViewController: FAViewController, JXPagingViewListViewDelegate { tableView.rowHeight = 56 tableView.separatorInset = .init(top: 0, left: 32, bottom: 0, right: 32) tableView.register(UINib(nibName: "FAMeCell", bundle: nil), forCellReuseIdentifier: "cell") -// tableView.addObserver(self, forKeyPath: "contentSize", context: nil) return tableView }() @@ -61,7 +45,7 @@ class FAMeListViewController: FAViewController, JXPagingViewListViewDelegate { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(userInfoUpdateNotification), name: FALogin.userInfoUpdateNotification, object: nil) - + setDataArr() self.bgView.isHidden = true fa_setupLayout() @@ -70,6 +54,7 @@ class FAMeListViewController: FAViewController, JXPagingViewListViewDelegate { @objc private func userInfoUpdateNotification() { tableViewHeaderView.userInfo = FALogin.manager.userInfo + setDataArr() } func listScrollView() -> UIScrollView { @@ -159,3 +144,27 @@ extension FAMeListViewController: UITableViewDelegate, UITableViewDataSource { } } +extension FAMeListViewController { + + private func setDataArr() { + + var arr = [ + FAMeItemModel(type: .language, name: "fableon_language".localized, icon: UIImage(named: "icon_language")), + FAMeItemModel(type: .feedback, name: "fableon_feedback".localized, icon: UIImage(named: "icon_feedback")), + FAMeItemModel(type: .about, name: "fableon_about_us".localized, icon: UIImage(named: "icon_about")), + + FAMeItemModel(type: .privacyPolicy, name: "fableon_privacy_policy".localized, icon: UIImage(named: "icon_privacy")), + FAMeItemModel(type: .userAgreement, name: "fableon_user_agreement".localized, icon: UIImage(named: "icon_user")), + ] + + if FALogin.manager.isLogin { + arr.append( + FAMeItemModel(type: .setting, name: "fableon_settings".localized, icon: UIImage(named: "icon_setting")) + ) + } + self.dataArr = arr + self.tableView.reloadData() + } + +} + diff --git a/Fableon/Object/Class/Me/M/FAFeedbackCountModel.swift b/Fableon/Object/Class/Me/M/FAFeedbackCountModel.swift new file mode 100644 index 0000000..29d3b75 --- /dev/null +++ b/Fableon/Object/Class/Me/M/FAFeedbackCountModel.swift @@ -0,0 +1,15 @@ +// +// FAFeedbackCountModel.swift +// Fableon +// +// Created by 湖北秦九 on 2025/11/11. +// + +import UIKit +import SmartCodable + +struct FAFeedbackCountModel: SmartCodable { + + var feedback_notice_num: Int? + +} diff --git a/Fableon/Object/Class/Me/V/FACoinPackHeaderView.swift b/Fableon/Object/Class/Me/V/FACoinPackHeaderView.swift index 97802fa..073c7d0 100644 --- a/Fableon/Object/Class/Me/V/FACoinPackHeaderView.swift +++ b/Fableon/Object/Class/Me/V/FACoinPackHeaderView.swift @@ -104,7 +104,7 @@ class FACoinPackHeaderView: UIView { .foregroundColor : UIColor.FFFFFF ])) - let countAtt = AttributedString(" \(0)".localized, attributes: AttributeContainer([ + let countAtt = AttributedString(" \(self.model?.receive_coins ?? 0)".localized, attributes: AttributeContainer([ .font : UIFont.font(ofSize: 14, weight: .bold), .foregroundColor : UIColor.FFFFFF ])) diff --git a/Fableon/Object/Class/Me/V/FACoinsPackClaimListCell.swift b/Fableon/Object/Class/Me/V/FACoinsPackClaimListCell.swift index 9051045..72a3221 100644 --- a/Fableon/Object/Class/Me/V/FACoinsPackClaimListCell.swift +++ b/Fableon/Object/Class/Me/V/FACoinsPackClaimListCell.swift @@ -24,7 +24,8 @@ class FACoinsPackClaimListCell: UICollectionViewCell { titleAtt.yy_color = .FFFFFF titleAtt.yy_font = .font(ofSize: 14, weight: .bold) - let dayAtt = NSMutableAttributedString(string: " (Day \(model?.day_text ?? ""))") + let day = "fableon_d".localized + let dayAtt = NSMutableAttributedString(string: " (\(day) \(model?.day_text ?? ""))") dayAtt.yy_color = ._20_A_1_FF dayAtt.yy_font = .font(ofSize: 14, weight: .regular) titleAtt.append(dayAtt) diff --git a/Fableon/Object/Class/Me/V/FAMeHeaderView.swift b/Fableon/Object/Class/Me/V/FAMeHeaderView.swift index edfdf26..f2cd5a1 100644 --- a/Fableon/Object/Class/Me/V/FAMeHeaderView.swift +++ b/Fableon/Object/Class/Me/V/FAMeHeaderView.swift @@ -97,6 +97,24 @@ class FAMeHeaderView: UIView { return button }() + private lazy var topUpButton: UIButton = { + let button = FAGradientButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + let vc = FAWalletViewController() + self.viewController?.navigationController?.pushViewController(vc, animated: true) + })) + button.fa_colors = [UIColor.BEDFFF.cgColor, UIColor._52_A_2_F_1.cgColor] + button.fa_locations = [0, 1] + button.fa_startPoint = .init(x: 0, y: 0.5) + button.fa_endPoint = .init(x: 1, y: 0.5) + button.layer.cornerRadius = 18 + button.layer.masksToBounds = true + button.setTitle("GO", for: .normal) + button.setTitleColor(._000000, for: .normal) + button.titleLabel?.font = .font(ofSize: 14, weight: .semibold) + return button + }() + override init(frame: CGRect) { super.init(frame: frame) @@ -118,6 +136,7 @@ extension FAMeHeaderView { addSubview(bonusCoinsView) addSubview(coinPackButton) addSubview(loginButton) + addSubview(topUpButton) avatarImageView.snp.makeConstraints { make in make.left.equalToSuperview().offset(16) @@ -159,6 +178,13 @@ extension FAMeHeaderView { make.width.equalTo(76) make.height.equalTo(28) } + + topUpButton.snp.makeConstraints { make in + make.centerY.equalTo(coinsView) + make.right.equalToSuperview().offset(-16) + make.width.equalTo(84) + make.height.equalTo(36) + } } } diff --git a/Fableon/Object/Class/Me/V/FAMeTableViewHeaderView.swift b/Fableon/Object/Class/Me/V/FAMeTableViewHeaderView.swift index 9123892..17a0899 100644 --- a/Fableon/Object/Class/Me/V/FAMeTableViewHeaderView.swift +++ b/Fableon/Object/Class/Me/V/FAMeTableViewHeaderView.swift @@ -11,18 +11,18 @@ class FAMeTableViewHeaderView: UIView { var userInfo: FAUserInfo? { didSet { + subtitleLabel.text = "vip_tip_01".localized if userInfo?.is_vip == true { subscribeView.isHidden = true expirationView.isHidden = false vipImageView.image = UIImage(named: "vip_image_02") titleLabel.text = "fableon_activated".localized - subtitleLabel.text = "vip_tip_01".localized } else { subscribeView.isHidden = false expirationView.isHidden = true vipImageView.image = UIImage(named: "vip_image_01") titleLabel.text = "fableon_not_activated".localized - subtitleLabel.text = "vip_tip_02".localized +// subtitleLabel.text = "vip_tip_02".localized } diff --git a/Fableon/Object/Class/Me/V/FASettingFooterView.swift b/Fableon/Object/Class/Me/V/FASettingFooterView.swift index b603748..134c404 100644 --- a/Fableon/Object/Class/Me/V/FASettingFooterView.swift +++ b/Fableon/Object/Class/Me/V/FASettingFooterView.swift @@ -49,7 +49,10 @@ class FASettingFooterView: UIView { let view = FALogoutAlert() view.clickHighlightButton = { - FALogin.manager.logout(completer: nil) + FALogin.manager.logout { [weak self] finish in + guard let self = self else { return } + self.viewController?.navigationController?.popToRootViewController(animated: true) + } } view.show() } diff --git a/Fableon/Object/Class/MyShort/C/FACollectViewController.swift b/Fableon/Object/Class/MyShort/C/FACollectViewController.swift index 659b38a..3109cff 100644 --- a/Fableon/Object/Class/MyShort/C/FACollectViewController.swift +++ b/Fableon/Object/Class/MyShort/C/FACollectViewController.swift @@ -21,6 +21,8 @@ class FACollectViewController: FAViewController { } } + private var needUpdate = false + private lazy var collectionViewLayout: UICollectionViewFlowLayout = { let width = floor((UIScreen.width - 16 - 32) / 3) @@ -67,11 +69,17 @@ class FACollectViewController: FAViewController { return item }() + + deinit { + NotificationCenter.default.removeObserver(self) + } + override func viewDidLoad() { super.viewDidLoad() self.edgesForExtendedLayout = .top self.title = "fableon_collect".localized - + NotificationCenter.default.addObserver(self, selector: #selector(loginStatusChangeNotification), name: FALogin.loginStatusChangeNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(updateShortCollectStateNotification), name: FAAPI.updateShortCollectStateNotification, object: nil) self.navigationItem.rightBarButtonItems = [historyButton, spaceButton, editBarButton] @@ -87,6 +95,14 @@ class FACollectViewController: FAViewController { self.navigationController?.setNavigationBarHidden(false, animated: true) } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + if needUpdate { + needUpdate = false + requestDataArr(page: 1, completer: nil) + } + } + override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) self.fa_isEditing = false @@ -138,6 +154,13 @@ class FACollectViewController: FAViewController { } } + @objc private func loginStatusChangeNotification() { + requestDataArr(page: 1, completer: nil) + } + + @objc private func updateShortCollectStateNotification() { + needUpdate = true + } } extension FACollectViewController { diff --git a/Fableon/Object/Class/MyShort/C/FAHistoryViewController.swift b/Fableon/Object/Class/MyShort/C/FAHistoryViewController.swift index cc5498b..7d86f24 100644 --- a/Fableon/Object/Class/MyShort/C/FAHistoryViewController.swift +++ b/Fableon/Object/Class/MyShort/C/FAHistoryViewController.swift @@ -31,7 +31,7 @@ class FAHistoryViewController: FAViewController { guard let self = self else { return } self.handleHeaderRefresh(nil) } - collectionView.fa_addRefreshFooter(insetBottom: 0) { [weak self] in + collectionView.fa_addRefreshFooter(insetBottom: collectionView.contentInset.bottom) { [weak self] in self?.handleFooterRefresh(nil) } return collectionView diff --git a/Fableon/Object/Class/Player/M/FAShortPlayModel.swift b/Fableon/Object/Class/Player/M/FAShortPlayModel.swift index db6af31..a95f02a 100644 --- a/Fableon/Object/Class/Player/M/FAShortPlayModel.swift +++ b/Fableon/Object/Class/Player/M/FAShortPlayModel.swift @@ -22,6 +22,7 @@ class FAShortPlayModel: NSObject, Identifiable, SmartCodable { var episode_total: Int? var horizontally_img: String? var category: [String]? + var categoryList: [FACategoryModel]? var short_play_id: String? var short_play_video_id: String? var video_info: FAVideoInfoModel? diff --git a/Fableon/Object/Class/Player/V/FADetailRecommendCell.swift b/Fableon/Object/Class/Player/V/FADetailRecommendCell.swift index 4eed2eb..00224c8 100644 --- a/Fableon/Object/Class/Player/V/FADetailRecommendCell.swift +++ b/Fableon/Object/Class/Player/V/FADetailRecommendCell.swift @@ -21,6 +21,7 @@ class FADetailRecommendCell: FSPagerViewCell { private lazy var player: JXPlayer = { let player = JXPlayer(controlView: nil) player.playerView = self.playerView + player.isLoop = true return player }() diff --git a/Fableon/Object/Class/Player/V/FADetailRecommendView.swift b/Fableon/Object/Class/Player/V/FADetailRecommendView.swift index e2f841c..a281cfe 100644 --- a/Fableon/Object/Class/Player/V/FADetailRecommendView.swift +++ b/Fableon/Object/Class/Player/V/FADetailRecommendView.swift @@ -16,6 +16,7 @@ class FADetailRecommendView: FABaseAlert { var dataArr: [FAShortPlayModel] = [] { didSet { self.pagerView.reloadData() + pageControl.numberOfPages = dataArr.count DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in self?.updateCurrentData() @@ -68,6 +69,15 @@ class FADetailRecommendView: FABaseAlert { return view }() + private lazy var pageControl: UIPageControl = { + let view = UIPageControl() + view.isUserInteractionEnabled = false + view.pageIndicatorTintColor = .FFFFFF.withAlphaComponent(0.6) + view.currentPageIndicatorTintColor = .FFFFFF + view.numberOfPages = 0 + return view + }() + override init(frame: CGRect) { super.init(frame: frame) contentWidth = UIScreen.width @@ -96,6 +106,7 @@ class FADetailRecommendView: FABaseAlert { guard let cell = self.pagerView.cellForItem(at: self.pagerView.currentIndex) as? FADetailRecommendCell else { return } self.currentCell = cell + self.pageControl.currentPage = self.pagerView.currentIndex // let model = cell.model // self.videoNameLabel.text = model?.name } @@ -109,6 +120,7 @@ extension FADetailRecommendView { contentView.addSubview(bgView) contentView.addSubview(cancelButton) bgView.addSubview(pagerView) + bgView.addSubview(pageControl) titleLabel.snp.makeConstraints { make in make.top.equalToSuperview() @@ -131,6 +143,11 @@ extension FADetailRecommendView { make.height.equalTo(267) make.center.equalToSuperview() } + + pageControl.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(pagerView.snp.bottom).offset(10) + } } } @@ -158,3 +175,13 @@ extension FADetailRecommendView: FSPagerViewDelegate, FSPagerViewDataSource { } } + +extension FADetailRecommendView { + + class PageControl: UIPageControl{ + override func size(forNumberOfPages pageCount: Int) -> CGSize { + return .init(width: 4, height: 4) + } + } + +} diff --git a/Fableon/Object/Class/Player/V/FAEpSelectorView.swift b/Fableon/Object/Class/Player/V/FAEpSelectorView.swift index 58f960a..752f479 100644 --- a/Fableon/Object/Class/Player/V/FAEpSelectorView.swift +++ b/Fableon/Object/Class/Player/V/FAEpSelectorView.swift @@ -192,9 +192,8 @@ extension FAEpSelectorView: UICollectionViewDelegate, UICollectionViewDataSource return } - self.didSelected?(indexPath.row) - self.dismiss(animated: true) { - + self.dismiss(animated: true) { [weak self] in + self?.didSelected?(indexPath.row) } } diff --git a/Fableon/Object/Class/Player/V/FAOldVideoRechargeView.swift b/Fableon/Object/Class/Player/V/FAOldVideoRechargeView.swift index da0ab27..31ae57e 100644 --- a/Fableon/Object/Class/Player/V/FAOldVideoRechargeView.swift +++ b/Fableon/Object/Class/Player/V/FAOldVideoRechargeView.swift @@ -71,7 +71,7 @@ class FAOldVideoRechargeView: FAPanModalContentView { private lazy var scrollView: FAScrollView = { let scrollView = FAScrollView() - scrollView.clipsToBounds = false +// scrollView.clipsToBounds = false return scrollView }() @@ -148,7 +148,7 @@ class FAOldVideoRechargeView: FAPanModalContentView { let label = UILabel() label.font = .font(ofSize: 12, weight: .medium) label.textColor = .FFFFFF - label.text = "store_tips_title".localized + label.text = "fableon_tips".localized return label }() diff --git a/Fableon/Object/Class/Player/V/FAPlayerDetailCell.swift b/Fableon/Object/Class/Player/V/FAPlayerDetailCell.swift index 903a2e1..695f529 100644 --- a/Fableon/Object/Class/Player/V/FAPlayerDetailCell.swift +++ b/Fableon/Object/Class/Player/V/FAPlayerDetailCell.swift @@ -32,6 +32,8 @@ class FAPlayerDetailCell: JXPlayerListCell { didSet { let controlView = self.controlView as? FAPlayerDetailControlView controlView?.shortModel = shortModel + + self.player.coverImageView?.fa_setImage(shortModel?.image_url) } } diff --git a/Fableon/Object/Class/Player/V/FAPlayerDetailControlView.swift b/Fableon/Object/Class/Player/V/FAPlayerDetailControlView.swift index ba84531..5b551db 100644 --- a/Fableon/Object/Class/Player/V/FAPlayerDetailControlView.swift +++ b/Fableon/Object/Class/Player/V/FAPlayerDetailControlView.swift @@ -58,6 +58,12 @@ class FAPlayerDetailControlView: JXPlayerListControlView { } } + override var isLoading: Bool { + didSet { + progressView.isLoading = isLoading + } + } + private lazy var epButton: UIHostingController = { let view = FAPlayerEpUIButton() let hc = UIHostingController(rootView: view) @@ -126,7 +132,15 @@ class FAPlayerDetailControlView: JXPlayerListControlView { let videoId = (self.model as? FAVideoInfoModel)?.short_play_video_id let isCollect = !(self.shortModel?.is_collect ?? false) - FAAPI.requestShortCollect(isCollect: isCollect, shortPlayId: shortPlayId, videoId: videoId, success: nil) + if isCollect { + FAAPI.requestShortCollect(isCollect: isCollect, shortPlayId: shortPlayId, videoId: videoId, success: nil) + } else { + let alert = FARemoveCollectAlert() + alert.clickHighlightButton = { + FAAPI.requestShortCollect(isCollect: isCollect, shortPlayId: shortPlayId, videoId: videoId, success: nil) + } + alert.show() + } }), for: .touchUpInside) diff --git a/Fableon/Object/Class/Player/V/FAPlayerProgressView.swift b/Fableon/Object/Class/Player/V/FAPlayerProgressView.swift index c3cb456..ef6a4e9 100644 --- a/Fableon/Object/Class/Player/V/FAPlayerProgressView.swift +++ b/Fableon/Object/Class/Player/V/FAPlayerProgressView.swift @@ -152,7 +152,7 @@ class FAPlayerProgressView: UIView { if self.isPaning { progress = self.panProgress } - + let currentProgressWidth = progressWidth * progress ///绘制进度 let progressPath = UIBezierPath(roundedRect: CGRect(x: progressX, y: progressY, width: progressWidth, height: lineWidth), cornerRadius: lineWidth / 2) @@ -165,6 +165,12 @@ class FAPlayerProgressView: UIView { context.addPath(currentPath.cgPath) context.setFillColor(currentProgress.cgColor) context.fillPath() + + ///绘制一个点 + let path = UIBezierPath(arcCenter: .init(x: currentProgressWidth + progressX, y: progressY + lineWidth / 2), radius: 3, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true) + context.addPath(path.cgPath) + context.setFillColor(currentProgress.cgColor) + context.fillPath() } diff --git a/Fableon/Object/Class/Player/VC/FAPlayerDetailViewController.swift b/Fableon/Object/Class/Player/VC/FAPlayerDetailViewController.swift index 370cdcb..ca7fb83 100644 --- a/Fableon/Object/Class/Player/VC/FAPlayerDetailViewController.swift +++ b/Fableon/Object/Class/Player/VC/FAPlayerDetailViewController.swift @@ -78,13 +78,13 @@ class FAPlayerDetailViewController: JXPlayerListViewController { self.viewModel.currentCell?.pause() } - override var previousVideoUrl: String? { - return self.fa_viewModel.previousEpisode?.video_url - } - - override var nextVideoUrl: String? { - return self.fa_viewModel.nextEpisode?.video_url - } +// override var previousVideoUrl: String? { +// return self.fa_viewModel.previousEpisode?.video_url +// } +// +// override var nextVideoUrl: String? { +// return self.fa_viewModel.nextEpisode?.video_url +// } override func play() { let videoInfo = self.viewModel.currentCell?.model as? FAVideoInfoModel @@ -92,6 +92,11 @@ class FAPlayerDetailViewController: JXPlayerListViewController { if videoInfo?.is_lock != true { super.play() FAAPI.requestCreatePlayHistory(videoId: videoInfo?.short_play_video_id, shortPlayId: videoInfo?.short_play_id) + + if let cell = viewModel.currentCell as? FAPlayerDetailCell, let model = cell.shortModel { + FATool.widgetLiveActivityModel = model + FATool.widgetLiveActivityModel?.current_episode = videoInfo?.episode + } return } self.pause() @@ -114,7 +119,7 @@ class FAPlayerDetailViewController: JXPlayerListViewController { @objc private func handleBackButton() { self.pause() - if !self.fa_viewModel.recommandDataArr.isEmpty { + if !self.fa_viewModel.recommandDataArr.isEmpty, self.fa_viewModel.isShowRecommand { let view = FADetailRecommendView() view.dataArr = self.fa_viewModel.recommandDataArr view.clickCloseButton = { [weak self] in @@ -126,6 +131,8 @@ class FAPlayerDetailViewController: JXPlayerListViewController { self.requestDetailList() } view.show(in: FATool.keyWindow) + } else { + self.handleNavigationBack() } // self.handleNavigationBack() diff --git a/Fableon/Object/Class/Player/VM/FAShortDetailViewModel.swift b/Fableon/Object/Class/Player/VM/FAShortDetailViewModel.swift index 78f8337..74fd689 100644 --- a/Fableon/Object/Class/Player/VM/FAShortDetailViewModel.swift +++ b/Fableon/Object/Class/Player/VM/FAShortDetailViewModel.swift @@ -7,6 +7,7 @@ import SwiftUI import JXPlayer +import YYText //@MainActor class FAShortDetailViewModel: JXPlayerListViewModel, ObservableObject { @@ -14,6 +15,9 @@ class FAShortDetailViewModel: JXPlayerListViewModel, ObservableObject { private(set) var dataArr: [FAShortDetailModel] = [] private(set) var recommandDataArr: [FAShortPlayModel] = [] + ///是否展示推荐数据 + private(set) var isShowRecommand = false + private var recommandTimer: Timer? var shortPlayId: String = "" @@ -43,6 +47,11 @@ class FAShortDetailViewModel: JXPlayerListViewModel, ObservableObject { func requestDetailData(indexPath: IndexPath? = nil, completer: ((_ code: Int) -> Void)?) { + isShowRecommand = false + recommandTimer?.invalidate() + recommandTimer = nil + recommandTimer = Timer.scheduledTimer(timeInterval: 6, target: YYTextWeakProxy(target: self), selector: #selector(handleRecommandTimer), userInfo: nil, repeats: false) + FAAPI.requestShortDetailData(shortPlayId: shortPlayId) { [weak self] model, code, msg in guard let self = self else { return } if let model = model { @@ -99,6 +108,18 @@ class FAShortDetailViewModel: JXPlayerListViewModel, ObservableObject { FAAPI.requestUploadPlayTime(shortPlayId: shortPlayId, videoId: videoId, seconds: Int(time) * 1000) } + override func playFinish(player: any JXPlayerCell) { + super.playFinish(player: player) + + let videoInfo = self.currentCell?.model as? FAVideoInfoModel + guard let shortPlayId = videoInfo?.short_play_id, let videoId = videoInfo?.short_play_video_id else { return } + FAAPI.requestUploadPlayTime(shortPlayId: shortPlayId, videoId: videoId, seconds: 0) + } + + @objc private func handleRecommandTimer() { + self.isShowRecommand = true + } + } extension FAShortDetailViewModel { @@ -119,6 +140,7 @@ extension FAShortDetailViewModel { func handleUnlockVideo() { unlockVideo { [weak self] finish in if finish { + FAToast.show(text: "fableo_success".localized) self?.playerListVC?.reloadData { self?.playerListVC?.play() } @@ -166,6 +188,10 @@ extension FAShortDetailViewModel { } private func _openOldRechargeView(_ model: FAPayDateModel, _ videoInfo: FAVideoInfoModel) { + FAStatAPI.requestEventStat(orderCode: nil, shortPlayId: videoInfo.short_play_id, videoId: videoInfo.short_play_video_id, eventKey: .payTemplateDialog, errorMsg: nil, otherParamenters: [ + "event_name" : "pay open" + ]) + let view = FAOldVideoRechargeView() view.model = model view.videoInfo = videoInfo diff --git a/Fableon/Object/Class/Recommend/C/FARecommendViewController.swift b/Fableon/Object/Class/Recommend/C/FARecommendViewController.swift index ed059bd..0baf1c4 100644 --- a/Fableon/Object/Class/Recommend/C/FARecommendViewController.swift +++ b/Fableon/Object/Class/Recommend/C/FARecommendViewController.swift @@ -7,6 +7,7 @@ import UIKit import JXPlayer +import LYEmptyView class FARecommendViewController: JXPlayerListViewController { @@ -21,16 +22,38 @@ class FARecommendViewController: JXPlayerListViewController { var fa_viewModel: FARecommendViewModel { return self.viewModel as! FARecommendViewModel } + + private lazy var notNetworkEmptyView: LYEmptyView = { + let view = FAEmpty.fa_notNetworkEmptyView { [weak self] in + self?.fa_viewModel.requestDataArr(page: 1) + self?.fa_setupLayout() + } + view.autoShowEmptyView = false + return view + }() + + deinit { + NotificationCenter.default.removeObserver(self) + } override func viewDidLoad() { super.viewDidLoad() + NotificationCenter.default.addObserver(self, selector: #selector(networkStatusDidChangeNotification), name: FANetworkMonitor.networkStatusDidChangeNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(loginStatusChangeNotification), name: FALogin.loginStatusChangeNotification, object: nil) self.register(FARecommendPlayerCell.self, forCellWithReuseIdentifier: "cell") + self.collectionView.fa_addRefreshHeader { [weak self] in + self?.fa_viewModel.requestDataArr(page: 1) { + self?.collectionView.fa_endHeaderRefreshing() + } + } + self.collectionView.isHidden = true self.delegate = self self.dataSource = self self.fa_viewModel.requestDataArr(page: 1) + fa_setupLayout() } override func viewWillAppear(_ animated: Bool) { @@ -43,12 +66,39 @@ class FARecommendViewController: JXPlayerListViewController { self.viewModel.currentCell?.pause() } - override var previousVideoUrl: String? { - return self.fa_viewModel.previousEpisode?.video_url +// override var previousVideoUrl: String? { +// return self.fa_viewModel.previousEpisode?.video_url +// } +// +// override var nextVideoUrl: String? { +// return self.fa_viewModel.nextEpisode?.video_url +// } + + @objc private func loginStatusChangeNotification() { + self.fa_viewModel.requestDataArr(page: 1) } - override var nextVideoUrl: String? { - return self.fa_viewModel.nextEpisode?.video_url + @objc private func networkStatusDidChangeNotification() { + if FANetworkMonitor.manager.isReachable == true, self.fa_viewModel.dataArr.isEmpty { + self.fa_viewModel.requestDataArr(page: 1) + } + fa_setupLayout() + } +} + +extension FARecommendViewController { + + private func fa_setupLayout() { + if FANetworkMonitor.manager.isReachable == false, self.collectionView.isHidden { + view.ly_emptyView = notNetworkEmptyView + view.ly_showEmpty() + return + } + if !self.collectionView.isHidden { + return + } + view.ly_hideEmpty() + self.collectionView.isHidden = false } } @@ -69,5 +119,7 @@ extension FARecommendViewController: JXPlayerListViewControllerDataSource { //MARK: JXPlayerListViewControllerDelegate extension FARecommendViewController: JXPlayerListViewControllerDelegate { - + func jx_playerViewControllerLoadMoreData(playerViewController: JXPlayerListViewController) { + self.fa_viewModel.requestDataArr(page: self.fa_viewModel.page + 1) + } } diff --git a/Fableon/Object/Class/Recommend/V/FARecommendPlayerCell.swift b/Fableon/Object/Class/Recommend/V/FARecommendPlayerCell.swift index 6db6ff5..086c526 100644 --- a/Fableon/Object/Class/Recommend/V/FARecommendPlayerCell.swift +++ b/Fableon/Object/Class/Recommend/V/FARecommendPlayerCell.swift @@ -21,6 +21,7 @@ class FARecommendPlayerCell: JXPlayerListCell { let videoInfo = model?.video_info self.player.setPlayUrl(url: videoInfo?.video_url ?? "") + self.player.coverImageView?.fa_setImage(model?.image_url) } } diff --git a/Fableon/Object/Class/Recommend/V/FARecommendPlayerControlView.swift b/Fableon/Object/Class/Recommend/V/FARecommendPlayerControlView.swift index 5a8084d..8e71af8 100644 --- a/Fableon/Object/Class/Recommend/V/FARecommendPlayerControlView.swift +++ b/Fableon/Object/Class/Recommend/V/FARecommendPlayerControlView.swift @@ -57,12 +57,12 @@ class FARecommendPlayerControlView: JXPlayerListControlView { } } - private lazy var epButton: UIHostingController = { - let view = FAPlayerEpUIButton() - let hc = UIHostingController(rootView: view) - hc.view.backgroundColor = .clear - return hc - }() +// private lazy var epButton: UIHostingController = { +// let view = FAPlayerEpUIButton() +// let hc = UIHostingController(rootView: view) +// hc.view.backgroundColor = .clear +// return hc +// }() private lazy var progressView: FAPlayerProgressView = { let view = FAPlayerProgressView() @@ -79,6 +79,9 @@ class FARecommendPlayerControlView: JXPlayerListControlView { label.font = .font(ofSize: 12, weight: .regular); label.textColor = UIColor(named: .color_FFFFFF)!.withAlphaComponent(0.8) label.numberOfLines = 2 + label.isUserInteractionEnabled = true + let tap = UITapGestureRecognizer(target: self, action: #selector(handleEp)) + label.addGestureRecognizer(tap) return label }() @@ -125,7 +128,15 @@ class FARecommendPlayerControlView: JXPlayerListControlView { let videoId = (self.model as? FAVideoInfoModel)?.short_play_video_id let isCollect = !(self.shortModel?.is_collect ?? false) - FAAPI.requestShortCollect(isCollect: isCollect, shortPlayId: shortPlayId, videoId: videoId, success: nil) + if isCollect { + FAAPI.requestShortCollect(isCollect: isCollect, shortPlayId: shortPlayId, videoId: videoId, success: nil) + } else { + let alert = FARemoveCollectAlert() + alert.clickHighlightButton = { + FAAPI.requestShortCollect(isCollect: isCollect, shortPlayId: shortPlayId, videoId: videoId, success: nil) + } + alert.show() + } }), for: .touchUpInside) @@ -154,17 +165,20 @@ class FARecommendPlayerControlView: JXPlayerListControlView { } private func updateEp() { -// let model = self.model as? FAVideoInfoModel - let model = self.model as? FAShortPlayModel - let videoInfo = model?.video_info - - - let text = "fableon_episode_set".localizedReplace(text: videoInfo?.episode ?? "") + "/" + "fableon_episode_set".localizedReplace(text: "\(model?.episode_total ?? 0)") - var view = FAPlayerEpUIButton(text: text) - view.clickHandle = { [weak self] in - self?.fa_viewModel?.pushPlayerDetail(self?.shortModel) - } - epButton.rootView = view +// let model = self.model as? FAShortPlayModel +// let videoInfo = model?.video_info +// +// +// let text = "fableon_episode_set".localizedReplace(text: videoInfo?.episode ?? "") + "/" + "fableon_episode_set".localizedReplace(text: "\(model?.episode_total ?? 0)") +// var view = FAPlayerEpUIButton(text: text) +// view.clickHandle = { [weak self] in +// self?.fa_viewModel?.pushPlayerDetail(self?.shortModel) +// } +// epButton.rootView = view + } + + @objc private func handleEp() { + self.fa_viewModel?.pushPlayerDetail(self.shortModel) } private func updateProgress() { @@ -191,7 +205,7 @@ extension FARecommendPlayerControlView { private func fa_setupLayout() { - addSubview(epButton.view) +// addSubview(epButton.view) addSubview(progressView) addSubview(textLabel) addSubview(shortNameLabel) @@ -199,16 +213,17 @@ extension FARecommendPlayerControlView { addSubview(collectButton) - epButton.view.snp.makeConstraints { make in - make.left.equalToSuperview().offset(16) - make.centerX.equalToSuperview() - make.bottom.equalToSuperview().offset(-10) - } +// epButton.view.snp.makeConstraints { make in +// make.left.equalToSuperview().offset(16) +// make.centerX.equalToSuperview() +// make.bottom.equalToSuperview().offset(-10) +// } progressView.snp.makeConstraints { make in make.left.equalToSuperview() make.centerX.equalToSuperview() - make.bottom.equalTo(epButton.view.snp.top).offset(-8) +// make.bottom.equalTo(epButton.view.snp.top).offset(-8) + make.bottom.equalToSuperview().offset(-1) } textLabel.snp.makeConstraints { make in diff --git a/Fableon/Object/Class/Recommend/VM/FARecommendViewModel.swift b/Fableon/Object/Class/Recommend/VM/FARecommendViewModel.swift index 29de1c8..78736a1 100644 --- a/Fableon/Object/Class/Recommend/VM/FARecommendViewModel.swift +++ b/Fableon/Object/Class/Recommend/VM/FARecommendViewModel.swift @@ -11,7 +11,7 @@ import JXPlayer class FARecommendViewModel: JXPlayerListViewModel { private(set) var dataArr: [FAShortPlayModel] = [] - + private(set) lazy var page = 1 var previousEpisode: FAVideoInfoModel? { @@ -65,6 +65,7 @@ extension FARecommendViewModel { FAAPI.requestRecommendVideo(page: page) { [weak self] listModel in guard let self = self else { return } if let listModel = listModel, let list = listModel.list { + self.page = page if page == 1 { self.playerListVC?.clearData() self.dataArr = list diff --git a/Fableon/Object/Class/Store/C/FACoinRecordViewController.swift b/Fableon/Object/Class/Store/C/FACoinRecordViewController.swift index 895c2d9..9a04899 100644 --- a/Fableon/Object/Class/Store/C/FACoinRecordViewController.swift +++ b/Fableon/Object/Class/Store/C/FACoinRecordViewController.swift @@ -25,7 +25,7 @@ class FACoinRecordViewController: FAViewController { tableView.fa_addRefreshHeader(insetTop: tableView.contentInset.top) { [weak self] in self?.handleHeaderRefresh(nil) } - tableView.fa_addRefreshFooter(insetBottom: 0) { [weak self] in + tableView.fa_addRefreshFooter(insetBottom: tableView.contentInset.bottom) { [weak self] in self?.handleFooterRefresh(nil) } return tableView @@ -65,11 +65,7 @@ extension FACoinRecordViewController: UITableViewDelegate, UITableViewDataSource func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let model = self.dataArr[indexPath.row] let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FAOrderRecordCell - if model.type == "send" { - cell.titleLabel.text = "fableo_bonus_coins".localized - } else { - cell.titleLabel.text = "fableo_recharge_coins".localized - } + cell.titleLabel.text = model.type cell.dateLabel.text = model.created_at cell.countLabel.text = "+\(model.value ?? "0")" return cell diff --git a/Fableon/Object/Class/Store/C/FAConsumptionRecordsViewController.swift b/Fableon/Object/Class/Store/C/FAConsumptionRecordsViewController.swift index 7bf4a35..1d19a82 100644 --- a/Fableon/Object/Class/Store/C/FAConsumptionRecordsViewController.swift +++ b/Fableon/Object/Class/Store/C/FAConsumptionRecordsViewController.swift @@ -25,7 +25,7 @@ class FAConsumptionRecordsViewController: FAViewController { tableView.fa_addRefreshHeader(insetTop: tableView.contentInset.top) { [weak self] in self?.handleHeaderRefresh(nil) } - tableView.fa_addRefreshFooter(insetBottom: 0) { [weak self] in + tableView.fa_addRefreshFooter(insetBottom: tableView.contentInset.bottom) { [weak self] in self?.handleFooterRefresh(nil) } return tableView diff --git a/Fableon/Object/Class/Store/C/FARewardCoinsViewController.swift b/Fableon/Object/Class/Store/C/FARewardCoinsViewController.swift index 89991fa..6edd377 100644 --- a/Fableon/Object/Class/Store/C/FARewardCoinsViewController.swift +++ b/Fableon/Object/Class/Store/C/FARewardCoinsViewController.swift @@ -25,7 +25,7 @@ class FARewardCoinsViewController: FAViewController { tableView.fa_addRefreshHeader(insetTop: tableView.contentInset.top) { [weak self] in self?.handleHeaderRefresh(nil) } - tableView.fa_addRefreshFooter(insetBottom: 0) { [weak self] in + tableView.fa_addRefreshFooter(insetBottom: tableView.contentInset.bottom) { [weak self] in self?.handleFooterRefresh(nil) } return tableView diff --git a/Fableon/Object/Class/Store/C/FAStoreViewController.swift b/Fableon/Object/Class/Store/C/FAStoreViewController.swift index cfa9e16..0227aca 100644 --- a/Fableon/Object/Class/Store/C/FAStoreViewController.swift +++ b/Fableon/Object/Class/Store/C/FAStoreViewController.swift @@ -73,7 +73,7 @@ class FAStoreViewController: FAViewController { let label = UILabel() label.font = .font(ofSize: 12, weight: .medium) label.textColor = .FFFFFF - label.text = "store_tips_title".localized + label.text = "fableon_tips".localized return label }() @@ -130,21 +130,21 @@ class FAStoreViewController: FAViewController { self.stackView.fa_removeAllArrangedSubview() guard let model = self.payDataModel else { return } - if model.pay_mode == 1 { - self.addCoinsView() - } else { - if let sort = model.sort, sort.count > 0 { - sort.forEach { - if $0 == .vip { - self.addVipView() - } else if $0 == .coin { - self.addCoinsView() - } +// if model.pay_mode == 1 { +// self.addCoinsView() +// } else { +// } + if let sort = model.sort, sort.count > 0 { + sort.forEach { + if $0 == .vip { + self.addVipView() + } else if $0 == .coin { + self.addCoinsView() } - } else { - self.addVipView() - self.addCoinsView() } + } else { + self.addVipView() + self.addCoinsView() } self.stackView.addArrangedSubview(tipView) } @@ -154,18 +154,18 @@ class FAStoreViewController: FAViewController { var newList: [FAPayItem] = [] - if model.show_type == 1, model.pay_mode == 1 { - if let list = model.list_coins, list.count > 0 { - list.forEach { - if $0.buy_type == .subCoins { - newList.append($0) - } - } - } - } else { - if let list = model.list_coins, list.count > 0 { - newList = list - } +// if model.show_type == 1, model.pay_mode == 1 { +// if let list = model.list_coins, list.count > 0 { +// list.forEach { +// if $0.buy_type == .subCoins { +// newList.append($0) +// } +// } +// } +// } else { +// } + if let list = model.list_coins, list.count > 0 { + newList = list } if newList.count > 0 { coinsView.setDataArr(newList) diff --git a/Fableon/Object/Class/Store/C/FAVipRecordViewController.swift b/Fableon/Object/Class/Store/C/FAVipRecordViewController.swift index d920d4b..42a7a8e 100644 --- a/Fableon/Object/Class/Store/C/FAVipRecordViewController.swift +++ b/Fableon/Object/Class/Store/C/FAVipRecordViewController.swift @@ -25,7 +25,7 @@ class FAVipRecordViewController: FAViewController { tableView.fa_addRefreshHeader(insetTop: tableView.contentInset.top) { [weak self] in self?.handleHeaderRefresh(nil) } - tableView.fa_addRefreshFooter(insetBottom: 0) { [weak self] in + tableView.fa_addRefreshFooter(insetBottom: tableView.contentInset.bottom) { [weak self] in self?.handleFooterRefresh(nil) } return tableView diff --git a/Fableon/Object/Class/Store/V/FACoinPackConfirmItem2View.swift b/Fableon/Object/Class/Store/V/FACoinPackConfirmItem2View.swift index fb3e96d..1abeb02 100644 --- a/Fableon/Object/Class/Store/V/FACoinPackConfirmItem2View.swift +++ b/Fableon/Object/Class/Store/V/FACoinPackConfirmItem2View.swift @@ -38,7 +38,7 @@ class FACoinPackConfirmItem2View: FACoinPackConfirmItemView { override init(frame: CGRect) { super.init(frame: frame) - self.titleLabel.text = "How Do I Receive Coins?" + self.titleLabel.text = "fableon_coin_bag_buy_tip_title".localized fa_setupLayout() } diff --git a/Fableon/Object/Class/Store/V/FAStoreCoinsPackCell.swift b/Fableon/Object/Class/Store/V/FAStoreCoinsPackCell.swift index d74654c..20a8965 100644 --- a/Fableon/Object/Class/Store/V/FAStoreCoinsPackCell.swift +++ b/Fableon/Object/Class/Store/V/FAStoreCoinsPackCell.swift @@ -13,7 +13,12 @@ class FAStoreCoinsPackCell: FAStoreCoinsCell { override var model: FAPayItem? { didSet { coinsLabel.text = "\(model?.ext_info?.max_total_coins ?? 0)" - ratioLabel.text = "\(model?.ext_info?.receive_coins_rate ?? "0%")" + if let text = model?.ext_info?.receive_coins_rate, !text.isEmpty { + ratioLabel.text = text + ratioBgView.isHidden = false + } else { + ratioBgView.isHidden = true + } priceView.setNeedsUpdateConfiguration() } @@ -124,9 +129,9 @@ class FAStoreCoinsPackCell: FAStoreCoinsCell { var subtitle = AttributedString("\(currency)\(oldPrice)", attributes: AttributeContainer([ .font : UIFont.font(ofSize: 12, weight: .regular), - .foregroundColor : UIColor._636_F_7_B.withAlphaComponent(0.2), - .strikethroughStyle: NSUnderlineStyle.single.rawValue, - .strikethroughColor: UIColor._636_F_7_B.withAlphaComponent(0.2) + .foregroundColor : UIColor._636_F_7_B.withAlphaComponent(0.05), + .strikethroughStyle: NSUnderlineStyle.double.rawValue, + .strikethroughColor: UIColor._636_F_7_B.withAlphaComponent(0.05) ])) button.configuration?.attributedSubtitle = subtitle diff --git a/Fableon/Object/Class/Store/V/FAStoreCoinsView.swift b/Fableon/Object/Class/Store/V/FAStoreCoinsView.swift index 46ea103..d7a909e 100644 --- a/Fableon/Object/Class/Store/V/FAStoreCoinsView.swift +++ b/Fableon/Object/Class/Store/V/FAStoreCoinsView.swift @@ -110,7 +110,9 @@ extension FAStoreCoinsView { addSubview(collectionView) collectionView.snp.makeConstraints { make in - make.edges.equalToSuperview() +// make.edges.equalToSuperview() + make.top.equalToSuperview().offset(8) + make.left.right.bottom.equalToSuperview() make.height.equalTo(1) } } @@ -190,6 +192,8 @@ extension FAStoreCoinsView: UICollectionViewDelegate, UICollectionViewDataSource if model.buy_type == .subCoins { let view = FACoinPackConfirmView() + view.shortPlayId = self.shortPlayId + view.videoId = self.videoId view.model = model view.buyFinishHandle = { [weak self] in guard let self = self else { return } diff --git a/Fableon/Object/Class/Store/V/FAStoreVipCell.swift b/Fableon/Object/Class/Store/V/FAStoreVipCell.swift index 9d30eb9..82776a9 100644 --- a/Fableon/Object/Class/Store/V/FAStoreVipCell.swift +++ b/Fableon/Object/Class/Store/V/FAStoreVipCell.swift @@ -12,7 +12,7 @@ class FAStoreVipCell: UICollectionViewCell { var item: FAPayItem? { didSet { - nameLabel.text = item?.title + nameLabel.text = item?.brief desLabel.text = item?.fa_description // coinTimeLabel.text = item?.auto_sub @@ -43,7 +43,7 @@ class FAStoreVipCell: UICollectionViewCell { let oldPriceStr = NSMutableAttributedString(string: oldPrice) oldPriceStr.yy_strikethroughColor = discountLabel.textColor - oldPriceStr.yy_strikethroughStyle = .single + oldPriceStr.yy_strikethroughStyle = .double discountLabel.attributedText = oldPriceStr discountLabel.isHidden = false @@ -156,7 +156,7 @@ class FAStoreVipCell: UICollectionViewCell { private lazy var discountLabel: UILabel = { let label = UILabel() label.font = .font(ofSize: 16, weight: .medium) - label.textColor = .FFFFFF.withAlphaComponent(0.4) + label.textColor = .FFFFFF.withAlphaComponent(0.03) return label }() diff --git a/Fableon/Object/Class/Store/V/FAStoreVipView.swift b/Fableon/Object/Class/Store/V/FAStoreVipView.swift index ae3e6b5..f5f23ca 100644 --- a/Fableon/Object/Class/Store/V/FAStoreVipView.swift +++ b/Fableon/Object/Class/Store/V/FAStoreVipView.swift @@ -20,16 +20,18 @@ class FAStoreVipView: UIView { var buyFinishHandle: (() -> Void)? + private lazy var collectionViewLayout: UICollectionViewCompositionalLayout = { let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))) let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(110)), subitems: [item]) - group.interItemSpacing = .fixed(14) let layoutSection = NSCollectionLayoutSection(group: group) + layoutSection.interGroupSpacing = 14 layoutSection.contentInsets = .init(top: 0, leading: 16, bottom: 0, trailing: 16) + let config = UICollectionViewCompositionalLayoutConfiguration() let layout = UICollectionViewCompositionalLayout(section: layoutSection) diff --git a/Fableon/Object/Libs/ActivityManager/FAActivityManager.swift b/Fableon/Object/Libs/ActivityManager/FAActivityManager.swift new file mode 100644 index 0000000..358acdc --- /dev/null +++ b/Fableon/Object/Libs/ActivityManager/FAActivityManager.swift @@ -0,0 +1,62 @@ +// +// FAActivityManager.swift +// Fableon +// +// Created by 湖北秦九 on 2025/11/10. +// + +import UIKit +import ActivityKit + +struct ActivityManagerAttributes: ActivityAttributes { + public struct ContentState: Codable, Hashable { + // Dynamic stateful properties about your activity go here! + var videoTitle: String + var videoCoverPath: String + var videoEpisode:String + var videoId: String + } + + // Fixed non-changing properties about your activity go here! + var name: String +} + +@available(iOS 16.1, *) +class FAActivityManager { + static var coverFileUrl: URL? { + let sharedContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.hn.qinjiu.fableon") + let fileURL = sharedContainerURL?.appendingPathComponent("cover.png") + return fileURL + } + + @objc public static func liveActivity(with videoTitle: String, videoId: String, videoEpisode: String) { + + let fileURL = self.coverFileUrl?.absoluteString ?? "" + + let contentState = ActivityManagerAttributes.ContentState(videoTitle: videoTitle, + videoCoverPath: fileURL, + videoEpisode: videoEpisode, + videoId:videoId) + + do { + if Activity.activities.count == 0 { + let attributes = ActivityManagerAttributes(name: "live activity") + let activity = try Activity.request(attributes: attributes, + contentState: contentState, + pushType: nil) + + print("Requested a pizza delivery Live Activity \(activity.id)") + } else { + Task { + for activity in Activity.activities { + await activity.update(using: contentState) + } + } + } + } catch (let error) { + print("Error requesting pizza delivery Live Activity \(error.localizedDescription)") + } + + + } +} diff --git a/Fableon/Object/Libs/AdjustStateManager/FAAdjustStateManager.swift b/Fableon/Object/Libs/AdjustStateManager/FAAdjustStateManager.swift index 2db0040..5dcd4d0 100644 --- a/Fableon/Object/Libs/AdjustStateManager/FAAdjustStateManager.swift +++ b/Fableon/Object/Libs/AdjustStateManager/FAAdjustStateManager.swift @@ -20,5 +20,14 @@ class FAAdjustStateManager { var idfaAuthorizationFinish = false - var apnsAuthorizationFinish = false + private(set) var apnsAuthorizationFinish = false + + var upgradeAlertFinish = false + + + func apnsFinish() { + self.apnsAuthorizationFinish = true + FATool.sceneDelegate?.retryHandleOpenAppMessage() + FATool.requestIDFAAuthorization(nil) + } } diff --git a/Fableon/Object/Libs/Alert/FAApnsAlert.swift b/Fableon/Object/Libs/Alert/FAApnsAlert.swift index f6273b0..b470c02 100644 --- a/Fableon/Object/Libs/Alert/FAApnsAlert.swift +++ b/Fableon/Object/Libs/Alert/FAApnsAlert.swift @@ -58,6 +58,12 @@ class FAApnsAlert: FABaseAlert { override func handleHighlightButton() { super.handleHighlightButton() FATool.openApnsSetting() + FAAdjustStateManager.manager.apnsFinish() + } + + override func dismiss() { + super.dismiss() + FAAdjustStateManager.manager.apnsFinish() } } diff --git a/Fableon/Object/Libs/Alert/FAUpdatesAlert.swift b/Fableon/Object/Libs/Alert/FAUpdatesAlert.swift index 550a685..07c9585 100644 --- a/Fableon/Object/Libs/Alert/FAUpdatesAlert.swift +++ b/Fableon/Object/Libs/Alert/FAUpdatesAlert.swift @@ -56,7 +56,7 @@ class FAUpdatesAlert: FABaseAlert { override init(frame: CGRect) { super.init(frame: frame) highlightButton.layer.cornerRadius = 18 - highlightButton.setTitle("fableon_update_now", for: .normal) + highlightButton.setTitle("fableon_update_now".localized, for: .normal) fa_setupLayout() } @@ -74,6 +74,12 @@ class FAUpdatesAlert: FABaseAlert { application.open(url) } } + + override func dismiss() { + super.dismiss() + FAAdjustStateManager.manager.upgradeAlertFinish = true + FATool.sceneDelegate?.retryHandleOpenAppMessage() + } } extension FAUpdatesAlert { diff --git a/Fableon/Object/Libs/Alert/FAVipRetainAlert.swift b/Fableon/Object/Libs/Alert/FAVipRetainAlert.swift index ac566c5..ae00ab6 100644 --- a/Fableon/Object/Libs/Alert/FAVipRetainAlert.swift +++ b/Fableon/Object/Libs/Alert/FAVipRetainAlert.swift @@ -132,6 +132,7 @@ extension FAVipRetainAlert { var payItem: FAPayItem? { didSet { nameLabel.text = payItem?.getVipTitle() + textLabel.text = "vip_tip_01".localized button.setNeedsUpdateConfiguration() } @@ -215,9 +216,9 @@ extension FAVipRetainAlert { button.configuration?.attributedSubtitle = AttributedString("\(currency)\(oldPrice)", attributes: AttributeContainer([ .font : UIFont.font(ofSize: 12, weight: .regular), - .foregroundColor : UIColor._000000.withAlphaComponent(0.5), - .strikethroughStyle: NSUnderlineStyle.single.rawValue, - .strikethroughColor: UIColor._000000.withAlphaComponent(0.5) + .foregroundColor : UIColor._000000.withAlphaComponent(0.05), + .strikethroughStyle: NSUnderlineStyle.double.rawValue, + .strikethroughColor: UIColor._000000.withAlphaComponent(0.05) ])) } else { @@ -239,7 +240,7 @@ extension FAVipRetainAlert { override init(frame: CGRect) { super.init(frame: frame) nameLabel.text = "Weekly VIP" - textLabel.text = "Unlimited access to all series" + addSubview(bgView) bgView.addSubview(bgIconImageView4) diff --git a/Fableon/Object/Libs/Empty/FAEmpty.swift b/Fableon/Object/Libs/Empty/FAEmpty.swift index f63c490..4696202 100644 --- a/Fableon/Object/Libs/Empty/FAEmpty.swift +++ b/Fableon/Object/Libs/Empty/FAEmpty.swift @@ -24,4 +24,33 @@ struct FAEmpty { return view! } + + static func fa_notNetworkEmptyView(clickBlock: (() -> Void)?) -> LYEmptyView { + + let view = LYEmptyView.emptyActionView(with: UIImage(named: "__startap"), titleStr: "No connection", detailStr: "Please check your network", btnTitleStr: "Try again") { + clickBlock?() + } + + view?.titleLabFont = .font(ofSize: 17, weight: .bold) + view?.titleLabTextColor = .FFFFFF + view?.detailLabFont = .font(ofSize: 15, weight: .regular) + view?.detailLabTextColor = .E_5_E_5_E_5 + + view?.actionBtnHeight = 32 + view?.actionBtnHorizontalMargin = 26 + view?.actionBtnCornerRadius = 16 + view?.actionBtnBorderWidth = 1 + view?.actionBtnBorderColor = .FFFFFF + view?.actionBtnBackGroundColor = .clear + view?.actionBtnTitleColor = .FFFFFF + view?.actionBtnFont = .font(ofSize: 14, weight: .medium) + + view?.contentViewOffset = -20 + view?.titleLabMargin = 30 + view?.detailLabMargin = 2 + view?.actionBtnMargin = 20 + return view! + + + } } diff --git a/Fableon/Object/Libs/FALogin/FALogin.swift b/Fableon/Object/Libs/FALogin/FALogin.swift index 23036de..9ba013f 100644 --- a/Fableon/Object/Libs/FALogin/FALogin.swift +++ b/Fableon/Object/Libs/FALogin/FALogin.swift @@ -77,6 +77,7 @@ class FALogin: NSObject { FAStatAPI.requestStatOnLine() completer?(true) NotificationCenter.default.post(name: FALogin.userInfoUpdateNotification, object: nil) + NotificationCenter.default.post(name: FALogin.loginStatusChangeNotification, object: nil) } } @@ -93,6 +94,7 @@ class FALogin: NSObject { FAStatAPI.requestStatOnLine() completer?(true) NotificationCenter.default.post(name: FALogin.userInfoUpdateNotification, object: nil) + NotificationCenter.default.post(name: FALogin.loginStatusChangeNotification, object: nil) } else { completer?(false) } @@ -129,5 +131,7 @@ extension FALogin { ///用户信息更新 @objc static let userInfoUpdateNotification = NSNotification.Name(rawValue: "FALogin.userInfoUpdateNotification") + ///登录状态发生变化 + @objc static let loginStatusChangeNotification = NSNotification.Name(rawValue: "FALogin.loginStatusChangeNotification") } diff --git a/Fableon/Object/Libs/FATool/FATool.swift b/Fableon/Object/Libs/FATool/FATool.swift index 58daa7f..14413c5 100644 --- a/Fableon/Object/Libs/FATool/FATool.swift +++ b/Fableon/Object/Libs/FATool/FATool.swift @@ -32,6 +32,9 @@ class FATool { return keyWindow?.rootViewController } + ///灵动岛数据 + static var widgetLiveActivityModel: FAShortPlayModel? + ///获得启动图 static func getLanuchViewController() -> UIViewController? { let storyboard = UIStoryboard(name: "LaunchScreen", bundle: nil) @@ -102,18 +105,31 @@ extension FATool { //MARK: 检查更新 extension FATool { + static weak var updateAlert: UIView? + static func checkUpdates() { + guard self.self.updateAlert == nil else { return } + FAAPI.requestVersionUpdateData { model in - guard let model = model else { return } + guard let model = model else { + FAAdjustStateManager.manager.upgradeAlertFinish = true + FATool.sceneDelegate?.retryHandleOpenAppMessage() + return + } self.showUpdatesAlert(model) } } static func showUpdatesAlert(_ model: FAVersionUpdateModel) { + guard self.self.updateAlert == nil else { return } #if !DEBUG if model.force != true { if let date = UserDefaults.standard.object(forKey: kFAVersionUpdateAlertDefaultsKey) as? Date { - if date.fa_isToday { return } + if date.fa_isToday { + FAAdjustStateManager.manager.upgradeAlertFinish = true + FATool.sceneDelegate?.retryHandleOpenAppMessage() + return + } } UserDefaults.standard.set(Date(), forKey: kFAVersionUpdateAlertDefaultsKey) } @@ -121,9 +137,17 @@ extension FATool { if model.canUpdate() { + FAStatAPI.requestEventStat(orderCode: nil, shortPlayId: nil, videoId: nil, eventKey: .forceUpdate, errorMsg: nil, otherParamenters: [ + "is_force" : model.force == true ? "true" : "false" + ]) + let view = FAUpdatesAlert() view.model = model view.show() + self.updateAlert = view + } else { + FAAdjustStateManager.manager.upgradeAlertFinish = true + FATool.sceneDelegate?.retryHandleOpenAppMessage() } } diff --git a/Fableon/Source/Assets.xcassets/color/#FF313B.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#FF313B.colorset/Contents.json new file mode 100644 index 0000000..6e3ce87 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#FF313B.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x3B", + "green" : "0x31", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/Thalire.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/Thalire.imageset/Contents.json index f48a40a..a3401c1 100644 --- a/Fableon/Source/Assets.xcassets/image/Thalire.imageset/Contents.json +++ b/Fableon/Source/Assets.xcassets/image/Thalire.imageset/Contents.json @@ -5,12 +5,12 @@ "scale" : "1x" }, { - "filename" : "Thalire@2x.png", + "filename" : "Fableon@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "Thalire@3x.png", + "filename" : "Fableon@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/Fableon/Source/Assets.xcassets/image/Thalire.imageset/Fableon@2x.png b/Fableon/Source/Assets.xcassets/image/Thalire.imageset/Fableon@2x.png new file mode 100644 index 0000000..c9c5486 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/Thalire.imageset/Fableon@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/Thalire.imageset/Fableon@3x.png b/Fableon/Source/Assets.xcassets/image/Thalire.imageset/Fableon@3x.png new file mode 100644 index 0000000..2391f2f Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/Thalire.imageset/Fableon@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/Thalire.imageset/Thalire@2x.png b/Fableon/Source/Assets.xcassets/image/Thalire.imageset/Thalire@2x.png deleted file mode 100644 index 7574e1e..0000000 Binary files a/Fableon/Source/Assets.xcassets/image/Thalire.imageset/Thalire@2x.png and /dev/null differ diff --git a/Fableon/Source/Assets.xcassets/image/Thalire.imageset/Thalire@3x.png b/Fableon/Source/Assets.xcassets/image/Thalire.imageset/Thalire@3x.png deleted file mode 100644 index 6b3c25c..0000000 Binary files a/Fableon/Source/Assets.xcassets/image/Thalire.imageset/Thalire@3x.png and /dev/null differ diff --git a/Fableon/Source/Assets.xcassets/image/__startap.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/__startap.imageset/Contents.json new file mode 100644 index 0000000..dbe0817 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/__startap.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "__startap@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "__startap@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/__startap.imageset/__startap@2x.png b/Fableon/Source/Assets.xcassets/image/__startap.imageset/__startap@2x.png new file mode 100644 index 0000000..ec526ca Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/__startap.imageset/__startap@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/__startap.imageset/__startap@3x.png b/Fableon/Source/Assets.xcassets/image/__startap.imageset/__startap@3x.png new file mode 100644 index 0000000..d82388d Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/__startap.imageset/__startap@3x.png differ diff --git a/Fableon/Source/Info.plist b/Fableon/Source/Info.plist index 7db30cc..0fa6392 100755 --- a/Fableon/Source/Info.plist +++ b/Fableon/Source/Info.plist @@ -46,5 +46,12 @@ + UIBackgroundModes + + fetch + remote-notification + + UIDesignRequiresCompatibility + diff --git a/Fableon/Source/en.lproj/Localizable.strings b/Fableon/Source/en.lproj/Localizable.strings index af56ca0..996fa73 100644 --- a/Fableon/Source/en.lproj/Localizable.strings +++ b/Fableon/Source/en.lproj/Localizable.strings @@ -91,6 +91,13 @@ "fableon_editor's_picks" = "Editor's Picks"; "fableon_home_new" = "New Releases"; "fableon_watch" = "Watch"; +"fableon_top_up" = "Top Up"; +"fableon_selection" = "selection"; +"fableon_coin_bag_buy_tip_title" = "How Do I Receive Coins?"; +"fableon_d" = "Day"; +"fableon_kick_out_login" = "Your account is already logged in on another device~"; +"fableon_rabkings" = "Rankings"; +"fableon_recommended_for_you" = "Recommended for you"; "remove_collect_alert_title" = "Remove from Favorites?"; "remove_collect_alert_text" = "This drama will be removed from your favorites."; @@ -107,6 +114,7 @@ "network_error_01" = "The service is abnormal. Check the network."; + "buy_fail_toast_01" = "Purchase failed, please try again later!"; "buy_fail_toast_02" = "The prequel to this series is not unlocked. Please unlock the prequel before unlocking this series"; @@ -130,4 +138,4 @@ "coins_pack_tips_title" = "Subscription Rules"; -"coins_pack_tips" = "1.Up to 2 subscriptions can be active at once.
2.Coins are delivered instantly upon purchase.
3.Daily bonus coins available from the next day.
4.All coins will be revoked when the subscription expires, including both initial and daily coins."; +"coins_pack_tips" = "1.Coins are delivered instantly upon purchase.
2.Daily bonus coins available from the next day.
3.All coins will be revoked when the subscription expires, including both initial and daily coins."; diff --git a/Fableon/firstpass/grey/preservesYoga/FFCheck.swift b/Fableon/firstpass/grey/preservesYoga/FFCheck.swift index cedcd6c..82af676 100644 --- a/Fableon/firstpass/grey/preservesYoga/FFCheck.swift +++ b/Fableon/firstpass/grey/preservesYoga/FFCheck.swift @@ -169,7 +169,7 @@ withUnsafeMutablePointer(to: &writeclearWatchdog) { pointer in $0.title = "fableon_popular".localized popularItem = $0 } else if $0.module_key == .week_ranking { - $0.title = "Rankings".localized + $0.title = "fableon_rabkings".localized rankingsItem = $0 } else if $0.module_key == .cagetory_recommand, genresItem == nil { $0.title = "veloriaDetailPath".localized diff --git a/Fableon/firstpass/grey/stationaritySumd/CSGleeView.swift b/Fableon/firstpass/grey/stationaritySumd/CSGleeView.swift index 1937fbc..ac6c617 100644 --- a/Fableon/firstpass/grey/stationaritySumd/CSGleeView.swift +++ b/Fableon/firstpass/grey/stationaritySumd/CSGleeView.swift @@ -74,7 +74,7 @@ var deceleratingMax: Double = 0.0 } HStack { - Text("selection".localized) + Text("fableon_selection".localized) .font(Font.font(size: 12, weight: .medium)) .foregroundStyle(textColor) .padding(.leading, 8) diff --git a/Fableon/firstpass/grey/zpbiquads/TCFableonCell.swift b/Fableon/firstpass/grey/zpbiquads/TCFableonCell.swift index e8316ff..80c7a67 100644 --- a/Fableon/firstpass/grey/zpbiquads/TCFableonCell.swift +++ b/Fableon/firstpass/grey/zpbiquads/TCFableonCell.swift @@ -16,8 +16,14 @@ var bottomProgressDict: [String: Any]? didSet { coverImageView.fa_setImage(model?.image_url) titleLabel.text = model?.name - countLabel.text = "\(model?.watch_total ?? 0)" epLabel.text = "fableon_episode_set".localizedReplace(text: "\(model?.episode_total ?? 0)") + + let count = model?.watch_total ?? 0 + var countStr = "\(count)" + if count > 1000 { + countStr = NSNumber(value: CGFloat(count) / 1000).toString(maximumFractionDigits: 1) + "k" + } + countLabel.text = countStr } } diff --git a/NotificationService/Info.plist b/NotificationService/Info.plist new file mode 100644 index 0000000..57421eb --- /dev/null +++ b/NotificationService/Info.plist @@ -0,0 +1,13 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.usernotifications.service + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).NotificationService + + + diff --git a/NotificationService/NotificationService.swift b/NotificationService/NotificationService.swift new file mode 100644 index 0000000..6a5fd19 --- /dev/null +++ b/NotificationService/NotificationService.swift @@ -0,0 +1,69 @@ +// +// NotificationService.swift +// NotificationService +// +// Created by 湖北秦九 on 2025/11/10. +// + +import UserNotifications + +class NotificationService: UNNotificationServiceExtension { + + var contentHandler: ((UNNotificationContent) -> Void)? + var bestAttemptContent: UNMutableNotificationContent? + + override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { + + self.contentHandler = contentHandler + bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) + + guard let bestAttemptContent = bestAttemptContent else { + contentHandler(request.content) + return + } + + if let options = request.content.userInfo["fcm_options"] as? [String : Any], + let imageURLString = options["image"] as? String, + let imageURL = URL(string: imageURLString) { + + downloadImage(from: imageURL) { attachment in + if let attachment = attachment { + bestAttemptContent.attachments = [attachment] + } + contentHandler(bestAttemptContent) + } + } else { + contentHandler(bestAttemptContent) + } + } + + override func serviceExtensionTimeWillExpire() { + // Called just before the extension will be terminated by the system. + // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. + if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { + contentHandler(bestAttemptContent) + } + } + + private func downloadImage(from url: URL, completion: @escaping (UNNotificationAttachment?) -> Void) { + let task = URLSession.shared.downloadTask(with: url) { (downloadedUrl, _, error) in + guard let downloadedUrl = downloadedUrl else { + completion(nil) + return + } + + let tmpDir = FileManager.default.temporaryDirectory + let tmpFile = tmpDir.appendingPathComponent(url.lastPathComponent) + + do { + try FileManager.default.moveItem(at: downloadedUrl, to: tmpFile) + let attachment = try UNNotificationAttachment(identifier: UUID().uuidString, url: tmpFile, options: nil) + completion(attachment) + } catch { + completion(nil) + } + } + task.resume() + } + +} diff --git a/Podfile b/Podfile index 06a7d0b..322e6be 100755 --- a/Podfile +++ b/Podfile @@ -20,7 +20,7 @@ target 'Fableon' do pod 'YYText' pod 'Alamofire' pod 'SmartCodable' - pod 'JXPlayer', '~> 0.1.5' + pod 'JXPlayer', '~> 0.1.8' pod 'Kingfisher' pod 'SnapKit' pod 'FSPagerView' diff --git a/Podfile.lock b/Podfile.lock index c7d7b9c..1fb1ef7 100755 --- a/Podfile.lock +++ b/Podfile.lock @@ -50,9 +50,9 @@ PODS: - IQTextView (1.0.5): - IQKeyboardToolbar/Placeholderable - JXPagingView/Paging (2.1.3) - - JXPlayer (0.1.5): - - SJMediaCacheServer - - SJVideoPlayer + - JXPlayer (0.1.8): + - SJMediaCacheServer (= 2.1.6) + - SJVideoPlayer (= 3.4.3) - JXSegmentedView (1.4.1) - Kingfisher (8.5.0) - LYEmptyView (1.3.1) @@ -86,10 +86,10 @@ PODS: - SJUIKit/ObserverHelper - SJUIKit/Queues - SJUIKit/SQLite3 - - SJMediaCacheServer (2.1.7): - - SJMediaCacheServer/Core (= 2.1.7) + - SJMediaCacheServer (2.1.6): + - SJMediaCacheServer/Core (= 2.1.6) - SJUIKit/SQLite3 - - SJMediaCacheServer/Core (2.1.7): + - SJMediaCacheServer/Core (2.1.6): - SJUIKit/SQLite3 - SJUIKit/AttributesFactory (0.0.0.59): - SJUIKit/AttributesFactory/Deprecated (= 0.0.0.59) @@ -148,7 +148,7 @@ DEPENDENCIES: - HWPanModal - IQKeyboardManagerSwift - JXPagingView/Paging - - JXPlayer (~> 0.1.5) + - JXPlayer (~> 0.1.8) - JXSegmentedView - Kingfisher - LYEmptyView @@ -215,14 +215,14 @@ SPEC CHECKSUMS: IQTextInputViewNotification: 3b9fb27a16e7ee8958cc9092cfb07a1a9e1fd559 IQTextView: ae13b4922f22e6f027f62c557d9f4f236b19d5c7 JXPagingView: afdd2e9af09c90160dd232b970d603cc6e7ddd0e - JXPlayer: 0e0cd9a2ee406ebb3051d00c760cd5fc599bf09b + JXPlayer: 8ca57987b506419cb51a8c84e6196c25043634ca JXSegmentedView: cd73555ce2134d1656db2cb383ba9c2f36fb5078 Kingfisher: ff0d31a1f07bdff6a1ebb3ba08b8e6e567b6500c LYEmptyView: b6d418cfa38b78df0cf243f9a9c25ccbdc399922 Masonry: 678fab65091a9290e40e2832a55e7ab731aad201 MJRefresh: ff9e531227924c84ce459338414550a05d2aea78 SJBaseVideoPlayer: b3122de12225b27b71bd9a8a1f08f4dcf2f4e5ec - SJMediaCacheServer: 0ca17fcdd5340fe1d2ad03110e511376f290d924 + SJMediaCacheServer: f6cd08ff32f5c6fc18ff06e676b42d6c17ce4cf1 SJUIKit: a40111941e408cc17d4c1c23495aa92999e814b0 SJVideoPlayer: 4f09814f58522e0975cb2dccfda925f6c8643467 SmartCodable: 545dd052990fe7e80085463b79a1a5e462ee29ff @@ -234,6 +234,6 @@ SPEC CHECKSUMS: YYText: 5c461d709e24d55a182d1441c41dc639a18a4849 ZLPhotoBrowser: d5928f08485c90a0b349a3a1e804e82c83ccf193 -PODFILE CHECKSUM: ee93369942c41a1babdca4fd27a29e21997fc357 +PODFILE CHECKSUM: 0d6c1bc4222cac60ce66cfe1a22456b0a9cc1685 COCOAPODS: 1.16.2 diff --git a/账号信息/银行卡信息.txt b/账号信息/银行卡信息.txt index 075035f..84605f4 100644 --- a/账号信息/银行卡信息.txt +++ b/账号信息/银行卡信息.txt @@ -1,3 +1,7 @@ + +hb.qinjiu@icloud.com +Discover2024 + 账户持有人: Hubei Qinjiu Network Technology Co., Ltd 银行账号: