mirror of
https://github.com/rommapp/romm.git
synced 2026-02-19 07:50:57 +01:00
Add age ratings to UI + filters
This commit is contained in:
@@ -91,6 +91,7 @@ class RomSchema(BaseModel):
|
||||
collections: list[str]
|
||||
companies: list[str]
|
||||
game_modes: list[str]
|
||||
age_ratings: list[str]
|
||||
igdb_metadata: RomIGDBMetadata | None
|
||||
moby_metadata: RomMobyMetadata | None
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ def test_update_rom(rename_file_mock, get_rom_by_id_mock, client, access_token,
|
||||
"expanded_games": "[]",
|
||||
"ports": "[]",
|
||||
"similar_games": "[]",
|
||||
"age_ratings": "[1, 2]",
|
||||
},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
@@ -39,6 +39,12 @@ class IGDBPlatform(TypedDict):
|
||||
name: NotRequired[str]
|
||||
|
||||
|
||||
class IGDBAgeRating(TypedDict):
|
||||
rating: str
|
||||
category: str
|
||||
rating_cover_url: str
|
||||
|
||||
|
||||
class IGDBMetadataPlatform(TypedDict):
|
||||
igdb_id: int
|
||||
name: str
|
||||
@@ -62,6 +68,7 @@ class IGDBMetadata(TypedDict):
|
||||
collections: list[str]
|
||||
companies: list[str]
|
||||
game_modes: list[str]
|
||||
age_ratings: list[IGDBAgeRating]
|
||||
platforms: list[IGDBMetadataPlatform]
|
||||
expansions: list[IGDBRelatedGame]
|
||||
dlcs: list[IGDBRelatedGame]
|
||||
@@ -101,6 +108,9 @@ def extract_metadata_from_igdb_rom(rom: dict) -> IGDBMetadata:
|
||||
IGDBMetadataPlatform(igdb_id=p.get("id", ""), name=p.get("name", ""))
|
||||
for p in rom.get("platforms", [])
|
||||
],
|
||||
"age_ratings": [
|
||||
IGDB_AGE_RATINGS[r["rating"]] for r in rom.get("age_ratings", [])
|
||||
],
|
||||
"expansions": [
|
||||
IGDBRelatedGame(
|
||||
id=e["id"],
|
||||
@@ -537,8 +547,8 @@ class IGDBBaseHandler(MetadataHandler):
|
||||
]
|
||||
|
||||
return [
|
||||
IGDBRom( # type: ignore[misc]
|
||||
{
|
||||
IGDBRom(
|
||||
{ # type: ignore[misc]
|
||||
k: v
|
||||
for k, v in {
|
||||
"igdb_id": rom["id"],
|
||||
@@ -546,12 +556,12 @@ class IGDBBaseHandler(MetadataHandler):
|
||||
"name": rom["name"],
|
||||
"summary": rom.get("summary", ""),
|
||||
"url_cover": self._normalize_cover_url(
|
||||
rom.get("cover", {})
|
||||
.get("url", "")
|
||||
.replace("t_thumb", "t_cover_big")
|
||||
pydash.get(rom, "cover.url", "").replace(
|
||||
"t_thumb", "t_cover_big"
|
||||
)
|
||||
),
|
||||
"url_screenshots": [
|
||||
self._normalize_cover_url(s.get("url", ""))
|
||||
self._normalize_cover_url(s.get("url", "")) # type: ignore[arg-type]
|
||||
for s in rom.get("screenshots", [])
|
||||
],
|
||||
"igdb_metadata": extract_metadata_from_igdb_rom(rom),
|
||||
@@ -674,6 +684,7 @@ GAMES_FIELDS = [
|
||||
"similar_games.slug",
|
||||
"similar_games.name",
|
||||
"similar_games.cover.url",
|
||||
"age_ratings.rating",
|
||||
]
|
||||
|
||||
SEARCH_FIELDS = ["game.id", "name"]
|
||||
@@ -902,3 +913,186 @@ IGDB_PLATFORM_LIST = [
|
||||
{"slug": "vc", "name": "Virtual Console"},
|
||||
{"slug": "airconsole", "name": "AirConsole"},
|
||||
]
|
||||
|
||||
IGDB_AGE_RATINGS: dict[int, IGDBAgeRating] = {
|
||||
1: {
|
||||
"rating": "Three",
|
||||
"category": "PEGI",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/pegi/pegi_3.png",
|
||||
},
|
||||
2: {
|
||||
"rating": "Seven",
|
||||
"category": "PEGI",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/pegi/pegi_7.png",
|
||||
},
|
||||
3: {
|
||||
"rating": "Twelve",
|
||||
"category": "PEGI",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/pegi/pegi_12.png",
|
||||
},
|
||||
4: {
|
||||
"rating": "Sixteen",
|
||||
"category": "PEGI",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/pegi/pegi_16.png",
|
||||
},
|
||||
5: {
|
||||
"rating": "Eighteen",
|
||||
"category": "PEGI",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/pegi/pegi_18.png",
|
||||
},
|
||||
6: {
|
||||
"rating": "RP",
|
||||
"category": "ESRB",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/esrb/esrb_rp.png",
|
||||
},
|
||||
7: {
|
||||
"rating": "EC",
|
||||
"category": "ESRB",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/esrb/esrb_ec.png",
|
||||
},
|
||||
8: {
|
||||
"rating": "E",
|
||||
"category": "ESRB",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/esrb/esrb_e.png",
|
||||
},
|
||||
9: {
|
||||
"rating": "E10",
|
||||
"category": "ESRB",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/esrb/esrb_e10.png",
|
||||
},
|
||||
10: {
|
||||
"rating": "T",
|
||||
"category": "ESRB",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/esrb/esrb_t.png",
|
||||
},
|
||||
11: {
|
||||
"rating": "M",
|
||||
"category": "ESRB",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/esrb/esrb_m.png",
|
||||
},
|
||||
12: {
|
||||
"rating": "AO",
|
||||
"category": "ESRB",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/esrb/esrb_ao.png",
|
||||
},
|
||||
13: {
|
||||
"rating": "CERO_A",
|
||||
"category": "CERO",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/cero/cero_a.png",
|
||||
},
|
||||
14: {
|
||||
"rating": "CERO_B",
|
||||
"category": "CERO",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/cero/cero_b.png",
|
||||
},
|
||||
15: {
|
||||
"rating": "CERO_C",
|
||||
"category": "CERO",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/cero/cero_c.png",
|
||||
},
|
||||
16: {
|
||||
"rating": "CERO_D",
|
||||
"category": "CERO",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/cero/cero_d.png",
|
||||
},
|
||||
17: {
|
||||
"rating": "CERO_Z",
|
||||
"category": "CERO",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/cero/cero_z.png",
|
||||
},
|
||||
18: {
|
||||
"rating": "USK_0",
|
||||
"category": "USK",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/usk/usk_0.png",
|
||||
},
|
||||
19: {
|
||||
"rating": "USK_6",
|
||||
"category": "USK",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/usk/usk_6.png",
|
||||
},
|
||||
20: {
|
||||
"rating": "USK_12",
|
||||
"category": "USK",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/usk/usk_12.png",
|
||||
},
|
||||
21: {
|
||||
"rating": "USK_16",
|
||||
"category": "USK",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/usk/usk_16.png",
|
||||
},
|
||||
22: {
|
||||
"rating": "USK_18",
|
||||
"category": "USK",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/usk/usk_18.png",
|
||||
},
|
||||
23: {
|
||||
"rating": "GRAC_ALL",
|
||||
"category": "GRAC",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/grac/grac_all.png",
|
||||
},
|
||||
24: {
|
||||
"rating": "GRAC_Twelve",
|
||||
"category": "GRAC",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/grac/grac_twelve.png",
|
||||
},
|
||||
25: {
|
||||
"rating": "GRAC_Fifteen",
|
||||
"category": "GRAC",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/grac/grac_fifteen.png",
|
||||
},
|
||||
26: {
|
||||
"rating": "GRAC_Eighteen",
|
||||
"category": "GRAC",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/grac/grac_eighteen.png",
|
||||
},
|
||||
27: {
|
||||
"rating": "GRAC_TESTING",
|
||||
"category": "GRAC",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/grac/grac_testing.png",
|
||||
},
|
||||
28: {
|
||||
"rating": "CLASS_IND_L",
|
||||
"category": "CLASS_IND",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/classind/classind_l.png",
|
||||
},
|
||||
29: {
|
||||
"rating": "CLASS_IND_Ten",
|
||||
"category": "CLASS_IND",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/classind/classind_ten.png",
|
||||
},
|
||||
30: {
|
||||
"rating": "CLASS_IND_Twelve",
|
||||
"category": "CLASS_IND",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/classind/classind_twelve.png",
|
||||
},
|
||||
31: {
|
||||
"rating": "ACB_G",
|
||||
"category": "ACB",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/acb/acb_g.png",
|
||||
},
|
||||
32: {
|
||||
"rating": "ACB_PG",
|
||||
"category": "ACB",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/acb/acb_pg.png",
|
||||
},
|
||||
33: {
|
||||
"rating": "ACB_M",
|
||||
"category": "ACB",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/acb/acb_m.png",
|
||||
},
|
||||
34: {
|
||||
"rating": "ACB_MA15",
|
||||
"category": "ACB",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/acb/acb_ma15.png",
|
||||
},
|
||||
35: {
|
||||
"rating": "ACB_R18",
|
||||
"category": "ACB",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/acb/acb_r18.png",
|
||||
},
|
||||
36: {
|
||||
"rating": "ACB_RC",
|
||||
"category": "ACB",
|
||||
"rating_cover_url": "https://www.igdb.com/icons/rating_icons/acb/acb_rc.png",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -162,6 +162,10 @@ class Rom(BaseModel):
|
||||
def game_modes(self) -> list[str]:
|
||||
return self.igdb_metadata.get("game_modes", [])
|
||||
|
||||
@property
|
||||
def age_ratings(self) -> list[str]:
|
||||
return [r["rating"] for r in self.igdb_metadata.get("age_ratings", [])]
|
||||
|
||||
@property
|
||||
def fs_resources_path(self) -> str:
|
||||
return f"roms/{str(self.platform_id)}/{str(self.id)}"
|
||||
|
||||
@@ -123,6 +123,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"age_ratings": [{ "rating": 1 }, { "rating": 2 }],
|
||||
"expansions": [
|
||||
{
|
||||
"id": 239930,
|
||||
|
||||
1
frontend/src/__generated__/index.ts
generated
1
frontend/src/__generated__/index.ts
generated
@@ -19,6 +19,7 @@ export type { DetailedRomSchema } from './models/DetailedRomSchema';
|
||||
export type { FirmwareSchema } from './models/FirmwareSchema';
|
||||
export type { HeartbeatResponse } from './models/HeartbeatResponse';
|
||||
export type { HTTPValidationError } from './models/HTTPValidationError';
|
||||
export type { IGDBAgeRating } from './models/IGDBAgeRating';
|
||||
export type { IGDBMetadataPlatform } from './models/IGDBMetadataPlatform';
|
||||
export type { IGDBRelatedGame } from './models/IGDBRelatedGame';
|
||||
export type { MessageResponse } from './models/MessageResponse';
|
||||
|
||||
@@ -38,6 +38,7 @@ export type DetailedRomSchema = {
|
||||
collections: Array<string>;
|
||||
companies: Array<string>;
|
||||
game_modes: Array<string>;
|
||||
age_ratings: Array<string>;
|
||||
igdb_metadata: (RomIGDBMetadata | null);
|
||||
moby_metadata: (RomMobyMetadata | null);
|
||||
path_cover_s: (string | null);
|
||||
|
||||
11
frontend/src/__generated__/models/IGDBAgeRating.ts
generated
Normal file
11
frontend/src/__generated__/models/IGDBAgeRating.ts
generated
Normal file
@@ -0,0 +1,11 @@
|
||||
/* generated using openapi-typescript-codegen -- do no edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type IGDBAgeRating = {
|
||||
rating: string;
|
||||
category: string;
|
||||
rating_cover_url: string;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { IGDBAgeRating } from './IGDBAgeRating';
|
||||
import type { IGDBMetadataPlatform } from './IGDBMetadataPlatform';
|
||||
import type { IGDBRelatedGame } from './IGDBRelatedGame';
|
||||
|
||||
@@ -16,6 +17,7 @@ export type RomIGDBMetadata = {
|
||||
collections?: Array<string>;
|
||||
companies?: Array<string>;
|
||||
game_modes?: Array<string>;
|
||||
age_ratings?: Array<IGDBAgeRating>;
|
||||
platforms?: Array<IGDBMetadataPlatform>;
|
||||
expansions?: Array<IGDBRelatedGame>;
|
||||
dlcs?: Array<IGDBRelatedGame>;
|
||||
|
||||
1
frontend/src/__generated__/models/RomSchema.ts
generated
1
frontend/src/__generated__/models/RomSchema.ts
generated
@@ -31,6 +31,7 @@ export type RomSchema = {
|
||||
collections: Array<string>;
|
||||
companies: Array<string>;
|
||||
game_modes: Array<string>;
|
||||
age_ratings: Array<string>;
|
||||
igdb_metadata: (RomIGDBMetadata | null);
|
||||
moby_metadata: (RomMobyMetadata | null);
|
||||
path_cover_s: (string | null);
|
||||
|
||||
@@ -33,6 +33,7 @@ export type SimpleRomSchema = {
|
||||
collections: Array<string>;
|
||||
companies: Array<string>;
|
||||
game_modes: Array<string>;
|
||||
age_ratings: Array<string>;
|
||||
igdb_metadata: (RomIGDBMetadata | null);
|
||||
moby_metadata: (RomMobyMetadata | null);
|
||||
path_cover_s: (string | null);
|
||||
|
||||
@@ -22,7 +22,6 @@ emitter?.on("showCreateUserDialog", () => {
|
||||
show.value = true;
|
||||
});
|
||||
|
||||
// Functions
|
||||
async function createUser() {
|
||||
await userApi
|
||||
.createUser(user.value)
|
||||
|
||||
@@ -22,7 +22,6 @@ emitter?.on("showEditUserDialog", (userToEdit) => {
|
||||
show.value = true;
|
||||
});
|
||||
|
||||
// Functions
|
||||
function triggerFileInput() {
|
||||
const fileInput = document.getElementById("file-input");
|
||||
fileInput?.click();
|
||||
@@ -138,8 +137,8 @@ function closeDialog() {
|
||||
imagePreviewUrl
|
||||
? imagePreviewUrl
|
||||
: user.avatar_path
|
||||
? `/assets/romm/assets/${user.avatar_path}?ts=${user.updated_at}`
|
||||
: defaultAvatarPath
|
||||
? `/assets/romm/assets/${user.avatar_path}?ts=${user.updated_at}`
|
||||
: defaultAvatarPath
|
||||
"
|
||||
>
|
||||
<v-fade-transition>
|
||||
|
||||
@@ -58,7 +58,6 @@ const usersPerPage = ref(isNaN(storedUsersPerPage) ? 25 : storedUsersPerPage);
|
||||
const pageCount = ref(0);
|
||||
emitter?.on("updateDataTablePages", updateDataTablePages);
|
||||
|
||||
// Functions
|
||||
function updateDataTablePages() {
|
||||
pageCount.value = Math.ceil(usersStore.allUsers.length / usersPerPage.value);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,11 @@ import romApi from "@/services/api/rom";
|
||||
import storeDownload from "@/stores/download";
|
||||
import type { DetailedRom } from "@/stores/roms";
|
||||
import type { Events } from "@/types/emitter";
|
||||
import { getDownloadLink, isEJSEmulationSupported, isRuffleEmulationSupported } from "@/utils";
|
||||
import {
|
||||
getDownloadLink,
|
||||
isEJSEmulationSupported,
|
||||
isRuffleEmulationSupported,
|
||||
} from "@/utils";
|
||||
import type { Emitter } from "mitt";
|
||||
import { inject, ref } from "vue";
|
||||
|
||||
@@ -14,7 +18,9 @@ const downloadStore = storeDownload();
|
||||
const emitter = inject<Emitter<Events>>("emitter");
|
||||
const playInfoIcon = ref("mdi-play");
|
||||
const ejsEmulationSupported = isEJSEmulationSupported(props.rom.platform_slug);
|
||||
const ruffleEmulationSupported = isRuffleEmulationSupported(props.rom.platform_slug);
|
||||
const ruffleEmulationSupported = isRuffleEmulationSupported(
|
||||
props.rom.platform_slug,
|
||||
);
|
||||
|
||||
// Functions
|
||||
async function copyDownloadLink(rom: DetailedRom) {
|
||||
@@ -26,7 +32,7 @@ async function copyDownloadLink(rom: DetailedRom) {
|
||||
getDownloadLink({
|
||||
rom,
|
||||
files: downloadStore.filesToDownload,
|
||||
})
|
||||
}),
|
||||
);
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
await navigator.clipboard.writeText(downloadLink);
|
||||
|
||||
@@ -23,7 +23,7 @@ const romUser = ref(
|
||||
note_raw_markdown: "",
|
||||
note_is_public: false,
|
||||
is_main_sibling: false,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Functions
|
||||
@@ -53,7 +53,7 @@ watch(
|
||||
note_is_public: false,
|
||||
is_main_sibling: false,
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
@@ -117,7 +117,7 @@ watch(
|
||||
v-model="downloadStore.filesToDownload"
|
||||
:label="rom.file_name"
|
||||
item-title="file_name"
|
||||
:items="rom.files.map(f => f.filename)"
|
||||
:items="rom.files.map((f) => f.filename)"
|
||||
rounded="0"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
@@ -137,13 +137,28 @@ watch(
|
||||
<v-chip size="small" label class="mx-1 my-1">
|
||||
Size: {{ formatBytes(rom.file_size_bytes) }}
|
||||
</v-chip>
|
||||
<v-chip v-if="!rom.multi && rom.sha1_hash" size="small" label class="mx-1 my-1">
|
||||
<v-chip
|
||||
v-if="!rom.multi && rom.sha1_hash"
|
||||
size="small"
|
||||
label
|
||||
class="mx-1 my-1"
|
||||
>
|
||||
SHA-1: {{ rom.sha1_hash }}
|
||||
</v-chip>
|
||||
<v-chip v-if="!rom.multi && rom.md5_hash" size="small" label class="mx-1 my-1">
|
||||
MD5: {{ rom.md5_hash }}
|
||||
<v-chip
|
||||
v-if="!rom.multi && rom.md5_hash"
|
||||
size="small"
|
||||
label
|
||||
class="mx-1 my-1"
|
||||
>
|
||||
MD5: {{ rom.md5_hash }}
|
||||
</v-chip>
|
||||
<v-chip v-if="!rom.multi && rom.crc_hash" size="small" label class="mx-1 my-1">
|
||||
<v-chip
|
||||
v-if="!rom.multi && rom.crc_hash"
|
||||
size="small"
|
||||
label
|
||||
class="mx-1 my-1"
|
||||
>
|
||||
CRC: {{ rom.crc_hash }}
|
||||
</v-chip>
|
||||
</v-col>
|
||||
@@ -180,7 +195,7 @@ watch(
|
||||
<v-col>
|
||||
<v-chip
|
||||
v-for="collection in collectionsWithoutFavourites(
|
||||
rom.user_collections
|
||||
rom.user_collections,
|
||||
)"
|
||||
:to="{ name: 'collection', params: { collection: collection.id } }"
|
||||
size="large"
|
||||
|
||||
@@ -23,7 +23,12 @@ function onFilterClick(filter: FilterType, value: string) {
|
||||
<v-row no-gutters>
|
||||
<v-col>
|
||||
<v-divider class="mx-2 my-4" />
|
||||
<template v-for="filter in galleryFilterStore.filters" :key="filter">
|
||||
<template
|
||||
v-for="filter in galleryFilterStore.filters.filter(
|
||||
(f) => f !== 'age_ratings',
|
||||
)"
|
||||
:key="filter"
|
||||
>
|
||||
<v-row
|
||||
v-if="rom[filter].length > 0"
|
||||
class="align-center my-3"
|
||||
@@ -47,6 +52,30 @@ function onFilterClick(filter: FilterType, value: string) {
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
<!-- Manually add age ratings to display logos -->
|
||||
<template
|
||||
v-if="
|
||||
rom.igdb_metadata?.age_ratings &&
|
||||
rom.igdb_metadata.age_ratings.length > 0
|
||||
"
|
||||
>
|
||||
<v-row no-gutters class="mt-5">
|
||||
<v-col cols="3" xl="2" class="text-capitalize">
|
||||
<span>Age Rating</span>
|
||||
</v-col>
|
||||
<div class="d-flex">
|
||||
<v-img
|
||||
v-for="value in rom.igdb_metadata.age_ratings"
|
||||
:key="value.rating"
|
||||
@click="onFilterClick('age_ratings', value.rating)"
|
||||
:src="value.rating_cover_url"
|
||||
height="50"
|
||||
width="50"
|
||||
class="mr-4 cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
</v-row>
|
||||
</template>
|
||||
<template v-if="rom.summary != ''">
|
||||
<v-divider class="mx-2 my-4" />
|
||||
<v-row no-gutters>
|
||||
|
||||
@@ -21,12 +21,11 @@ const romUser = ref(
|
||||
note_raw_markdown: "",
|
||||
note_is_public: false,
|
||||
is_main_sibling: false,
|
||||
}
|
||||
},
|
||||
);
|
||||
const publicNotes =
|
||||
props.rom.user_notes?.filter((note) => note.user_id !== auth.user?.id) ?? [];
|
||||
|
||||
// Functions
|
||||
function togglePublic() {
|
||||
romUser.value.note_is_public = !romUser.value.note_is_public;
|
||||
romApi.updateUserRomProps({
|
||||
@@ -61,7 +60,7 @@ watch(
|
||||
note_is_public: false,
|
||||
is_main_sibling: false,
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
@@ -131,11 +130,7 @@ watch(
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-card
|
||||
rounded="0"
|
||||
v-if="publicNotes && publicNotes.length > 0"
|
||||
class="mt-2"
|
||||
>
|
||||
<v-card rounded="0" v-if="publicNotes && publicNotes.length > 0" class="mt-2">
|
||||
<v-card-title class="bg-terciary">
|
||||
<v-list-item class="pl-2 pr-0">
|
||||
<span class="text-h6">Public notes</span>
|
||||
|
||||
@@ -60,7 +60,7 @@ async function downloasSaves() {
|
||||
function updateDataTablePages() {
|
||||
if (props.rom.user_saves) {
|
||||
pageCount.value = Math.ceil(
|
||||
props.rom.user_saves.length / itemsPerPage.value
|
||||
props.rom.user_saves.length / itemsPerPage.value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ async function downloasStates() {
|
||||
function updateDataTablePages() {
|
||||
if (props.rom.user_states) {
|
||||
pageCount.value = Math.ceil(
|
||||
props.rom.user_states.length / itemsPerPage.value
|
||||
props.rom.user_states.length / itemsPerPage.value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ function deleteSelectedFirmware() {
|
||||
|
||||
function updateDataTablePages() {
|
||||
pageCount.value = Math.ceil(
|
||||
Number(currentPlatform.value?.firmware?.length) / itemsPerPage.value
|
||||
Number(currentPlatform.value?.firmware?.length) / itemsPerPage.value,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,10 @@ import { inject, nextTick, ref } from "vue";
|
||||
import { useDisplay } from "vuetify";
|
||||
|
||||
// Props
|
||||
const show = ref(false);
|
||||
const { xs } = useDisplay();
|
||||
const emitter = inject<Emitter<Events>>("emitter");
|
||||
const galleryFilterStore = storeGalleryFilter();
|
||||
|
||||
const {
|
||||
activeFilterDrawer,
|
||||
selectedGenre,
|
||||
@@ -25,7 +25,10 @@ const {
|
||||
filterCollections,
|
||||
selectedCompany,
|
||||
filterCompanies,
|
||||
selectedAgeRating,
|
||||
filterAgeRatings,
|
||||
} = storeToRefs(galleryFilterStore);
|
||||
|
||||
const filters = [
|
||||
{
|
||||
label: "Genre",
|
||||
@@ -47,6 +50,11 @@ const filters = [
|
||||
selected: selectedCompany,
|
||||
items: filterCompanies,
|
||||
},
|
||||
{
|
||||
label: "Age Rating",
|
||||
selected: selectedAgeRating,
|
||||
items: filterAgeRatings,
|
||||
},
|
||||
];
|
||||
|
||||
// Functions
|
||||
@@ -55,6 +63,7 @@ function resetFilters() {
|
||||
selectedFranchise.value = null;
|
||||
selectedCollection.value = null;
|
||||
selectedCompany.value = null;
|
||||
selectedAgeRating.value = null;
|
||||
galleryFilterStore.disableFilterUnmatched();
|
||||
galleryFilterStore.disableFilterFavourites();
|
||||
nextTick(() => emitter?.emit("filter", null));
|
||||
|
||||
@@ -11,7 +11,6 @@ const emitter = inject<Emitter<Events>>("emitter");
|
||||
const galleryFilterStore = storeGalleryFilter();
|
||||
const { filterSearch } = storeToRefs(galleryFilterStore);
|
||||
|
||||
// Functions
|
||||
const filterRoms = debounce(() => {
|
||||
emitter?.emit("filter", null);
|
||||
}, 500);
|
||||
|
||||
@@ -69,7 +69,7 @@ function resetSelection() {
|
||||
async function addToFavourites() {
|
||||
if (!favCollection.value) return;
|
||||
favCollection.value.roms = favCollection.value.roms.concat(
|
||||
selectedRoms.value.map((r) => r.id)
|
||||
selectedRoms.value.map((r) => r.id),
|
||||
);
|
||||
await collectionApi
|
||||
.updateCollection({ collection: favCollection.value as Collection })
|
||||
@@ -98,7 +98,7 @@ async function addToFavourites() {
|
||||
async function removeFromFavourites() {
|
||||
if (!favCollection.value) return;
|
||||
favCollection.value.roms = favCollection.value.roms.filter(
|
||||
(value) => !selectedRoms.value.map((r) => r.id).includes(value)
|
||||
(value) => !selectedRoms.value.map((r) => r.id).includes(value),
|
||||
);
|
||||
if (romsStore.currentCollection?.name.toLowerCase() == "favourites") {
|
||||
romsStore.remove(selectedRoms.value);
|
||||
@@ -210,7 +210,7 @@ function onDownload() {
|
||||
? emitter?.emit('showAddToCollectionDialog', romsStore.selectedRoms)
|
||||
: emitter?.emit(
|
||||
'showRemoveFromCollectionDialog',
|
||||
romsStore.selectedRoms
|
||||
romsStore.selectedRoms,
|
||||
)
|
||||
"
|
||||
/>
|
||||
@@ -259,6 +259,6 @@ function onDownload() {
|
||||
pointer-events: none;
|
||||
}
|
||||
.sticky-bottom * {
|
||||
pointer-events: auto; /* Re-enables pointer events for all child elements */
|
||||
pointer-events: auto; /* Re-enables pointer events for all child elements */
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -42,13 +42,12 @@ emitter?.on(
|
||||
});
|
||||
fsSlugToCreate.value = fsSlug;
|
||||
selectedPlatform.value = supportedPlatforms.value?.find(
|
||||
(platform) => platform.slug == slug
|
||||
(platform) => platform.slug == slug,
|
||||
);
|
||||
show.value = true;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Functions
|
||||
function addBindPlatform() {
|
||||
if (!selectedPlatform.value) return;
|
||||
configApi
|
||||
@@ -60,7 +59,7 @@ function addBindPlatform() {
|
||||
if (selectedPlatform.value) {
|
||||
configStore.addPlatformBinding(
|
||||
fsSlugToCreate.value,
|
||||
selectedPlatform.value.slug
|
||||
selectedPlatform.value.slug,
|
||||
);
|
||||
}
|
||||
})
|
||||
|
||||
@@ -42,10 +42,10 @@ emitter?.on(
|
||||
});
|
||||
fsSlugToCreate.value = fsSlug;
|
||||
selectedPlatform.value = supportedPlatforms.value?.find(
|
||||
(platform) => platform.slug == slug
|
||||
(platform) => platform.slug == slug,
|
||||
);
|
||||
show.value = true;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Functions
|
||||
@@ -60,7 +60,7 @@ function addVersionPlatform() {
|
||||
if (selectedPlatform.value) {
|
||||
configStore.addPlatformBinding(
|
||||
fsSlugToCreate.value,
|
||||
selectedPlatform.value.slug
|
||||
selectedPlatform.value.slug,
|
||||
);
|
||||
}
|
||||
})
|
||||
|
||||
@@ -21,7 +21,6 @@ emitter?.on("showDeletePlatformBindingDialog", ({ fsSlug, slug }) => {
|
||||
show.value = true;
|
||||
});
|
||||
|
||||
// Functions
|
||||
function deleteBindPlatform() {
|
||||
configApi
|
||||
.deletePlatformBindConfig({ fsSlug: platformBindingFSSlugToDelete.value })
|
||||
@@ -74,10 +73,7 @@ function closeDialog() {
|
||||
<v-row class="justify-center mb-2" no-gutters>
|
||||
<v-btn-group divided density="compact">
|
||||
<v-btn class="bg-terciary" @click="closeDialog"> Cancel </v-btn>
|
||||
<v-btn
|
||||
class="bg-terciary text-romm-red"
|
||||
@click="deleteBindPlatform"
|
||||
>
|
||||
<v-btn class="bg-terciary text-romm-red" @click="deleteBindPlatform">
|
||||
Confirm
|
||||
</v-btn>
|
||||
</v-btn-group>
|
||||
|
||||
@@ -37,7 +37,6 @@ const itemsPerPage = ref(10);
|
||||
const pageCount = ref(0);
|
||||
const PER_PAGE_OPTIONS = [10, 25, 50, 100];
|
||||
|
||||
// Functions
|
||||
async function addRomsToCollection() {
|
||||
if (!selectedCollection.value) return;
|
||||
selectedCollection.value.roms.push(...roms.value.map((r) => r.id));
|
||||
|
||||
@@ -30,7 +30,6 @@ emitter?.on("updateUrlCover", (url_cover) => {
|
||||
imagePreviewUrl.value = url_cover;
|
||||
});
|
||||
|
||||
// Functions
|
||||
function triggerFileInput() {
|
||||
const fileInput = document.getElementById("file-input");
|
||||
fileInput?.click();
|
||||
@@ -146,13 +145,15 @@ function closeDialog() {
|
||||
<template #append-inner>
|
||||
<v-btn-group rounded="0" divided density="compact">
|
||||
<v-btn
|
||||
:disabled="!heartbeat.value.METADATA_SOURCES?.STEAMGRIDDB_ENABLED"
|
||||
:disabled="
|
||||
!heartbeat.value.METADATA_SOURCES?.STEAMGRIDDB_ENABLED
|
||||
"
|
||||
size="small"
|
||||
class="translucent-dark"
|
||||
@click="
|
||||
emitter?.emit(
|
||||
'showSearchCoverDialog',
|
||||
collection.name as string
|
||||
collection.name as string,
|
||||
)
|
||||
"
|
||||
>
|
||||
|
||||
@@ -30,7 +30,6 @@ emitter?.on("updateUrlCover", (url_cover) => {
|
||||
imagePreviewUrl.value = url_cover;
|
||||
});
|
||||
|
||||
// Functions
|
||||
function triggerFileInput() {
|
||||
const fileInput = document.getElementById("file-input");
|
||||
fileInput?.click();
|
||||
@@ -134,7 +133,11 @@ function closeDialog() {
|
||||
<v-col>
|
||||
<v-switch
|
||||
v-model="collection.is_public"
|
||||
:label="collection.is_public ? 'Public (visible to everyone)' : 'Private (only visible to me)'"
|
||||
:label="
|
||||
collection.is_public
|
||||
? 'Public (visible to everyone)'
|
||||
: 'Private (only visible to me)'
|
||||
"
|
||||
color="romm-accent-1"
|
||||
class="px-2"
|
||||
hide-details
|
||||
@@ -155,13 +158,15 @@ function closeDialog() {
|
||||
<template #append-inner>
|
||||
<v-btn-group rounded="0" divided density="compact">
|
||||
<v-btn
|
||||
:disabled="!heartbeat.value.METADATA_SOURCES?.STEAMGRIDDB_ENABLED"
|
||||
:disabled="
|
||||
!heartbeat.value.METADATA_SOURCES?.STEAMGRIDDB_ENABLED
|
||||
"
|
||||
size="small"
|
||||
class="translucent-dark"
|
||||
@click="
|
||||
emitter?.emit(
|
||||
'showSearchCoverDialog',
|
||||
collection.name as string
|
||||
collection.name as string,
|
||||
)
|
||||
"
|
||||
>
|
||||
|
||||
@@ -36,11 +36,10 @@ const itemsPerPage = ref(10);
|
||||
const pageCount = ref(0);
|
||||
const PER_PAGE_OPTIONS = [10, 25, 50, 100];
|
||||
|
||||
// Functions
|
||||
async function removeRomsFromCollection() {
|
||||
if (!selectedCollection.value) return;
|
||||
selectedCollection.value.roms = selectedCollection.value.roms.filter(
|
||||
(id) => !roms.value.map((r) => r.id).includes(id)
|
||||
(id) => !roms.value.map((r) => r.id).includes(id),
|
||||
);
|
||||
await collectionApi
|
||||
.updateCollection({ collection: selectedCollection.value })
|
||||
|
||||
@@ -21,7 +21,6 @@ const collectionsStore = storeCollections();
|
||||
const romsStore = storeRoms();
|
||||
const { favCollection } = storeToRefs(collectionsStore);
|
||||
|
||||
// Functions
|
||||
async function switchFromFavourites() {
|
||||
if (!favCollection.value) {
|
||||
await collectionApi
|
||||
@@ -53,7 +52,7 @@ async function switchFromFavourites() {
|
||||
} else {
|
||||
if (favCollection.value) {
|
||||
favCollection.value.roms = favCollection.value.roms.filter(
|
||||
(id) => id !== props.rom.id
|
||||
(id) => id !== props.rom.id,
|
||||
);
|
||||
if (romsStore.currentCollection?.name.toLowerCase() == "favourites") {
|
||||
romsStore.remove([props.rom]);
|
||||
|
||||
@@ -34,7 +34,6 @@ const itemsPerPage = ref(10);
|
||||
const pageCount = ref(0);
|
||||
const PER_PAGE_OPTIONS = [10, 25, 50, 100];
|
||||
|
||||
// Functions
|
||||
function triggerFileInput() {
|
||||
const fileInput = document.getElementById("file-input");
|
||||
fileInput?.click();
|
||||
|
||||
@@ -34,7 +34,6 @@ const itemsPerPage = ref(10);
|
||||
const pageCount = ref(0);
|
||||
const PER_PAGE_OPTIONS = [10, 25, 50, 100];
|
||||
|
||||
// Functions
|
||||
function triggerFileInput() {
|
||||
const fileInput = document.getElementById("file-input");
|
||||
fileInput?.click();
|
||||
|
||||
@@ -15,7 +15,6 @@ emitter?.on("showCopyDownloadLinkDialog", (downloadLink) => {
|
||||
link.value = downloadLink;
|
||||
});
|
||||
|
||||
// Functions
|
||||
function closeDialog() {
|
||||
show.value = false;
|
||||
link.value = "";
|
||||
|
||||
@@ -40,7 +40,6 @@ const itemsPerPage = ref(10);
|
||||
const pageCount = ref(0);
|
||||
const PER_PAGE_OPTIONS = [10, 25, 50, 100];
|
||||
|
||||
// Functions
|
||||
async function deleteRoms() {
|
||||
await romApi
|
||||
.deleteRoms({ roms: roms.value, deleteFromFs: romsToDeleteFromFs.value })
|
||||
|
||||
@@ -135,7 +135,7 @@ function confirm() {
|
||||
updateRom(
|
||||
Object.assign(selectedMatchRom.value, {
|
||||
url_cover: selectedCover.value.url_cover,
|
||||
})
|
||||
}),
|
||||
);
|
||||
closeDialog();
|
||||
}
|
||||
|
||||
@@ -36,14 +36,13 @@ emitter?.on("showSearchRomDialog", () => {
|
||||
show.value = true;
|
||||
});
|
||||
|
||||
// Functions
|
||||
async function filterRoms() {
|
||||
if (!selectedPlatform.value) {
|
||||
filteredRoms.value = searchedRoms.value as SimpleRom[];
|
||||
} else {
|
||||
filteredRoms.value = searchedRoms.value.filter(
|
||||
(rom: { platform_name: string }) =>
|
||||
rom.platform_name == selectedPlatform.value?.platform_name
|
||||
rom.platform_name == selectedPlatform.value?.platform_name,
|
||||
) as SimpleRom[];
|
||||
}
|
||||
}
|
||||
@@ -73,7 +72,7 @@ async function searchRoms() {
|
||||
platform_name: rom.platform_name,
|
||||
platform_slug: rom.platform_slug,
|
||||
},
|
||||
])
|
||||
]),
|
||||
).values(),
|
||||
];
|
||||
filterRoms();
|
||||
|
||||
@@ -62,7 +62,6 @@ emitter?.on("showUploadRomDialog", (platformWhereUpload) => {
|
||||
});
|
||||
});
|
||||
|
||||
// Functions
|
||||
async function uploadRoms() {
|
||||
if (!selectedPlatform.value) return;
|
||||
show.value = false;
|
||||
@@ -104,7 +103,7 @@ async function uploadRoms() {
|
||||
uploadStore.clear();
|
||||
|
||||
const successfulUploads = responses.filter(
|
||||
(d) => d.status == "fulfilled"
|
||||
(d) => d.status == "fulfilled",
|
||||
);
|
||||
const failedUploads = responses.filter((d) => d.status == "rejected");
|
||||
|
||||
@@ -156,7 +155,7 @@ function triggerFileInput() {
|
||||
|
||||
function removeRomFromList(romName: string) {
|
||||
filesToUpload.value = filesToUpload.value.filter(
|
||||
(rom) => rom.name !== romName
|
||||
(rom) => rom.name !== romName,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ async function switchFromFavourites() {
|
||||
} else {
|
||||
if (favCollection.value) {
|
||||
favCollection.value.roms = favCollection.value.roms.filter(
|
||||
(id) => id !== props.rom.id
|
||||
(id) => id !== props.rom.id,
|
||||
);
|
||||
if (romsStore.currentCollection?.name.toLowerCase() == "favourites") {
|
||||
romsStore.remove([props.rom]);
|
||||
|
||||
@@ -75,14 +75,13 @@ const HEADERS = [
|
||||
{ title: "", align: "end", key: "actions", sortable: false },
|
||||
] as const;
|
||||
|
||||
// Functions
|
||||
function rowClick(_: Event, row: { item: SimpleRom }) {
|
||||
router.push({ name: "rom", params: { rom: row.item.id } });
|
||||
}
|
||||
|
||||
function updateDataTablePages() {
|
||||
pageCount.value = Math.ceil(
|
||||
romsStore.filteredRoms.length / itemsPerPage.value
|
||||
romsStore.filteredRoms.length / itemsPerPage.value,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -137,7 +136,11 @@ onMounted(() => {
|
||||
>
|
||||
<template #append>
|
||||
<v-chip
|
||||
v-if="item.sibling_roms && item.sibling_roms.length > 0 && showSiblings"
|
||||
v-if="
|
||||
item.sibling_roms &&
|
||||
item.sibling_roms.length > 0 &&
|
||||
showSiblings
|
||||
"
|
||||
class="translucent-dark ml-2"
|
||||
size="x-small"
|
||||
>
|
||||
|
||||
@@ -16,7 +16,6 @@ const { filteredCollections, searchText } = storeToRefs(collectionsStore);
|
||||
const { activeCollectionsDrawer } = storeToRefs(navigationStore);
|
||||
const emitter = inject<Emitter<Events>>("emitter");
|
||||
|
||||
// Functions
|
||||
async function addCollection() {
|
||||
emitter?.emit("showCreateCollectionDialog", null);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ const platformsStore = storePlatforms();
|
||||
const { filteredPlatforms, searchText } = storeToRefs(platformsStore);
|
||||
const { activePlatformsDrawer } = storeToRefs(navigationStore);
|
||||
|
||||
// Functions
|
||||
function clear() {
|
||||
searchText.value = "";
|
||||
}
|
||||
|
||||
@@ -11,14 +11,13 @@ const { VERSION } = heartbeat.value;
|
||||
const GITHUB_VERSION = ref(VERSION);
|
||||
const latestVersionDismissed = ref(VERSION === "development");
|
||||
|
||||
// Functions
|
||||
function dismissVersionBanner() {
|
||||
localStorage.setItem("dismissedVersion", GITHUB_VERSION.value);
|
||||
latestVersionDismissed.value = true;
|
||||
}
|
||||
onMounted(async () => {
|
||||
const response = await fetch(
|
||||
"https://api.github.com/repos/rommapp/romm/releases/latest"
|
||||
"https://api.github.com/repos/rommapp/romm/releases/latest",
|
||||
);
|
||||
const json = await response.json();
|
||||
GITHUB_VERSION.value = json.tag_name;
|
||||
@@ -72,6 +71,6 @@ onMounted(async () => {
|
||||
pointer-events: none;
|
||||
}
|
||||
.sticky-bottom * {
|
||||
pointer-events: auto; /* Re-enables pointer events for all child elements */
|
||||
pointer-events: auto; /* Re-enables pointer events for all child elements */
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -35,7 +35,6 @@ const itemsPerPage = ref(10);
|
||||
const pageCount = ref(0);
|
||||
const PER_PAGE_OPTIONS = [10, 25, 50, 100];
|
||||
|
||||
// Functions
|
||||
function triggerFileInput() {
|
||||
const fileInput = document.getElementById("file-input");
|
||||
fileInput?.click();
|
||||
|
||||
@@ -29,7 +29,7 @@ withDefaults(
|
||||
icon: null,
|
||||
width: "",
|
||||
height: "",
|
||||
}
|
||||
},
|
||||
);
|
||||
const emit = defineEmits(["update:modelValue", "close"]);
|
||||
const hasToolbarSlot = ref(false);
|
||||
@@ -37,7 +37,6 @@ const hasPrependSlot = ref(false);
|
||||
const hasAppendSlot = ref(false);
|
||||
const hasFooterSlot = ref(false);
|
||||
|
||||
// Functions
|
||||
function closeDialog() {
|
||||
emit("update:modelValue", false);
|
||||
emit("close");
|
||||
|
||||
@@ -23,7 +23,6 @@ emitter?.on("showSearchCoverDialog", (term) => {
|
||||
if (searchTerm.value) searchCovers();
|
||||
});
|
||||
|
||||
// Functions
|
||||
async function searchCovers() {
|
||||
covers.value = [];
|
||||
|
||||
@@ -47,7 +46,7 @@ async function searchCovers() {
|
||||
coverType.value === "all"
|
||||
? game.resources
|
||||
: game.resources.filter(
|
||||
(resource) => resource.type === coverType.value
|
||||
(resource) => resource.type === coverType.value,
|
||||
),
|
||||
};
|
||||
})
|
||||
@@ -81,7 +80,7 @@ function filterCovers() {
|
||||
coverType.value === "all"
|
||||
? game.resources
|
||||
: game.resources.filter(
|
||||
(resource) => resource.type === coverType.value
|
||||
(resource) => resource.type === coverType.value,
|
||||
),
|
||||
};
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useRouter } from "vue-router";
|
||||
|
||||
// Props
|
||||
const romsStore = storeRoms();
|
||||
const { recentRoms } = storeToRefs(romsStore)
|
||||
const { recentRoms } = storeToRefs(romsStore);
|
||||
const router = useRouter();
|
||||
|
||||
// Functions
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import { normalizeString } from "@/utils";
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
const filters = ["genres", "franchises", "collections", "companies"] as const;
|
||||
const filters = [
|
||||
"genres",
|
||||
"franchises",
|
||||
"collections",
|
||||
"companies",
|
||||
"age_ratings",
|
||||
] as const;
|
||||
|
||||
export type FilterType = (typeof filters)[number];
|
||||
|
||||
@@ -14,6 +20,7 @@ export default defineStore("galleryFilter", {
|
||||
filterFranchises: [] as string[],
|
||||
filterCollections: [] as string[],
|
||||
filterCompanies: [] as string[],
|
||||
filterAgeRatings: [] as string[],
|
||||
filterUnmatched: false,
|
||||
filterFavourites: false,
|
||||
filterDuplicates: false,
|
||||
@@ -21,6 +28,7 @@ export default defineStore("galleryFilter", {
|
||||
selectedFranchise: null as string | null,
|
||||
selectedCollection: null as string | null,
|
||||
selectedCompany: null as string | null,
|
||||
selectedAgeRating: null as string | null,
|
||||
}),
|
||||
|
||||
actions: {
|
||||
@@ -42,6 +50,9 @@ export default defineStore("galleryFilter", {
|
||||
setFilterCompanies(companies: string[]) {
|
||||
this.filterCompanies = companies;
|
||||
},
|
||||
setFilterAgeRatings(ageRatings: string[]) {
|
||||
this.filterAgeRatings = ageRatings;
|
||||
},
|
||||
setSelectedFilterGenre(genre: string) {
|
||||
this.selectedGenre = genre;
|
||||
},
|
||||
@@ -54,6 +65,9 @@ export default defineStore("galleryFilter", {
|
||||
setSelectedFilterCompany(company: string) {
|
||||
this.selectedCompany = company;
|
||||
},
|
||||
setSelectedFilterAgeRating(ageRating: string) {
|
||||
this.selectedAgeRating = ageRating;
|
||||
},
|
||||
switchFilterUnmatched() {
|
||||
this.filterUnmatched = !this.filterUnmatched;
|
||||
},
|
||||
@@ -81,7 +95,8 @@ export default defineStore("galleryFilter", {
|
||||
this.selectedGenre ||
|
||||
this.selectedFranchise ||
|
||||
this.selectedCollection ||
|
||||
this.selectedCompany,
|
||||
this.selectedCompany ||
|
||||
this.selectedAgeRating,
|
||||
);
|
||||
},
|
||||
reset() {
|
||||
@@ -93,6 +108,7 @@ export default defineStore("galleryFilter", {
|
||||
this.selectedFranchise = null;
|
||||
this.selectedCollection = null;
|
||||
this.selectedCompany = null;
|
||||
this.selectedAgeRating = null;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -155,6 +155,9 @@ export default defineStore("roms", {
|
||||
if (galleryFilter.selectedCompany) {
|
||||
this._filterCompany(galleryFilter.selectedCompany);
|
||||
}
|
||||
if (galleryFilter.selectedAgeRating) {
|
||||
this._filterAgeRating(galleryFilter.selectedAgeRating);
|
||||
}
|
||||
},
|
||||
_filterSearch(searchFilter: string) {
|
||||
this._filteredIDs = this.filteredRoms
|
||||
@@ -208,6 +211,13 @@ export default defineStore("roms", {
|
||||
)
|
||||
.map((rom) => rom.id);
|
||||
},
|
||||
_filterAgeRating(ageRatingToFilter: string) {
|
||||
this._filteredIDs = this.filteredRoms
|
||||
.filter((rom) =>
|
||||
rom.age_ratings.some((ageRating) => ageRating === ageRatingToFilter),
|
||||
)
|
||||
.map((rom) => rom.id);
|
||||
},
|
||||
// Selected roms
|
||||
setSelection(roms: SimpleRom[]) {
|
||||
this._selectedIDs = roms.map((rom) => rom.id);
|
||||
|
||||
@@ -27,7 +27,6 @@ const script = document.createElement("script");
|
||||
script.src = "/assets/emulatorjs/loader.js";
|
||||
script.async = true;
|
||||
|
||||
// Functions
|
||||
function onPlay() {
|
||||
window.EJS_fullscreenOnLoaded = fullScreenOnPlay.value;
|
||||
document.body.appendChild(script);
|
||||
@@ -68,12 +67,12 @@ onMounted(async () => {
|
||||
} else if (rom.value.user_states) {
|
||||
// Otherwise auto select most recent state by last updated date
|
||||
stateRef.value = rom.value.user_states?.sort((a, b) =>
|
||||
b.updated_at.localeCompare(a.updated_at)
|
||||
b.updated_at.localeCompare(a.updated_at),
|
||||
)[0];
|
||||
}
|
||||
|
||||
const storedBiosID = localStorage.getItem(
|
||||
`player:${rom.value.platform_slug}:bios_id`
|
||||
`player:${rom.value.platform_slug}:bios_id`,
|
||||
);
|
||||
if (storedBiosID) {
|
||||
biosRef.value =
|
||||
@@ -82,7 +81,7 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
const storedCore = localStorage.getItem(
|
||||
`player:${rom.value.platform_slug}:core`
|
||||
`player:${rom.value.platform_slug}:core`,
|
||||
);
|
||||
if (storedCore) {
|
||||
coreRef.value = storedCore;
|
||||
|
||||
@@ -69,7 +69,7 @@ async function fetchRoms() {
|
||||
timeout: 4000,
|
||||
});
|
||||
console.error(
|
||||
`Couldn't fetch roms for collection ID ${currentCollection.value?.id}: ${error}`
|
||||
`Couldn't fetch roms for collection ID ${currentCollection.value?.id}: ${error}`,
|
||||
);
|
||||
noCollectionError.value = true;
|
||||
})
|
||||
@@ -87,28 +87,35 @@ function setFilters() {
|
||||
...new Set(
|
||||
romsStore.filteredRoms
|
||||
.flatMap((rom) => rom.genres.map((genre) => genre))
|
||||
.sort()
|
||||
.sort(),
|
||||
),
|
||||
]);
|
||||
galleryFilterStore.setFilterFranchises([
|
||||
...new Set(
|
||||
romsStore.filteredRoms
|
||||
.flatMap((rom) => rom.franchises.map((franchise) => franchise))
|
||||
.sort()
|
||||
.sort(),
|
||||
),
|
||||
]);
|
||||
galleryFilterStore.setFilterCompanies([
|
||||
...new Set(
|
||||
romsStore.filteredRoms
|
||||
.flatMap((rom) => rom.companies.map((company) => company))
|
||||
.sort()
|
||||
.sort(),
|
||||
),
|
||||
]);
|
||||
galleryFilterStore.setFilterCollections([
|
||||
...new Set(
|
||||
romsStore.filteredRoms
|
||||
.flatMap((rom) => rom.collections.map((collection) => collection))
|
||||
.sort()
|
||||
.sort(),
|
||||
),
|
||||
]);
|
||||
galleryFilterStore.setFilterAgeRatings([
|
||||
...new Set(
|
||||
romsStore.filteredRoms
|
||||
.flatMap((rom) => rom.age_ratings.map((ageRating) => ageRating))
|
||||
.sort(),
|
||||
),
|
||||
]);
|
||||
}
|
||||
@@ -135,7 +142,7 @@ function onGameClick(emitData: { rom: SimpleRom; event: MouseEvent }) {
|
||||
}
|
||||
if (emitData.event.shiftKey) {
|
||||
const [start, end] = [romsStore.lastSelectedIndex, index].sort(
|
||||
(a, b) => a - b
|
||||
(a, b) => a - b,
|
||||
);
|
||||
if (romsStore.selectedRoms.includes(emitData.rom)) {
|
||||
for (let i = start + 1; i < end; i++) {
|
||||
@@ -147,7 +154,7 @@ function onGameClick(emitData: { rom: SimpleRom; event: MouseEvent }) {
|
||||
}
|
||||
}
|
||||
romsStore.updateLastSelected(
|
||||
romsStore.selectedRoms.includes(emitData.rom) ? index : index - 1
|
||||
romsStore.selectedRoms.includes(emitData.rom) ? index : index - 1,
|
||||
);
|
||||
} else {
|
||||
romsStore.updateLastSelected(index);
|
||||
|
||||
@@ -42,7 +42,6 @@ let timeout: ReturnType<typeof setTimeout>;
|
||||
const emitter = inject<Emitter<Events>>("emitter");
|
||||
emitter?.on("filter", onFilterChange);
|
||||
|
||||
// Functions
|
||||
async function fetchRoms() {
|
||||
if (gettingRoms.value) return;
|
||||
|
||||
@@ -69,7 +68,7 @@ async function fetchRoms() {
|
||||
timeout: 4000,
|
||||
});
|
||||
console.error(
|
||||
`Couldn't fetch roms for platform ID ${currentPlatform.value?.id}: ${error}`
|
||||
`Couldn't fetch roms for platform ID ${currentPlatform.value?.id}: ${error}`,
|
||||
);
|
||||
noPlatformError.value = true;
|
||||
})
|
||||
@@ -87,28 +86,35 @@ function setFilters() {
|
||||
...new Set(
|
||||
romsStore.filteredRoms
|
||||
.flatMap((rom) => rom.genres.map((genre) => genre))
|
||||
.sort()
|
||||
.sort(),
|
||||
),
|
||||
]);
|
||||
galleryFilterStore.setFilterFranchises([
|
||||
...new Set(
|
||||
romsStore.filteredRoms
|
||||
.flatMap((rom) => rom.franchises.map((franchise) => franchise))
|
||||
.sort()
|
||||
.sort(),
|
||||
),
|
||||
]);
|
||||
galleryFilterStore.setFilterCompanies([
|
||||
...new Set(
|
||||
romsStore.filteredRoms
|
||||
.flatMap((rom) => rom.companies.map((company) => company))
|
||||
.sort()
|
||||
.sort(),
|
||||
),
|
||||
]);
|
||||
galleryFilterStore.setFilterCollections([
|
||||
...new Set(
|
||||
romsStore.filteredRoms
|
||||
.flatMap((rom) => rom.collections.map((collection) => collection))
|
||||
.sort()
|
||||
.sort(),
|
||||
),
|
||||
]);
|
||||
galleryFilterStore.setFilterAgeRatings([
|
||||
...new Set(
|
||||
romsStore.filteredRoms
|
||||
.flatMap((rom) => rom.age_ratings.map((ageRating) => ageRating))
|
||||
.sort(),
|
||||
),
|
||||
]);
|
||||
}
|
||||
@@ -135,7 +141,7 @@ function onGameClick(emitData: { rom: SimpleRom; event: MouseEvent }) {
|
||||
}
|
||||
if (emitData.event.shiftKey) {
|
||||
const [start, end] = [romsStore.lastSelectedIndex, index].sort(
|
||||
(a, b) => a - b
|
||||
(a, b) => a - b,
|
||||
);
|
||||
if (romsStore.selectedRoms.includes(emitData.rom)) {
|
||||
for (let i = start + 1; i < end; i++) {
|
||||
@@ -147,7 +153,7 @@ function onGameClick(emitData: { rom: SimpleRom; event: MouseEvent }) {
|
||||
}
|
||||
}
|
||||
romsStore.updateLastSelected(
|
||||
romsStore.selectedRoms.includes(emitData.rom) ? index : index - 1
|
||||
romsStore.selectedRoms.includes(emitData.rom) ? index : index - 1,
|
||||
);
|
||||
} else {
|
||||
romsStore.updateLastSelected(index);
|
||||
@@ -201,6 +207,7 @@ const filterToSetFilter: Record<FilterType, Function> = {
|
||||
franchises: galleryFilterStore.setSelectedFilterFranchise,
|
||||
collections: galleryFilterStore.setSelectedFilterCollection,
|
||||
companies: galleryFilterStore.setSelectedFilterCompany,
|
||||
age_ratings: galleryFilterStore.setSelectedFilterAgeRating,
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
|
||||
@@ -41,7 +41,6 @@ const noRomError = ref(false);
|
||||
const romsStore = storeRoms();
|
||||
const { currentRom } = storeToRefs(romsStore);
|
||||
|
||||
// Functions
|
||||
async function fetchDetails() {
|
||||
await romApi
|
||||
.getRom({ romId: parseInt(route.params.rom as string) })
|
||||
@@ -91,7 +90,7 @@ watch(
|
||||
() => route.fullPath,
|
||||
async () => {
|
||||
await fetchDetails();
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
|
||||
@@ -52,7 +52,6 @@ emitter?.on("refreshDrawer", async () => {
|
||||
platformsStore.set(platformData);
|
||||
});
|
||||
|
||||
// Functions
|
||||
onBeforeMount(async () => {
|
||||
await platformApi
|
||||
.getPlatforms()
|
||||
@@ -68,8 +67,8 @@ onBeforeMount(async () => {
|
||||
collectionsStore.set(collections);
|
||||
collectionsStore.setFavCollection(
|
||||
collections.find(
|
||||
(collection) => collection.name.toLowerCase() === "favourites"
|
||||
)
|
||||
(collection) => collection.name.toLowerCase() === "favourites",
|
||||
),
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
@@ -43,12 +43,11 @@ const step = ref(1);
|
||||
const filledAdminUser = computed(
|
||||
() =>
|
||||
defaultAdminUser.value.username != "" &&
|
||||
defaultAdminUser.value.password != ""
|
||||
defaultAdminUser.value.password != "",
|
||||
);
|
||||
const isFirstStep = computed(() => step.value == 1);
|
||||
const isLastStep = computed(() => step.value == 2);
|
||||
|
||||
// Functions
|
||||
async function finishWizard() {
|
||||
await userApi
|
||||
.createUser(defaultAdminUser.value)
|
||||
@@ -189,14 +188,20 @@ async function finishWizard() {
|
||||
|
||||
<v-stepper-actions :disabled="!filledAdminUser">
|
||||
<template #prev>
|
||||
<v-btn class="text-white text-shadow" :ripple="false" :disabled="isFirstStep" @click="prev">{{
|
||||
isFirstStep ? "" : "previous"
|
||||
}}</v-btn>
|
||||
<v-btn
|
||||
class="text-white text-shadow"
|
||||
:ripple="false"
|
||||
:disabled="isFirstStep"
|
||||
@click="prev"
|
||||
>{{ isFirstStep ? "" : "previous" }}</v-btn
|
||||
>
|
||||
</template>
|
||||
<template #next>
|
||||
<v-btn class="text-white text-shadow" @click="!isLastStep ? next() : finishWizard()">{{
|
||||
!isLastStep ? "Next" : "Finish"
|
||||
}}</v-btn>
|
||||
<v-btn
|
||||
class="text-white text-shadow"
|
||||
@click="!isLastStep ? next() : finishWizard()"
|
||||
>{{ !isLastStep ? "Next" : "Finish" }}</v-btn
|
||||
>
|
||||
</template>
|
||||
</v-stepper-actions>
|
||||
</template>
|
||||
@@ -223,7 +228,9 @@ async function finishWizard() {
|
||||
max-width: 300px;
|
||||
}
|
||||
#version {
|
||||
text-shadow: 1px 1px 1px #000000, 0 0 1px #000000;
|
||||
text-shadow:
|
||||
1px 1px 1px #000000,
|
||||
0 0 1px #000000;
|
||||
bottom: 0.3rem;
|
||||
right: 0.5rem;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user