feat: 최근 들은 콘텐츠 로컬 DB 추가
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23605" systemVersion="23G93" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithSwiftData="YES" userDefinedModelVersionIdentifier="">
|
||||||
|
<entity name="RecentContent" representedClassName=".RecentContent" syncable="YES">
|
||||||
|
<attribute name="contentId" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="coverImageUrl" optional="YES" attributeType="String"/>
|
||||||
|
<attribute name="creatorNickname" optional="YES" attributeType="String"/>
|
||||||
|
<attribute name="listenedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="title" optional="YES" attributeType="String"/>
|
||||||
|
</entity>
|
||||||
|
</model>
|
||||||
@@ -16,6 +16,7 @@ struct ContentDetailPlayView: View {
|
|||||||
@Binding var isShowPreviewAlert: Bool
|
@Binding var isShowPreviewAlert: Bool
|
||||||
|
|
||||||
@StateObject var contentPlayManager = ContentPlayManager.shared
|
@StateObject var contentPlayManager = ContentPlayManager.shared
|
||||||
|
@StateObject var recentContentViewModel = RecentContentViewModel()
|
||||||
|
|
||||||
@State private var isRepeat = UserDefaults.bool(forKey: .isContentPlayLoop)
|
@State private var isRepeat = UserDefaults.bool(forKey: .isContentPlayLoop)
|
||||||
@State private var isEditing = false
|
@State private var isEditing = false
|
||||||
@@ -101,6 +102,13 @@ struct ContentDetailPlayView: View {
|
|||||||
isPreview: !audioContent.existOrdered && audioContent.price > 0
|
isPreview: !audioContent.existOrdered && audioContent.price > 0
|
||||||
)
|
)
|
||||||
isShowPreviewAlert = true
|
isShowPreviewAlert = true
|
||||||
|
|
||||||
|
recentContentViewModel.insertRecentContent(
|
||||||
|
contentId: Int64(audioContent.contentId),
|
||||||
|
coverImageUrl: audioContent.coverImageUrl,
|
||||||
|
title: audioContent.title,
|
||||||
|
creatorNickname: audioContent.creator.nickname
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import MediaPlayer
|
|||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
import Kingfisher
|
import Kingfisher
|
||||||
|
import SwiftUICore
|
||||||
|
|
||||||
final class ContentPlayerPlayManager: NSObject, ObservableObject {
|
final class ContentPlayerPlayManager: NSObject, ObservableObject {
|
||||||
enum LoopState {
|
enum LoopState {
|
||||||
@@ -23,6 +24,8 @@ final class ContentPlayerPlayManager: NSObject, ObservableObject {
|
|||||||
|
|
||||||
private let repository = ContentGenerateUrlRepository()
|
private let repository = ContentGenerateUrlRepository()
|
||||||
|
|
||||||
|
@StateObject var recentContentViewModel = RecentContentViewModel()
|
||||||
|
|
||||||
@Published var id = 0
|
@Published var id = 0
|
||||||
@Published var title = ""
|
@Published var title = ""
|
||||||
@Published var nickname = ""
|
@Published var nickname = ""
|
||||||
@@ -132,6 +135,13 @@ final class ContentPlayerPlayManager: NSObject, ObservableObject {
|
|||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
self.fetchAlbumArtAndUpdateNowPlayingInfo()
|
self.fetchAlbumArtAndUpdateNowPlayingInfo()
|
||||||
|
|
||||||
|
recentContentViewModel.insertRecentContent(
|
||||||
|
contentId: Int64(id),
|
||||||
|
coverImageUrl: coverImageUrl,
|
||||||
|
title: title,
|
||||||
|
creatorNickname: nickname
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func checkPlaybackStart(bufferedTime: Double, isLikelyToKeepUp: Bool) {
|
private func checkPlaybackStart(bufferedTime: Double, isLikelyToKeepUp: Bool) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import RefreshableScrollView
|
|||||||
struct MyPageView: View {
|
struct MyPageView: View {
|
||||||
|
|
||||||
@StateObject var viewModel = MyPageViewModel()
|
@StateObject var viewModel = MyPageViewModel()
|
||||||
|
@StateObject var recentContentViewModel = RecentContentViewModel()
|
||||||
|
|
||||||
@State private var payload = Payload()
|
@State private var payload = Payload()
|
||||||
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
|
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
|
||||||
@@ -68,6 +69,7 @@ struct MyPageView: View {
|
|||||||
) {
|
) {
|
||||||
viewModel.getMypage()
|
viewModel.getMypage()
|
||||||
}
|
}
|
||||||
|
.padding(.horizontal, 24)
|
||||||
} else {
|
} else {
|
||||||
HStack {
|
HStack {
|
||||||
Text("LOGIN")
|
Text("LOGIN")
|
||||||
@@ -78,6 +80,7 @@ struct MyPageView: View {
|
|||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.background(Color.gray22)
|
.background(Color.gray22)
|
||||||
.cornerRadius(16)
|
.cornerRadius(16)
|
||||||
|
.padding(.horizontal, 24)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
AppState.shared
|
AppState.shared
|
||||||
.setAppStep(step: .login)
|
.setAppStep(step: .login)
|
||||||
@@ -91,6 +94,7 @@ struct MyPageView: View {
|
|||||||
token: token,
|
token: token,
|
||||||
refresh: { viewModel.getMypage() }
|
refresh: { viewModel.getMypage() }
|
||||||
)
|
)
|
||||||
|
.padding(.horizontal, 24)
|
||||||
|
|
||||||
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||||
// Category Buttons
|
// Category Buttons
|
||||||
@@ -105,23 +109,24 @@ struct MyPageView: View {
|
|||||||
viewModel.getMypage()
|
viewModel.getMypage()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
.padding(.horizontal, 24)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let url = URL(string: "https://blog.naver.com/sodalive_official"),
|
if let url = URL(string: "https://blog.naver.com/sodalive_official"),
|
||||||
UIApplication.shared.canOpenURL(url) {
|
UIApplication.shared.canOpenURL(url) {
|
||||||
// Voice On Banner
|
// Voice On Banner
|
||||||
Image("img_introduce_voiceon")
|
Image("img_introduce_voiceon")
|
||||||
|
.padding(.horizontal, 24)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
UIApplication.shared.open(url)
|
UIApplication.shared.open(url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && !viewModel.recentContentList.isEmpty {
|
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && !recentContentViewModel.recentContents.isEmpty {
|
||||||
// Recent 10 Section
|
// Recent 10 Section
|
||||||
RecentContentSection(recentContentList: viewModel.recentContentList)
|
RecentContentSection(recentContents: recentContentViewModel.recentContents)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 24)
|
|
||||||
.padding(.vertical, 32)
|
.padding(.vertical, 32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,6 +134,7 @@ struct MyPageView: View {
|
|||||||
.onAppear {
|
.onAppear {
|
||||||
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||||
viewModel.getMypage()
|
viewModel.getMypage()
|
||||||
|
recentContentViewModel.fetchRecentContents()
|
||||||
}
|
}
|
||||||
viewModel.getLatestNotice()
|
viewModel.getLatestNotice()
|
||||||
}
|
}
|
||||||
@@ -442,24 +448,25 @@ struct CategoryButtonItem: View {
|
|||||||
// MARK: - Recent 10 Content Section
|
// MARK: - Recent 10 Content Section
|
||||||
struct RecentContentSection: View {
|
struct RecentContentSection: View {
|
||||||
|
|
||||||
let recentContentList: [AudioContentMainItem]
|
let recentContents: [RecentContent]
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 14) {
|
VStack(alignment: .leading, spacing: 14) {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("최근 들은 ")
|
Text("최근 들은 ")
|
||||||
.font(.system(size: 16, weight: .bold))
|
.font(.custom(Font.preBold.rawValue, size: 16))
|
||||||
.foregroundColor(.white)
|
.foregroundColor(Color(hex: "B0BEC5"))
|
||||||
|
|
||||||
Text("\(recentContentList.count)")
|
Text("\(recentContents.count)")
|
||||||
.font(.system(size: 16, weight: .bold))
|
.font(.custom(Font.preBold.rawValue, size: 16))
|
||||||
.foregroundColor(.white)
|
.foregroundColor(Color(hex: "FDC118"))
|
||||||
}
|
}
|
||||||
|
.padding(.horizontal, 24)
|
||||||
|
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
HStack(spacing: 16) {
|
HStack(spacing: 16) {
|
||||||
ForEach(0..<recentContentList.count, id: \.self) { index in
|
ForEach(0..<recentContents.count, id: \.self) { index in
|
||||||
RecentItemView()
|
RecentItemView(content: recentContents[index])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 24)
|
.padding(.horizontal, 24)
|
||||||
@@ -469,67 +476,41 @@ struct RecentContentSection: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct RecentItemView: View {
|
struct RecentItemView: View {
|
||||||
|
|
||||||
|
let content: RecentContent
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 8) {
|
VStack(spacing: 8) {
|
||||||
// Thumbnail placeholder
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
RoundedRectangle(cornerRadius: 16)
|
KFImage(URL(string: content.coverImageUrl))
|
||||||
.fill(Color.gray)
|
.cancelOnDisappear(true)
|
||||||
.frame(width: 168, height: 168)
|
.resizable()
|
||||||
.overlay(
|
.scaledToFill()
|
||||||
VStack {
|
.frame(width: 160, height: 160, alignment: .top)
|
||||||
Spacer()
|
.cornerRadius(16)
|
||||||
HStack {
|
|
||||||
Spacer()
|
|
||||||
Text("00:12:33")
|
|
||||||
.font(.system(size: 12))
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.padding(.horizontal, 10)
|
|
||||||
.padding(.vertical, 3)
|
|
||||||
.background(Color.black.opacity(0.7))
|
|
||||||
.cornerRadius(39)
|
|
||||||
}
|
|
||||||
.padding(8)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.overlay(
|
|
||||||
VStack {
|
|
||||||
HStack {
|
|
||||||
Text("신작")
|
|
||||||
.font(.system(size: 12))
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.padding(.horizontal, 10)
|
|
||||||
.padding(.vertical, 3)
|
|
||||||
.background(LinearGradient(
|
|
||||||
gradient: Gradient(colors: [Color(hex: "0001B1"), Color(hex: "3B5FF1")]),
|
|
||||||
startPoint: .leading,
|
|
||||||
endPoint: .trailing
|
|
||||||
))
|
|
||||||
.cornerRadius(12)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Image(systemName: "shield.fill")
|
|
||||||
.foregroundColor(.red)
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
}
|
|
||||||
.padding(8)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
|
||||||
Text("우디(Woody)")
|
|
||||||
.font(.system(size: 18))
|
|
||||||
.foregroundColor(.white)
|
|
||||||
|
|
||||||
Text("우기라스")
|
Text(content.title)
|
||||||
.font(.system(size: 14))
|
.font(.custom(Font.preRegular.rawValue, size: 18))
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.multilineTextAlignment(.leading)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
.lineLimit(1)
|
||||||
|
.padding(.horizontal, 6)
|
||||||
|
.padding(.top, 8)
|
||||||
|
|
||||||
|
|
||||||
|
Text(content.creatorNickname)
|
||||||
|
.font(.custom(Font.preRegular.rawValue, size: 14))
|
||||||
.foregroundColor(Color(hex: "78909C"))
|
.foregroundColor(Color(hex: "78909C"))
|
||||||
|
.lineLimit(1)
|
||||||
|
.padding(.horizontal, 6)
|
||||||
|
.padding(.top, 4)
|
||||||
|
}
|
||||||
|
.frame(width: 160)
|
||||||
|
.onTapGesture {
|
||||||
|
AppState.shared.setAppStep(step: .contentDetail(contentId: Int(content.contentId)))
|
||||||
}
|
}
|
||||||
.padding(.leading, 6)
|
|
||||||
}
|
}
|
||||||
.frame(width: 168)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ final class MyPageViewModel: ObservableObject {
|
|||||||
@Published var rewardCan: Int = 0
|
@Published var rewardCan: Int = 0
|
||||||
@Published var point: Int = 0
|
@Published var point: Int = 0
|
||||||
@Published var isAuth: Bool = false
|
@Published var isAuth: Bool = false
|
||||||
@Published var recentContentList: [AudioContentMainItem] = []
|
|
||||||
|
|
||||||
@Published var errorMessage = ""
|
@Published var errorMessage = ""
|
||||||
@Published var isShowPopup = false
|
@Published var isShowPopup = false
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// PersistenceController.swift
|
||||||
|
// SodaLive
|
||||||
|
//
|
||||||
|
// Created by klaus on 7/28/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
struct PersistenceController {
|
||||||
|
static let shared = PersistenceController()
|
||||||
|
|
||||||
|
let container: NSPersistentContainer
|
||||||
|
|
||||||
|
init() {
|
||||||
|
container = NSPersistentContainer(name: "DataModel")
|
||||||
|
container.loadPersistentStores { description, error in
|
||||||
|
if let error = error {
|
||||||
|
fatalError("Error loading Core Data stores: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// RecentContent+CoreDataClass.swift
|
||||||
|
// SodaLive
|
||||||
|
//
|
||||||
|
// Created by klaus on 7/28/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
@objc(RecentContent)
|
||||||
|
public class RecentContent: NSManagedObject {
|
||||||
|
}
|
||||||
|
|
||||||
|
extension RecentContent {
|
||||||
|
@nonobjc public class func fetchRequest() -> NSFetchRequest<RecentContent> {
|
||||||
|
return NSFetchRequest<RecentContent>(entityName: "RecentContent")
|
||||||
|
}
|
||||||
|
|
||||||
|
@NSManaged public var contentId: Int64
|
||||||
|
@NSManaged public var coverImageUrl: String
|
||||||
|
@NSManaged public var title: String
|
||||||
|
@NSManaged public var creatorNickname: String
|
||||||
|
@NSManaged public var listenedAt: Date
|
||||||
|
}
|
||||||
123
SodaLive/Sources/MyPage/Recent/DB/RecentContentService.swift
Normal file
123
SodaLive/Sources/MyPage/Recent/DB/RecentContentService.swift
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
//
|
||||||
|
// RecentContentService.swift
|
||||||
|
// SodaLive
|
||||||
|
//
|
||||||
|
// Created by klaus on 7/28/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
class RecentContentService {
|
||||||
|
private let viewContext: NSManagedObjectContext
|
||||||
|
|
||||||
|
init(context: NSManagedObjectContext = PersistenceController.shared.container.viewContext) {
|
||||||
|
self.viewContext = context
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRecentContents(limit: Int = 10) -> [RecentContent] {
|
||||||
|
let request = RecentContent.fetchRequest()
|
||||||
|
request.sortDescriptors = [NSSortDescriptor(key: "listenedAt", ascending: false)]
|
||||||
|
request.fetchLimit = limit
|
||||||
|
|
||||||
|
do {
|
||||||
|
return try viewContext.fetch(request)
|
||||||
|
} catch {
|
||||||
|
print("Error fetching recent contents: \(error)")
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func insertRecentContent(contentId: Int64, coverImageUrl: String, title: String, creatorNickname: String) {
|
||||||
|
// Check if content already exists
|
||||||
|
if let existingContent = findContent(byId: contentId) {
|
||||||
|
// Update timestamp
|
||||||
|
existingContent.listenedAt = Date()
|
||||||
|
saveContext()
|
||||||
|
} else {
|
||||||
|
// Create new content
|
||||||
|
let newContent = RecentContent(context: viewContext)
|
||||||
|
newContent.contentId = contentId
|
||||||
|
newContent.coverImageUrl = coverImageUrl
|
||||||
|
newContent.title = title
|
||||||
|
newContent.creatorNickname = creatorNickname
|
||||||
|
newContent.listenedAt = Date()
|
||||||
|
saveContext()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep only most recent 10 items
|
||||||
|
keepMostRecent(limit: 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteByContentId(contentId: Int64) {
|
||||||
|
if let content = findContent(byId: contentId) {
|
||||||
|
viewContext.delete(content)
|
||||||
|
saveContext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCount() -> Int {
|
||||||
|
let request = RecentContent.fetchRequest()
|
||||||
|
|
||||||
|
do {
|
||||||
|
return try viewContext.count(for: request)
|
||||||
|
} catch {
|
||||||
|
print("Error counting recent contents: \(error)")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func truncate() {
|
||||||
|
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = RecentContent.fetchRequest()
|
||||||
|
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
|
||||||
|
|
||||||
|
do {
|
||||||
|
try viewContext.execute(deleteRequest)
|
||||||
|
saveContext()
|
||||||
|
} catch {
|
||||||
|
print("Error truncating recent contents: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func keepMostRecent(limit: Int) {
|
||||||
|
let count = getCount()
|
||||||
|
if count <= limit { return }
|
||||||
|
|
||||||
|
let request = RecentContent.fetchRequest()
|
||||||
|
request.sortDescriptors = [NSSortDescriptor(key: "listenedAt", ascending: false)]
|
||||||
|
|
||||||
|
do {
|
||||||
|
let allContents = try viewContext.fetch(request)
|
||||||
|
for i in limit..<allContents.count {
|
||||||
|
viewContext.delete(allContents[i])
|
||||||
|
}
|
||||||
|
saveContext()
|
||||||
|
} catch {
|
||||||
|
print("Error keeping most recent contents: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func findContent(byId contentId: Int64) -> RecentContent? {
|
||||||
|
let request = RecentContent.fetchRequest()
|
||||||
|
request.predicate = NSPredicate(format: "contentId == %lld", contentId)
|
||||||
|
request.fetchLimit = 1
|
||||||
|
|
||||||
|
do {
|
||||||
|
let results = try viewContext.fetch(request)
|
||||||
|
return results.first
|
||||||
|
} catch {
|
||||||
|
print("Error finding content by ID: \(error)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func saveContext() {
|
||||||
|
if viewContext.hasChanges {
|
||||||
|
do {
|
||||||
|
try viewContext.save()
|
||||||
|
} catch {
|
||||||
|
print("Error saving context: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
SodaLive/Sources/MyPage/Recent/RecentContentViewModel.swift
Normal file
55
SodaLive/Sources/MyPage/Recent/RecentContentViewModel.swift
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
//
|
||||||
|
// RecentContentViewModel.swift
|
||||||
|
// SodaLive
|
||||||
|
//
|
||||||
|
// Created by klaus on 7/28/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
class RecentContentViewModel: ObservableObject {
|
||||||
|
@Published var recentContents: [RecentContent] = []
|
||||||
|
@Published var contentCount: Int = 0
|
||||||
|
|
||||||
|
private let service: RecentContentService
|
||||||
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
init(service: RecentContentService = RecentContentService()) {
|
||||||
|
self.service = service
|
||||||
|
fetchRecentContents()
|
||||||
|
fetchContentCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchRecentContents(limit: Int = 10) {
|
||||||
|
self.recentContents = service.getRecentContents(limit: limit)
|
||||||
|
self.contentCount = recentContents.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchContentCount() {
|
||||||
|
self.contentCount = service.getCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
func insertRecentContent(contentId: Int64, coverImageUrl: String, title: String, creatorNickname: String) {
|
||||||
|
service.insertRecentContent(
|
||||||
|
contentId: contentId,
|
||||||
|
coverImageUrl: coverImageUrl,
|
||||||
|
title: title,
|
||||||
|
creatorNickname: creatorNickname
|
||||||
|
)
|
||||||
|
fetchRecentContents()
|
||||||
|
fetchContentCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteByContentId(contentId: Int64) {
|
||||||
|
service.deleteByContentId(contentId: contentId)
|
||||||
|
fetchRecentContents()
|
||||||
|
fetchContentCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
func truncate() {
|
||||||
|
service.truncate()
|
||||||
|
fetchRecentContents()
|
||||||
|
fetchContentCount()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ struct SettingsView: View {
|
|||||||
@State private var isShowLogoutAllDeviceDialog = false
|
@State private var isShowLogoutAllDeviceDialog = false
|
||||||
|
|
||||||
@StateObject var viewModel = SettingsViewModel()
|
@StateObject var viewModel = SettingsViewModel()
|
||||||
|
@StateObject var recentContentViewModel = RecentContentViewModel()
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
let cardWidth = screenSize().width - 26.7
|
let cardWidth = screenSize().width - 26.7
|
||||||
@@ -200,7 +201,10 @@ struct SettingsView: View {
|
|||||||
ContentPlayerPlayManager.shared.resetPlayer()
|
ContentPlayerPlayManager.shared.resetPlayer()
|
||||||
viewModel.logout {
|
viewModel.logout {
|
||||||
self.isShowLogoutDialog = false
|
self.isShowLogoutDialog = false
|
||||||
|
|
||||||
UserDefaults.reset()
|
UserDefaults.reset()
|
||||||
|
recentContentViewModel.truncate()
|
||||||
|
|
||||||
AppState.shared.isChangeAdultContentVisible = true
|
AppState.shared.isChangeAdultContentVisible = true
|
||||||
AppState.shared.setAppStep(step: .splash)
|
AppState.shared.setAppStep(step: .splash)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user