forked from MeloNX/MeloNX
UI enhancements
This commit is contained in:
parent
757fb1f6d1
commit
df2b17ddd6
@ -42,72 +42,54 @@ struct GameLibraryView: View {
|
|||||||
}
|
}
|
||||||
return Ryujinx.shared.games.filter {
|
return Ryujinx.shared.games.filter {
|
||||||
$0.titleName.localizedCaseInsensitiveContains(searchText) ||
|
$0.titleName.localizedCaseInsensitiveContains(searchText) ||
|
||||||
$0.developer.localizedCaseInsensitiveContains(searchText)
|
$0.developer.localizedCaseInsensitiveContains(searchText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
iOSNav {
|
iOSNav {
|
||||||
ScrollView {
|
List {
|
||||||
LazyVStack(alignment: .leading, spacing: 20) {
|
if Ryujinx.shared.games.isEmpty {
|
||||||
if !isSearching {
|
VStack(spacing: 16) {
|
||||||
Text("Games")
|
Image(systemName: "gamecontroller.fill")
|
||||||
.font(.system(size: 34, weight: .bold))
|
.font(.system(size: 64))
|
||||||
.padding(.horizontal)
|
.foregroundColor(.secondary.opacity(0.7))
|
||||||
.padding(.top, 12)
|
.padding(.top, 60)
|
||||||
|
Text("No Games Found")
|
||||||
|
.font(.title2.bold())
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
Text("Add ROM, Keys and Firmware to get started")
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
if Ryujinx.shared.games.isEmpty {
|
.padding(.top, 40)
|
||||||
VStack(spacing: 16) {
|
} else {
|
||||||
Image(systemName: "gamecontroller.fill")
|
if !isSearching && !recentGames.isEmpty {
|
||||||
.font(.system(size: 64))
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
.foregroundColor(.secondary.opacity(0.7))
|
Text("Recent")
|
||||||
.padding(.top, 60)
|
|
||||||
Text("No Games Found")
|
|
||||||
.font(.title2.bold())
|
.font(.title2.bold())
|
||||||
.foregroundColor(.primary)
|
.padding(.horizontal)
|
||||||
Text("Add ROM, Keys and Firmware to get started")
|
|
||||||
.font(.subheadline)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
.padding(.top, 40)
|
|
||||||
} else {
|
|
||||||
if !isSearching && !recentGames.isEmpty {
|
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
|
||||||
Text("Recent")
|
|
||||||
.font(.title2.bold())
|
|
||||||
.padding(.horizontal)
|
|
||||||
|
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
LazyHStack(spacing: 16) {
|
LazyHStack(spacing: 16) {
|
||||||
ForEach(recentGames) { game in
|
ForEach(recentGames) { game in
|
||||||
RecentGameCard(game: game, startemu: $startemu)
|
RecentGameCard(game: game, startemu: $startemu)
|
||||||
.onTapGesture {
|
|
||||||
addToRecentGames(game)
|
|
||||||
startemu = game
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.horizontal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
|
||||||
Text("All Games")
|
|
||||||
.font(.title2.bold())
|
|
||||||
.padding(.horizontal)
|
|
||||||
|
|
||||||
LazyVStack(spacing: 2) {
|
|
||||||
ForEach(filteredGames) { game in
|
|
||||||
GameListRow(game: game, startemu: $startemu, games: games, isViewingGameInfo: $isViewingGameInfo, isSelectingGameUpdate: $isSelectingGameUpdate, gameInfo: $gameInfo)
|
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
addToRecentGames(game)
|
addToRecentGames(game)
|
||||||
|
startemu = game
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
|
Text("All Games")
|
||||||
|
.font(.title2.bold())
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
LazyVStack(spacing: 2) {
|
LazyVStack(spacing: 2) {
|
||||||
ForEach(filteredGames) { game in
|
ForEach(filteredGames) { game in
|
||||||
GameListRow(game: game, startemu: $startemu, games: games, isViewingGameInfo: $isViewingGameInfo, isSelectingGameUpdate: $isSelectingGameUpdate, gameInfo: $gameInfo)
|
GameListRow(game: game, startemu: $startemu, games: games, isViewingGameInfo: $isViewingGameInfo, isSelectingGameUpdate: $isSelectingGameUpdate, gameInfo: $gameInfo)
|
||||||
@ -117,42 +99,51 @@ struct GameLibraryView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
}
|
ForEach(filteredGames) { game in
|
||||||
.onAppear {
|
GameListRow(game: game, startemu: $startemu, games: games, isViewingGameInfo: $isViewingGameInfo, isSelectingGameUpdate: $isSelectingGameUpdate, gameInfo: $gameInfo)
|
||||||
loadRecentGames()
|
.onTapGesture {
|
||||||
|
addToRecentGames(game)
|
||||||
let firmware = Ryujinx.shared.fetchFirmwareVersion()
|
}
|
||||||
firmwareversion = (firmware == "" ? "0" : firmware)
|
|
||||||
}
|
|
||||||
.fileImporter(isPresented: $firmwareInstaller, allowedContentTypes: [.item]) { result in
|
|
||||||
switch result {
|
|
||||||
case .success(let url):
|
|
||||||
do {
|
|
||||||
let fun = url.startAccessingSecurityScopedResource()
|
|
||||||
let path = url.path
|
|
||||||
|
|
||||||
Ryujinx.shared.installFirmware(firmwarePath: path)
|
|
||||||
|
|
||||||
firmwareversion = (Ryujinx.shared.fetchFirmwareVersion() == "" ? "0" : Ryujinx.shared.fetchFirmwareVersion())
|
|
||||||
if fun {
|
|
||||||
url.stopAccessingSecurityScopedResource()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case .failure(let error):
|
|
||||||
print(error)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.navigationTitle("Games")
|
||||||
|
.navigationBarTitleDisplayMode(.large)
|
||||||
|
.onAppear {
|
||||||
|
loadRecentGames()
|
||||||
|
|
||||||
|
let firmware = Ryujinx.shared.fetchFirmwareVersion()
|
||||||
|
firmwareversion = (firmware == "" ? "0" : firmware)
|
||||||
|
}
|
||||||
|
.fileImporter(isPresented: $firmwareInstaller, allowedContentTypes: [.item]) { result in
|
||||||
|
switch result {
|
||||||
|
case .success(let url):
|
||||||
|
do {
|
||||||
|
let fun = url.startAccessingSecurityScopedResource()
|
||||||
|
let path = url.path
|
||||||
|
|
||||||
|
Ryujinx.shared.installFirmware(firmwarePath: path)
|
||||||
|
|
||||||
|
firmwareversion = (Ryujinx.shared.fetchFirmwareVersion() == "" ? "0" : Ryujinx.shared.fetchFirmwareVersion())
|
||||||
|
if fun {
|
||||||
|
url.stopAccessingSecurityScopedResource()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .failure(let error):
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .topBarLeading) {
|
ToolbarItem(placement: .topBarTrailing) {
|
||||||
Button {
|
Button {
|
||||||
isSelectingGameFile.toggle()
|
isSelectingGameFile.toggle()
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "plus")
|
Image(systemName: "plus")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolbarItem(placement: .topBarLeading) {
|
ToolbarItem(placement: .topBarLeading) {
|
||||||
Menu {
|
Menu {
|
||||||
Text("Firmware Version: \(firmwareversion)")
|
Text("Firmware Version: \(firmwareversion)")
|
||||||
@ -215,7 +206,6 @@ struct GameLibraryView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.background(Color(.systemGroupedBackground))
|
|
||||||
.searchable(text: $searchText)
|
.searchable(text: $searchText)
|
||||||
.onChange(of: searchText) { _ in
|
.onChange(of: searchText) { _ in
|
||||||
isSearching = !searchText.isEmpty
|
isSearching = !searchText.isEmpty
|
||||||
@ -296,7 +286,6 @@ struct GameLibraryView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private func addToRecentGames(_ game: Game) {
|
private func addToRecentGames(_ game: Game) {
|
||||||
recentGames.removeAll { $0.id == game.id }
|
recentGames.removeAll { $0.id == game.id }
|
||||||
|
|
||||||
@ -329,8 +318,7 @@ struct GameLibraryView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Delete Game Function
|
||||||
// MARK: - Delete Game Function
|
|
||||||
func deleteGame(game: Game) {
|
func deleteGame(game: Game) {
|
||||||
let fileManager = FileManager.default
|
let fileManager = FileManager.default
|
||||||
do {
|
do {
|
||||||
@ -343,7 +331,7 @@ struct GameLibraryView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: -Game Model
|
// MARK: - Game Model
|
||||||
extension Game: Codable {
|
extension Game: Codable {
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case titleName, titleId, developer, version, fileURL
|
case titleName, titleId, developer, version, fileURL
|
||||||
@ -372,7 +360,7 @@ extension Game: Codable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: -Recent Game Card
|
// MARK: - Recent Game Card
|
||||||
struct RecentGameCard: View {
|
struct RecentGameCard: View {
|
||||||
let game: Game
|
let game: Game
|
||||||
@Binding var startemu: Game?
|
@Binding var startemu: Game?
|
||||||
@ -393,7 +381,7 @@ struct RecentGameCard: View {
|
|||||||
ZStack {
|
ZStack {
|
||||||
RoundedRectangle(cornerRadius: 12)
|
RoundedRectangle(cornerRadius: 12)
|
||||||
.fill(colorScheme == .dark ?
|
.fill(colorScheme == .dark ?
|
||||||
Color(.systemGray5) : Color(.systemGray6))
|
Color(.systemGray5) : Color(.systemGray6))
|
||||||
.frame(width: 140, height: 140)
|
.frame(width: 140, height: 140)
|
||||||
|
|
||||||
Image(systemName: "gamecontroller.fill")
|
Image(systemName: "gamecontroller.fill")
|
||||||
@ -419,7 +407,7 @@ struct RecentGameCard: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: -Game List Item
|
// MARK: - Game List Item
|
||||||
struct GameListRow: View {
|
struct GameListRow: View {
|
||||||
let game: Game
|
let game: Game
|
||||||
@Binding var startemu: Game?
|
@Binding var startemu: Game?
|
||||||
@ -447,7 +435,7 @@ struct GameListRow: View {
|
|||||||
ZStack {
|
ZStack {
|
||||||
RoundedRectangle(cornerRadius: 8)
|
RoundedRectangle(cornerRadius: 8)
|
||||||
.fill(colorScheme == .dark ?
|
.fill(colorScheme == .dark ?
|
||||||
Color(.systemGray5) : Color(.systemGray6))
|
Color(.systemGray5) : Color(.systemGray6))
|
||||||
.frame(width: 45, height: 45)
|
.frame(width: 45, height: 45)
|
||||||
|
|
||||||
Image(systemName: "gamecontroller.fill")
|
Image(systemName: "gamecontroller.fill")
|
||||||
@ -474,9 +462,6 @@ struct GameListRow: View {
|
|||||||
.foregroundColor(.accentColor)
|
.foregroundColor(.accentColor)
|
||||||
.opacity(0.8)
|
.opacity(0.8)
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
|
||||||
.padding(.vertical, 8)
|
|
||||||
.background(Color(.systemBackground))
|
|
||||||
.contextMenu {
|
.contextMenu {
|
||||||
Section {
|
Section {
|
||||||
Button {
|
Button {
|
||||||
@ -533,4 +518,3 @@ struct GameListRow: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,39 +15,45 @@ struct UpdateManagerSheet: View {
|
|||||||
@Binding var game: Game?
|
@Binding var game: Game?
|
||||||
@State private var isSelectingGameUpdate = false
|
@State private var isSelectingGameUpdate = false
|
||||||
@State private var jsonURL: URL? = nil
|
@State private var jsonURL: URL? = nil
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
VStack {
|
List(paths, id: \..self, selection: $selectedItem) { item in
|
||||||
List(paths, id: \..self) { item in
|
Button(action: {
|
||||||
Button(action: {
|
selectItem(item.lastPathComponent)
|
||||||
selectItem(item.lastPathComponent)
|
}) {
|
||||||
}) {
|
HStack {
|
||||||
HStack {
|
Text(item.lastPathComponent)
|
||||||
Text(item.lastPathComponent)
|
.foregroundStyle(Color(uiColor: .label))
|
||||||
if selectedItem == "\(game!.titleId)/\(item.lastPathComponent)" {
|
Spacer()
|
||||||
Spacer()
|
if selectedItem == "\(game!.titleId)/\(item.lastPathComponent)" {
|
||||||
Image(systemName: "checkmark")
|
Image(systemName: "checkmark.circle.fill")
|
||||||
}
|
.foregroundStyle(Color.accentColor)
|
||||||
}
|
.font(.system(size: 24))
|
||||||
}
|
} else {
|
||||||
.contextMenu {
|
Image(systemName: "circle")
|
||||||
Button {
|
.foregroundStyle(Color(uiColor: .secondaryLabel))
|
||||||
removeUpdate(item)
|
.font(.system(size: 24))
|
||||||
} label: {
|
|
||||||
Text("Remove Update")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.contextMenu {
|
||||||
|
Button {
|
||||||
|
removeUpdate(item)
|
||||||
|
} label: {
|
||||||
|
Text("Remove Update")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.onAppear() {
|
.onAppear {
|
||||||
print(URL.documentsDirectory.appendingPathComponent("games").appendingPathComponent(game!.titleId).appendingPathComponent("updates.json"))
|
print(URL.documentsDirectory.appendingPathComponent("games").appendingPathComponent(game!.titleId).appendingPathComponent("updates.json"))
|
||||||
|
|
||||||
loadJSON(URL.documentsDirectory.appendingPathComponent("games").appendingPathComponent(game!.titleId).appendingPathComponent("updates.json"))
|
loadJSON(URL.documentsDirectory.appendingPathComponent("games").appendingPathComponent(game!.titleId).appendingPathComponent("updates.json"))
|
||||||
}
|
}
|
||||||
.navigationTitle("\(game!.titleName) Updates")
|
.navigationTitle("\(game!.titleName) Updates")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
Button("+") {
|
Button("Add", systemImage: "plus") {
|
||||||
isSelectingGameUpdate = true
|
isSelectingGameUpdate = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,7 +86,8 @@ struct UpdateManagerSheet: View {
|
|||||||
let destinationURL = romUpdatedDirectory.appendingPathComponent(url.lastPathComponent)
|
let destinationURL = romUpdatedDirectory.appendingPathComponent(url.lastPathComponent)
|
||||||
try? fileManager.copyItem(at: url, to: destinationURL)
|
try? fileManager.copyItem(at: url, to: destinationURL)
|
||||||
|
|
||||||
Ryujinx.shared.setTitleUpdate(titleId: gameInfo.titleId, updatePath: "\(gameInfo.titleId)/" + url.lastPathComponent)
|
items.append(gameInfo.titleId + "/" + url.lastPathComponent)
|
||||||
|
selectItem(url.lastPathComponent)
|
||||||
Ryujinx.shared.games = Ryujinx.shared.loadGames()
|
Ryujinx.shared.games = Ryujinx.shared.loadGames()
|
||||||
loadJSON(jsonURL!)
|
loadJSON(jsonURL!)
|
||||||
} catch {
|
} catch {
|
||||||
@ -108,6 +115,7 @@ struct UpdateManagerSheet: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
saveJSON(selectedItem: selectedItem ?? "")
|
saveJSON(selectedItem: selectedItem ?? "")
|
||||||
|
Ryujinx.shared.games = Ryujinx.shared.loadGames()
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveJSON(selectedItem: String) {
|
func saveJSON(selectedItem: String) {
|
||||||
@ -122,7 +130,6 @@ struct UpdateManagerSheet: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func loadJSON(_ json: URL) {
|
func loadJSON(_ json: URL) {
|
||||||
|
|
||||||
self.jsonURL = json
|
self.jsonURL = json
|
||||||
print("Failed to read JSO")
|
print("Failed to read JSO")
|
||||||
|
|
||||||
@ -132,16 +139,17 @@ struct UpdateManagerSheet: View {
|
|||||||
do {
|
do {
|
||||||
let data = try Data(contentsOf: jsonURL)
|
let data = try Data(contentsOf: jsonURL)
|
||||||
if let jsonDict = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
if let jsonDict = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
||||||
let list = jsonDict["paths"] as? [String] {
|
let list = jsonDict["paths"] as? [String]
|
||||||
|
{
|
||||||
var urls: [URL] = []
|
var urls: [URL] = []
|
||||||
|
|
||||||
for path in list {
|
for path in list {
|
||||||
urls.append(URL.documentsDirectory.appendingPathComponent("updates").appendingPathComponent(path))
|
urls.append(URL.documentsDirectory.appendingPathComponent("updates").appendingPathComponent(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.items = list
|
items = list
|
||||||
self.paths = urls
|
paths = urls
|
||||||
self.selectedItem = jsonDict["selected"] as? String
|
selectedItem = jsonDict["selected"] as? String
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
print("Failed to read JSON: \(error)")
|
print("Failed to read JSON: \(error)")
|
||||||
@ -155,8 +163,8 @@ struct UpdateManagerSheet: View {
|
|||||||
do {
|
do {
|
||||||
let newData = try JSONSerialization.data(withJSONObject: defaultData, options: .prettyPrinted)
|
let newData = try JSONSerialization.data(withJSONObject: defaultData, options: .prettyPrinted)
|
||||||
try newData.write(to: jsonURL)
|
try newData.write(to: jsonURL)
|
||||||
self.items = []
|
items = []
|
||||||
self.selectedItem = ""
|
selectedItem = ""
|
||||||
} catch {
|
} catch {
|
||||||
print("Failed to create default JSON: \(error)")
|
print("Failed to create default JSON: \(error)")
|
||||||
}
|
}
|
||||||
@ -183,9 +191,9 @@ struct UpdateManagerSheet: View {
|
|||||||
|
|
||||||
let newData = try JSONSerialization.data(withJSONObject: jsonDict, options: .prettyPrinted)
|
let newData = try JSONSerialization.data(withJSONObject: jsonDict, options: .prettyPrinted)
|
||||||
try newData.write(to: jsonURL)
|
try newData.write(to: jsonURL)
|
||||||
|
Ryujinx.shared.games = Ryujinx.shared.loadGames()
|
||||||
} catch {
|
} catch {
|
||||||
print("Failed to update JSON: \(error)")
|
print("Failed to update JSON: \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user