1
This commit is contained in:
parent
010ea1cd70
commit
008d5b55b8
11
FAWidget/Assets.xcassets/AccentColor.colorset/Contents.json
Normal file
11
FAWidget/Assets.xcassets/AccentColor.colorset/Contents.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
35
FAWidget/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file
35
FAWidget/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
6
FAWidget/Assets.xcassets/Contents.json
Normal file
6
FAWidget/Assets.xcassets/Contents.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
84
FAWidget/FAWidget.swift
Normal file
84
FAWidget/FAWidget.swift
Normal file
@ -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<Entry>) -> ()) {
|
||||
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<Void> {
|
||||
// // 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: "🤩")
|
||||
//}
|
||||
17
FAWidget/FAWidgetBundle.swift
Normal file
17
FAWidget/FAWidgetBundle.swift
Normal file
@ -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()
|
||||
}
|
||||
}
|
||||
129
FAWidget/FAWidgetLiveActivity.swift
Normal file
129
FAWidget/FAWidgetLiveActivity.swift
Normal file
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
FAWidget/Info.plist
Normal file
11
FAWidget/Info.plist
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.widgetkit-extension</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
10
FAWidgetExtension.entitlements
Normal file
10
FAWidgetExtension.entitlements
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.hn.qinjiu.fableon</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -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 = "<group>"; };
|
||||
031FDEAD2EB093B100F4CAC7 /* FABuyRecordsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FABuyRecordsModel.swift; sourceTree = "<group>"; };
|
||||
@ -552,6 +599,23 @@
|
||||
03E9A74D2EB5E0F7000D1067 /* FALanguageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FALanguageModel.swift; sourceTree = "<group>"; };
|
||||
03E9A74F2EB5EAC0000D1067 /* FALanguageDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FALanguageDataModel.swift; sourceTree = "<group>"; };
|
||||
03E9A7512EB83A58000D1067 /* FAAppStartViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAAppStartViewController.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
03E9A7652EC19110000D1067 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
03E9A7842EC19516000D1067 /* FAWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAWidget.swift; sourceTree = "<group>"; };
|
||||
03E9A7852EC19516000D1067 /* FAWidgetBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAWidgetBundle.swift; sourceTree = "<group>"; };
|
||||
03E9A7862EC19516000D1067 /* FAWidgetLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAWidgetLiveActivity.swift; sourceTree = "<group>"; };
|
||||
03E9A7872EC19516000D1067 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
03E9A78F2EC1B007000D1067 /* FAActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAActivityManager.swift; sourceTree = "<group>"; };
|
||||
03E9A7922EC1B0BC000D1067 /* FAWidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FAWidgetExtension.entitlements; sourceTree = "<group>"; };
|
||||
03E9A7932EC1B24E000D1067 /* UIImage+FAAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+FAAdd.swift"; sourceTree = "<group>"; };
|
||||
03E9A7962EC2C7DF000D1067 /* FAHomeNewTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAHomeNewTransformer.swift; sourceTree = "<group>"; };
|
||||
03E9A7992EC2C8F3000D1067 /* NSNumber+FAAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSNumber+FAAdd.swift"; sourceTree = "<group>"; };
|
||||
03E9A79B2EC31AD0000D1067 /* FAFeedbackCountModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAFeedbackCountModel.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
@ -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 = "<group>";
|
||||
@ -897,6 +979,7 @@
|
||||
03E239BD2EAA1A4E004A8CEC /* FAHomeItem.swift */,
|
||||
03E239BE2EAA1A4E004A8CEC /* FAHomeModuleItem.swift */,
|
||||
03E239BF2EAA1A4E004A8CEC /* FAPopularModel.swift */,
|
||||
03E9A7962EC2C7DF000D1067 /* FAHomeNewTransformer.swift */,
|
||||
);
|
||||
path = M;
|
||||
sourceTree = "<group>";
|
||||
@ -988,6 +1071,7 @@
|
||||
children = (
|
||||
03E239ED2EAA1A4E004A8CEC /* FAMeItemModel.swift */,
|
||||
03E9A7412EB4A603000D1067 /* FAVersionUpdateModel.swift */,
|
||||
03E9A79B2EC31AD0000D1067 /* FAFeedbackCountModel.swift */,
|
||||
);
|
||||
path = M;
|
||||
sourceTree = "<group>";
|
||||
@ -1212,6 +1296,7 @@
|
||||
03E23A962EAA1A65004A8CEC /* Libs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
03E9A78E2EC1AFEA000D1067 /* ActivityManager */,
|
||||
031FDEE22EB3487700F4CAC7 /* Alert */,
|
||||
039CE6122EAB0DE1007B5EED /* FAIap */,
|
||||
039CE60F2EAB0D2D007B5EED /* JXIAPManager */,
|
||||
@ -1302,10 +1387,41 @@
|
||||
path = WaterfallFlowLayout;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
03E9A7662EC19110000D1067 /* NotificationService */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
03E9A7642EC19110000D1067 /* Info.plist */,
|
||||
03E9A7652EC19110000D1067 /* NotificationService.swift */,
|
||||
);
|
||||
path = NotificationService;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
03E9A7882EC19516000D1067 /* FAWidget */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
03E9A7832EC19516000D1067 /* Assets.xcassets */,
|
||||
03E9A7842EC19516000D1067 /* FAWidget.swift */,
|
||||
03E9A7852EC19516000D1067 /* FAWidgetBundle.swift */,
|
||||
03E9A7862EC19516000D1067 /* FAWidgetLiveActivity.swift */,
|
||||
03E9A7872EC19516000D1067 /* Info.plist */,
|
||||
);
|
||||
path = FAWidget;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
03E9A78E2EC1AFEA000D1067 /* ActivityManager */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
03E9A78F2EC1B007000D1067 /* FAActivityManager.swift */,
|
||||
);
|
||||
path = ActivityManager;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
612P1QES0606A2642109V515 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C4THBP0A1283PFXW440071Q5 /* Pods_Fableon.framework */,
|
||||
03E9A76E2EC1950F000D1067 /* WidgetKit.framework */,
|
||||
03E9A7702EC1950F000D1067 /* SwiftUI.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@ -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 = "<group>";
|
||||
@ -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 = (
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,11 @@
|
||||
<array>
|
||||
<string>applinks:fableon.go.link</string>
|
||||
<string>applinks:kuzt.adj.st</string>
|
||||
<string>applinks:www.hbqinjiu.com</string>
|
||||
</array>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.hn.qinjiu.fableon</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array/>
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
25
Fableon/Object/Base/Extension/NSNumber+FAAdd.swift
Normal file
25
Fableon/Object/Base/Extension/NSNumber+FAAdd.swift
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
68
Fableon/Object/Base/Extension/UIImage+FAAdd.swift
Normal file
68
Fableon/Object/Base/Extension/UIImage+FAAdd.swift
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,6 +65,9 @@ struct FAAPI {
|
||||
]
|
||||
|
||||
FANetworkManager.manager.request(FABaseURL + "/myHistorys", method: .get, parameters: parameters, isToast: false) { (response: FANetworkManager.Response<FANetworkManager.List<FAShortPlayModel>>) 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<FAShortDetailModel>) 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<FAFeedbackCountModel>) in
|
||||
completer?(response.data)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ extension FABaseWebViewController {
|
||||
|
||||
case kFAWebMessageAccountDeletionFinish:
|
||||
self.navigationController?.popToRootViewController(animated: true)
|
||||
|
||||
NotificationCenter.default.post(name: FALogin.loginStatusChangeNotification, object: nil)
|
||||
|
||||
default:
|
||||
break
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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"],
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
17
Fableon/Object/Class/Home/M/FAHomeNewTransformer.swift
Normal file
17
Fableon/Object/Class/Home/M/FAHomeNewTransformer.swift
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="24128" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24063"/>
|
||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
@ -11,7 +11,7 @@
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="U6b-Vx-4bR">
|
||||
<collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="U6b-Vx-4bR" customClass="FAHomeSectionTitleView" customModule="Fableon" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="555" height="123"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
@ -27,6 +27,9 @@
|
||||
<constraint firstItem="8Zn-D3-Qbm" firstAttribute="leading" secondItem="U6b-Vx-4bR" secondAttribute="leading" constant="16" id="EqE-jT-c4H"/>
|
||||
<constraint firstAttribute="top" secondItem="8Zn-D3-Qbm" secondAttribute="top" id="WgU-9Y-14J"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="titleLabel" destination="8Zn-D3-Qbm" id="ApY-aQ-yPU"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="318.32061068702291" y="66.549295774647888"/>
|
||||
</collectionReusableView>
|
||||
</objects>
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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?()
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
15
Fableon/Object/Class/Me/M/FAFeedbackCountModel.swift
Normal file
15
Fableon/Object/Class/Me/M/FAFeedbackCountModel.swift
Normal file
@ -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?
|
||||
|
||||
}
|
||||
@ -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
|
||||
]))
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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?
|
||||
|
||||
@ -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
|
||||
}()
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}()
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -58,6 +58,12 @@ class FAPlayerDetailControlView: JXPlayerListControlView {
|
||||
}
|
||||
}
|
||||
|
||||
override var isLoading: Bool {
|
||||
didSet {
|
||||
progressView.isLoading = isLoading
|
||||
}
|
||||
}
|
||||
|
||||
private lazy var epButton: UIHostingController<FAPlayerEpUIButton> = {
|
||||
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)
|
||||
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -57,12 +57,12 @@ class FARecommendPlayerControlView: JXPlayerListControlView {
|
||||
}
|
||||
}
|
||||
|
||||
private lazy var epButton: UIHostingController<FAPlayerEpUIButton> = {
|
||||
let view = FAPlayerEpUIButton()
|
||||
let hc = UIHostingController(rootView: view)
|
||||
hc.view.backgroundColor = .clear
|
||||
return hc
|
||||
}()
|
||||
// private lazy var epButton: UIHostingController<FAPlayerEpUIButton> = {
|
||||
// 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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 }
|
||||
|
||||
@ -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
|
||||
}()
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
62
Fableon/Object/Libs/ActivityManager/FAActivityManager.swift
Normal file
62
Fableon/Object/Libs/ActivityManager/FAActivityManager.swift
Normal file
@ -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<ActivityManagerAttributes>.activities.count == 0 {
|
||||
let attributes = ActivityManagerAttributes(name: "live activity")
|
||||
let activity = try Activity<ActivityManagerAttributes>.request(attributes: attributes,
|
||||
contentState: contentState,
|
||||
pushType: nil)
|
||||
|
||||
print("Requested a pizza delivery Live Activity \(activity.id)")
|
||||
} else {
|
||||
Task {
|
||||
for activity in Activity<ActivityManagerAttributes>.activities {
|
||||
await activity.update(using: contentState)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (let error) {
|
||||
print("Error requesting pizza delivery Live Activity \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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!
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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")
|
||||
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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"
|
||||
}
|
||||
|
||||
BIN
Fableon/Source/Assets.xcassets/image/Thalire.imageset/Fableon@2x.png
vendored
Normal file
BIN
Fableon/Source/Assets.xcassets/image/Thalire.imageset/Fableon@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
BIN
Fableon/Source/Assets.xcassets/image/Thalire.imageset/Fableon@3x.png
vendored
Normal file
BIN
Fableon/Source/Assets.xcassets/image/Thalire.imageset/Fableon@3x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.1 KiB |
22
Fableon/Source/Assets.xcassets/image/__startap.imageset/Contents.json
vendored
Normal file
22
Fableon/Source/Assets.xcassets/image/__startap.imageset/Contents.json
vendored
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
BIN
Fableon/Source/Assets.xcassets/image/__startap.imageset/__startap@2x.png
vendored
Normal file
BIN
Fableon/Source/Assets.xcassets/image/__startap.imageset/__startap@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
BIN
Fableon/Source/Assets.xcassets/image/__startap.imageset/__startap@3x.png
vendored
Normal file
BIN
Fableon/Source/Assets.xcassets/image/__startap.imageset/__startap@3x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
@ -46,5 +46,12 @@
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>fetch</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UIDesignRequiresCompatibility</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -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.<br>2.Coins are delivered instantly upon purchase.<br>3.Daily bonus coins available from the next day.<br>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.<br>2.Daily bonus coins available from the next day.<br>3.All coins will be revoked when the subscription expires, including both initial and daily coins.";
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
13
NotificationService/Info.plist
Normal file
13
NotificationService/Info.plist
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.usernotifications.service</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
69
NotificationService/NotificationService.swift
Normal file
69
NotificationService/NotificationService.swift
Normal file
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
2
Podfile
2
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'
|
||||
|
||||
20
Podfile.lock
20
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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user