mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
228 lines
9.5 KiB
Swift
228 lines
9.5 KiB
Swift
//
|
||
// AnisetteServerList.swift
|
||
// SideStore
|
||
//
|
||
// Created by ny on 6/18/24.
|
||
// Copyright © 2024 SideStore. All rights reserved.
|
||
//
|
||
|
||
import UIKit
|
||
import SwiftUI
|
||
import AltStoreCore
|
||
|
||
typealias SUIButton = SwiftUI.Button
|
||
|
||
// MARK: - AnisetteServerData
|
||
struct AnisetteServerData: Codable {
|
||
let servers: [Server]
|
||
}
|
||
|
||
// MARK: - Server
|
||
struct Server: Codable {
|
||
var name: String
|
||
var address: String
|
||
}
|
||
|
||
class AnisetteViewModel: ObservableObject {
|
||
@Published var selected: String = ""
|
||
|
||
@Published var source: String = "https://servers.sidestore.io/servers.json"
|
||
@Published var servers: [Server] = []
|
||
|
||
init() {
|
||
// using the custom Anisette list
|
||
if !UserDefaults.standard.menuAnisetteList.isEmpty {
|
||
self.source = UserDefaults.standard.menuAnisetteList
|
||
}
|
||
}
|
||
|
||
func getListOfServers() {
|
||
guard let url = URL(string: source) else { return }
|
||
|
||
// DO NOT use local cache when fetching anisette servers
|
||
var request = URLRequest(url: url)
|
||
request.cachePolicy = .reloadIgnoringLocalCacheData
|
||
|
||
URLSession.shared.dataTask(with: request) { data, response, error in
|
||
if let error = error {
|
||
return
|
||
}
|
||
if let data = data {
|
||
do {
|
||
let decoder = Foundation.JSONDecoder()
|
||
let servers = try decoder.decode(AnisetteServerData.self, from: data)
|
||
DispatchQueue.main.async {
|
||
self.servers = servers.servers
|
||
// store server addresses as list
|
||
UserDefaults.standard.menuAnisetteServersList = servers.servers.map(\.self.address)
|
||
}
|
||
} catch {
|
||
// Handle decoding error
|
||
print("Failed to decode JSON: \(error)")
|
||
}
|
||
}
|
||
}.resume()
|
||
}
|
||
}
|
||
|
||
struct AnisetteServers: View {
|
||
@Environment(\.presentationMode) var presentationMode
|
||
@StateObject var viewModel: AnisetteViewModel = AnisetteViewModel()
|
||
@State var selected: String? = nil
|
||
@State private var showingConfirmation = false
|
||
var errorCallback: () -> ()
|
||
|
||
var body: some View {
|
||
ZStack {
|
||
Color(UIColor.systemBackground)
|
||
.ignoresSafeArea()
|
||
.onAppear {
|
||
viewModel.getListOfServers()
|
||
}
|
||
VStack {
|
||
if #available(iOS 16.0, *) {
|
||
SwiftUI.List($viewModel.servers, id: \.address, selection: $selected) { server in
|
||
HStack {
|
||
VStack(alignment: .leading) {
|
||
Text("\(server.name.wrappedValue)")
|
||
.font(.headline)
|
||
.foregroundColor(.primary)
|
||
Text("\(server.address.wrappedValue)")
|
||
.font(.subheadline)
|
||
.foregroundColor(.secondary)
|
||
}
|
||
Spacer()
|
||
if selected != nil {
|
||
if server.address.wrappedValue == selected {
|
||
Spacer()
|
||
Image(systemName: "checkmark.circle.fill")
|
||
.foregroundColor(.accentColor)
|
||
.onAppear {
|
||
UserDefaults.standard.menuAnisetteURL = server.address.wrappedValue
|
||
print(UserDefaults.synchronize(.standard)())
|
||
print(UserDefaults.standard.menuAnisetteURL)
|
||
print(server.address.wrappedValue)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.padding()
|
||
.background(RoundedRectangle(cornerRadius: 10).fill(Color(UIColor.secondarySystemBackground)))
|
||
.shadow(color: Color.black.opacity(0.2), radius: 5, x: 0, y: 5)
|
||
}
|
||
.listStyle(.plain)
|
||
.scrollContentBackground(.hidden)
|
||
.listRowBackground(Color(UIColor.systemBackground))
|
||
} else {
|
||
List(selection: $selected) {
|
||
ForEach($viewModel.servers, id: \.name) { server in
|
||
VStack {
|
||
HStack {
|
||
Text("\(server.name.wrappedValue)")
|
||
.foregroundColor(.primary)
|
||
.frame(alignment: .center)
|
||
Text("\(server.address.wrappedValue)")
|
||
.foregroundColor(.secondary)
|
||
.frame(alignment: .center)
|
||
}
|
||
}
|
||
Spacer()
|
||
}
|
||
.padding()
|
||
.background(RoundedRectangle(cornerRadius: 10).fill(Color(UIColor.secondarySystemBackground)))
|
||
.shadow(color: Color.black.opacity(0.2), radius: 5, x: 0, y: 5)
|
||
}
|
||
.listStyle(.plain)
|
||
}
|
||
|
||
VStack(spacing: 16) {
|
||
TextField("Anisette Server List", text: $viewModel.source)
|
||
.padding()
|
||
.background(RoundedRectangle(cornerRadius: 10).fill(Color(UIColor.secondarySystemFill)))
|
||
.foregroundColor(.primary)
|
||
.frame(height: 60)
|
||
.shadow(color: Color.black.opacity(0.2), radius: 5, x: 0, y: 5)
|
||
.onChange(of: viewModel.source) { newValue in
|
||
UserDefaults.standard.menuAnisetteList = newValue
|
||
viewModel.getListOfServers()
|
||
}
|
||
|
||
HStack(spacing: 16) {
|
||
SUIButton(action: {
|
||
presentationMode.wrappedValue.dismiss()
|
||
}) {
|
||
HStack{
|
||
Spacer()
|
||
Text("Back")
|
||
.fontWeight(.semibold)
|
||
Spacer()
|
||
}
|
||
.contentShape(Rectangle())
|
||
}
|
||
.buttonStyle(PlainButtonStyle())
|
||
.padding()
|
||
.background(RoundedRectangle(cornerRadius: 10).fill(Color.accentColor))
|
||
.foregroundColor(.white)
|
||
.shadow(color: Color.accentColor.opacity(0.4), radius: 10, x: 0, y: 5)
|
||
|
||
SUIButton(action: {
|
||
viewModel.getListOfServers()
|
||
}) {
|
||
HStack{
|
||
Spacer()
|
||
Text("Refresh Servers")
|
||
.fontWeight(.semibold)
|
||
.frame(maxWidth: .infinity)
|
||
Spacer()
|
||
}
|
||
.contentShape(Rectangle())
|
||
}
|
||
.buttonStyle(PlainButtonStyle())
|
||
.padding()
|
||
.background(RoundedRectangle(cornerRadius: 10).fill(Color.accentColor))
|
||
.foregroundColor(.white)
|
||
.shadow(color: Color.accentColor.opacity(0.4), radius: 10, x: 0, y: 5)
|
||
|
||
}
|
||
|
||
SUIButton(action: {
|
||
showingConfirmation = true
|
||
}) {
|
||
Text("Reset adi.pb")
|
||
.fontWeight(.semibold)
|
||
.frame(maxWidth: .infinity)
|
||
}
|
||
.buttonStyle(PlainButtonStyle())
|
||
.padding()
|
||
.background(RoundedRectangle(cornerRadius: 10).fill(Color.red))
|
||
.foregroundColor(.white)
|
||
.shadow(color: Color.red.opacity(0.4), radius: 10, x: 0, y: 5)
|
||
.alert(isPresented: $showingConfirmation) {
|
||
Alert(
|
||
title: Text("Reset adi.pb"),
|
||
message: Text("are you sure to clear the adi.pb from keychain?"),
|
||
primaryButton: .default(Text("do it")) {
|
||
#if !DEBUG
|
||
if Keychain.shared.adiPb != nil {
|
||
Keychain.shared.adiPb = nil
|
||
}
|
||
#endif
|
||
print("Cleared adi.pb from keychain")
|
||
errorCallback()
|
||
presentationMode.wrappedValue.dismiss()
|
||
},
|
||
secondaryButton: .cancel(Text("cancel")) {
|
||
print("canceled")
|
||
}
|
||
)
|
||
}
|
||
}
|
||
.padding(.horizontal)
|
||
.padding(.bottom)
|
||
}
|
||
}
|
||
.navigationBarHidden(true)
|
||
.navigationTitle("")
|
||
}
|
||
}
|