Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/add offline database #86

Merged
merged 1 commit into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Ichime/ContentView/ContentViewWithSideBar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ struct ContentViewWithSideBar: View {
@Environment(\.modelContext) private var modelContext
@AppStorage("ContentViewWithTabView.selectedTab") private var selectedTab: Tabs = .home

@State var viewModel: ShowListStatusModel = ApplicationDependency.container.resolve()
@State var viewModel: UserAnimeListCache = ApplicationDependency.container.resolve()

var body: some View {
TabView(selection: $selectedTab) {
Expand Down
2 changes: 1 addition & 1 deletion Ichime/ContentView/ContentViewWithTabBar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ struct ContentViewWithTabBar: View {
@Environment(\.modelContext) private var modelContext
@AppStorage("ContentViewWithTabView.selectedTab") private var selectedTab: Tabs = .home

@State var viewModel: ShowListStatusModel = ApplicationDependency.container.resolve()
@State var viewModel: UserAnimeListCache = ApplicationDependency.container.resolve()

var body: some View {
TabView(selection: $selectedTab) {
Expand Down
15 changes: 13 additions & 2 deletions Ichime/DependencyInjection/DI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import ShikimoriApiClient
import SwiftData

class ApplicationDependency: DIFramework {

static let container: DIContainer = {
let container = DIContainer()
container.append(framework: ApplicationDependency.self)
Expand All @@ -21,7 +22,13 @@ class ApplicationDependency: DIFramework {

static func load(container: DIContainer) {
container.register {
let schema = Schema([ShowListStatusEntity.self])
let schema = Schema([
UserAnimeListModel.self,
DbAnime.self,
DbGenre.self,
DbStudio.self,
])
let storeURL = URL.documentsDirectory.appending(path: "offline.sqlite")
let modelConfiguration = ModelConfiguration(
schema: schema,
groupContainer: .identifier(ServiceLocator.appGroup)
Expand Down Expand Up @@ -73,7 +80,11 @@ class ApplicationDependency: DIFramework {
}

container.register {
ShowListStatusModel(apiClient: $0, userManager: $1, modelContainer: $2)
UserAnimeListCache(apiClient: $0, userManager: $1, modelContainer: $2)
}

container.register {
DbService(modelContainer: $0)
}

container.register { Anime365Client(apiClient: $0) }
Expand Down
65 changes: 65 additions & 0 deletions Ichime/IchimeApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,27 @@ struct IchimeApp: App {
let container: ModelContainer = ApplicationDependency.container.resolve()

@Environment(\.scenePhase) private var phase
@State private var isImporting = false
@State private var progressText = "Импорт базы данных..."

var body: some Scene {
WindowGroup {
ContentView()
.overlay(alignment: .bottom) {
if isImporting {
ProgressView(progressText)
.padding()
.background(.ultraThinMaterial)
.cornerRadius(10)
}
}
.onAppear {
VideoPlayerController.enableBackgroundMode()
NotificationCounterWatcher.askBadgePermission()
}
.task {
await importDatabase()
}
}.onChange(of: phase) {
switch phase {
case .background:
Expand All @@ -33,4 +46,56 @@ struct IchimeApp: App {
}
.modelContainer(container)
}

private func importDatabase() async {
guard !isImporting else { return }
isImporting = true

do {
// Запрос к API
let url = URL(string: "https://db.dimensi.dev/api/latest")!
let (data, _) = try await URLSession.shared.data(from: url)
let response = try JSONDecoder().decode(DbServerResponse.self, from: data)

let lastUpdated = UserDefaults().string(forKey: "lastUpdated") ?? ""
let lastTimestamp = Int(lastUpdated) ?? 0

if lastTimestamp == response.date {
isImporting = false
return
}

let animeImporter = AnimeImporter(modelContainer: container)
Task.detached(priority: .high) {
do {
let dbUrl = "https://db.dimensi.dev\(response.url)"
async let result: () = animeImporter.importDatabase(from: dbUrl)
for await progress in await animeImporter.currentProgress {
await MainActor.run {
progressText = progress
}
}
try await result
await MainActor.run {
isImporting = false
UserDefaults().set(response.date, forKey: "lastUpdated")
}
}
catch {
await MainActor.run {
isImporting = false
}
}
}
}
catch {
isImporting = false
print("Error fetching database info: \(error)")
}
}
}

struct DbServerResponse: Codable {
let date: Int
let url: String
}
67 changes: 27 additions & 40 deletions Ichime/MyLists/Component/AnimeList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,60 +56,47 @@ private struct MyListEntry: View {
}

struct AnimeList: View {
let categories: [ScraperAPI.Types.ListByCategory]
let onUpdate: () async -> Void
let status: AnimeWatchStatus
let animeList: [UserAnimeListModel]

@State var selectedShow: ScraperAPI.Types.Show?
@State var selectedShow: MyListShow?

var body: some View {
List {
ForEach(categories, id: \.type) { category in
Section {
ForEach(category.shows, id: \.id) { show in
Button(action: {
selectedShow = show
}) {
MyListEntry(
primaryTitle: show.name.ru, // TODO: сделать romaji primary
secondaryTitle: show.name.romaji,
currentEpisodeProgress: show.episodes.watched,
totalEpisodes: show.episodes.total
)
.contextMenu(
menuItems: {

NavigationLink(destination: ShowView(showId: show.id)) {
Text("Открыть")
}
},
preview: {
IndependentShowCardContextMenuPreview(showId: show.id)
Section {
ForEach(animeList, id: \.id) { show in
Button(action: {
selectedShow = .init(id: show.id, name: show.name.ru, totalEpisodes: show.progress.total)
}) {
MyListEntry(
primaryTitle: show.name.ru, // TODO: сделать romaji primary
secondaryTitle: show.name.romaji,
currentEpisodeProgress: show.progress.watched,
totalEpisodes: show.progress.total
)
.contextMenu(
menuItems: {
NavigationLink(destination: ShowView(showId: show.id)) {
Text("Открыть")
}
)
}
},
preview: {
IndependentShowCardContextMenuPreview(showId: show.id)
}
)
}
} header: {
Text(category.type.rawValue)
}
} header: {
Text(status.title)
}
}
.sheet(
item: $selectedShow,
content: { show in
MyListEditView(
show: .init(id: show.id, name: show.name.ru, totalEpisodes: show.episodes.total)
) {
Task {
await onUpdate()
}
}
show: show
)
}
)
}
}

#Preview {
NavigationStack {
AnimeList(categories: ScraperAPI.Types.ListByCategory.sampleData) {}
}
}
2 changes: 1 addition & 1 deletion Ichime/MyLists/Model/MyListShow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

struct MyListShow {
struct MyListShow: Identifiable {
let id: Int
let name: String
let totalEpisodes: Int?
Expand Down
132 changes: 0 additions & 132 deletions Ichime/MyLists/Model/ShowListStatus.swift

This file was deleted.

Loading