Merge pull request #2564 from rommapp/favorites-collection

Explicit favorites collection in model
This commit is contained in:
Georges-Antoine Assi
2025-10-18 14:36:43 -04:00
committed by GitHub
52 changed files with 190 additions and 139 deletions

View File

@@ -0,0 +1,42 @@
"""empty message
Revision ID: 0055_collection_is_favorite
Revises: 0054_add_platform_metadata_slugs
Create Date: 2025-10-18 13:24:15.119652
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "0055_collection_is_favorite"
down_revision = "0054_add_platform_metadata_slugs"
branch_labels = None
depends_on = None
def upgrade() -> None:
with op.batch_alter_table("collections", schema=None) as batch_op:
batch_op.add_column(sa.Column("is_favorite", sa.Boolean(), nullable=False))
# Find favorite collection and set is_favorite to True
from handler.database import db_collection_handler, db_user_handler
users = db_user_handler.get_users()
for user in users:
collection = db_collection_handler.get_collection_by_name("favourites", user.id)
if not collection:
collection = db_collection_handler.get_collection_by_name(
"favorites", user.id
)
if collection:
db_collection_handler.update_collection(
collection.id, {"is_favorite": True}
)
def downgrade() -> None:
with op.batch_alter_table("collections", schema=None) as batch_op:
batch_op.drop_column("is_favorite")

View File

@@ -53,6 +53,7 @@ async def add_collection(
"description": data.get("description", ""),
"url_cover": data.get("url_cover", ""),
"is_public": data.get("is_public", False),
"is_favorite": data.get("is_favorite", False),
"user_id": request.user.id,
}
db_collection = db_collection_handler.get_collection_by_name(

View File

@@ -196,9 +196,9 @@ def get_roms(
bool | None,
Query(description="Whether the rom matched a metadata source."),
] = None,
favourite: Annotated[
favorite: Annotated[
bool | None,
Query(description="Whether the rom is marked as favourite."),
Query(description="Whether the rom is marked as favorite."),
] = None,
duplicate: Annotated[
bool | None,
@@ -288,7 +288,7 @@ def get_roms(
smart_collection_id=smart_collection_id,
search_term=search_term,
matched=matched,
favourite=favourite,
favorite=favorite,
duplicate=duplicate,
playable=playable,
has_ra=has_ra,

View File

@@ -59,6 +59,15 @@ class DBCollectionsHandler(DBBaseHandler):
) -> Collection | None:
return session.scalar(query.filter_by(name=name, user_id=user_id).limit(1))
@begin_session
@with_roms
def get_favorite_collection(
self, user_id: int, query: Query = None, session: Session = None
) -> Collection | None:
return session.scalar(
query.filter_by(is_favorite=True, user_id=user_id).limit(1)
)
@begin_session
@with_roms
def get_collections(
@@ -211,7 +220,7 @@ class DBCollectionsHandler(DBBaseHandler):
virtual_collection_id=criteria.get("virtual_collection_id"),
search_term=criteria.get("search_term"),
matched=criteria.get("matched"),
favourite=criteria.get("favourite"),
favorite=criteria.get("favorite"),
duplicate=criteria.get("duplicate"),
playable=criteria.get("playable"),
has_ra=criteria.get("has_ra"),

View File

@@ -245,27 +245,24 @@ class DBRomsHandler(DBBaseHandler):
predicate = not_(predicate)
return query.filter(predicate)
def filter_by_favourite(
def filter_by_favorite(
self, query: Query, session: Session, value: bool, user_id: int | None
) -> Query:
"""Filter based on whether the rom is in the user's Favourites collection."""
"""Filter based on whether the rom is in the user's favorites collection."""
if not user_id:
return query
from . import db_collection_handler
favourites_collection = db_collection_handler.get_collection_by_name(
"favourites", user_id
)
if favourites_collection:
predicate = Rom.id.in_(favourites_collection.rom_ids)
favorites_collection = db_collection_handler.get_favorite_collection(user_id)
if favorites_collection:
predicate = Rom.id.in_(favorites_collection.rom_ids)
if not value:
predicate = not_(predicate)
return query.filter(predicate)
# If no Favourites collection exists, return the original query if non-favourites
# were requested, or an empty query if favourites were requested.
# If no favorites collection exists, return the original query if non-favorites
# were requested, or an empty query if favorites were requested.
if not value:
return query
return query.filter(false())
@@ -377,7 +374,7 @@ class DBRomsHandler(DBBaseHandler):
smart_collection_id: int | None = None,
search_term: str | None = None,
matched: bool | None = None,
favourite: bool | None = None,
favorite: bool | None = None,
duplicate: bool | None = None,
playable: bool | None = None,
has_ra: bool | None = None,
@@ -419,9 +416,9 @@ class DBRomsHandler(DBBaseHandler):
if matched is not None:
query = self.filter_by_matched(query, value=matched)
if favourite is not None:
query = self.filter_by_favourite(
query, session=session, value=favourite, user_id=user_id
if favorite is not None:
query = self.filter_by_favorite(
query, session=session, value=favorite, user_id=user_id
)
if duplicate is not None:
@@ -651,7 +648,7 @@ class DBRomsHandler(DBBaseHandler):
virtual_collection_id=kwargs.get("virtual_collection_id", None),
search_term=kwargs.get("search_term", None),
matched=kwargs.get("matched", None),
favourite=kwargs.get("favourite", None),
favorite=kwargs.get("favorite", None),
duplicate=kwargs.get("duplicate", None),
playable=kwargs.get("playable", None),
has_ra=kwargs.get("has_ra", None),

View File

@@ -24,6 +24,7 @@ class Collection(BaseModel):
name: Mapped[str] = mapped_column(String(length=400))
description: Mapped[str | None] = mapped_column(Text)
is_public: Mapped[bool] = mapped_column(default=False)
is_favorite: Mapped[bool] = mapped_column(default=False)
path_cover_l: Mapped[str | None] = mapped_column(Text, default="")
path_cover_s: Mapped[str | None] = mapped_column(Text, default="")
url_cover: Mapped[str | None] = mapped_column(
@@ -89,10 +90,6 @@ class Collection(BaseModel):
if r.path_cover_l
]
@property
def is_favorite(self) -> bool:
return self.name.lower() == "favourites"
def __repr__(self) -> str:
return self.name

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from sqlalchemy import String, func, select
from sqlalchemy import Integer, String, func, select
from sqlalchemy.orm import Mapped, column_property, mapped_column, relationship
from models.base import BaseModel
@@ -19,18 +19,18 @@ class Platform(BaseModel):
__tablename__ = "platforms"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
igdb_id: Mapped[int | None]
sgdb_id: Mapped[int | None]
moby_id: Mapped[int | None]
ss_id: Mapped[int | None]
ra_id: Mapped[int | None]
launchbox_id: Mapped[int | None]
hasheous_id: Mapped[int | None]
tgdb_id: Mapped[int | None]
flashpoint_id: Mapped[int | None]
igdb_slug: Mapped[str | None]
moby_slug: Mapped[str | None]
hltb_slug: Mapped[str | None]
igdb_id: Mapped[int | None] = mapped_column(Integer(), default=None)
sgdb_id: Mapped[int | None] = mapped_column(Integer(), default=None)
moby_id: Mapped[int | None] = mapped_column(Integer(), default=None)
ss_id: Mapped[int | None] = mapped_column(Integer(), default=None)
ra_id: Mapped[int | None] = mapped_column(Integer(), default=None)
launchbox_id: Mapped[int | None] = mapped_column(Integer(), default=None)
hasheous_id: Mapped[int | None] = mapped_column(Integer(), default=None)
tgdb_id: Mapped[int | None] = mapped_column(Integer(), default=None)
flashpoint_id: Mapped[int | None] = mapped_column(Integer(), default=None)
igdb_slug: Mapped[str | None] = mapped_column(String(length=100), default=None)
moby_slug: Mapped[str | None] = mapped_column(String(length=100), default=None)
hltb_slug: Mapped[str | None] = mapped_column(String(length=100), default=None)
slug: Mapped[str] = mapped_column(String(length=100))
fs_slug: Mapped[str] = mapped_column(String(length=100))
name: Mapped[str] = mapped_column(String(length=400))

View File

@@ -26,7 +26,7 @@ const {
searchTerm,
filterUnmatched,
filterMatched,
filterFavourites,
filterFavorites,
filterDuplicates,
filterPlayables,
filterRA,
@@ -57,7 +57,7 @@ async function goToRandomGame() {
: null,
filterUnmatched: filterUnmatched.value,
filterMatched: filterMatched.value,
filterFavourites: filterFavourites.value,
filterFavorites: filterFavorites.value,
filterDuplicates: filterDuplicates.value,
filterPlayables: filterPlayables.value,
filterRA: filterRA.value,

View File

@@ -8,7 +8,7 @@ import { useRouter } from "vue-router";
import { useDisplay } from "vuetify";
import SearchTextField from "@/components/Gallery/AppBar/Search/SearchTextField.vue";
import FilterDuplicatesBtn from "@/components/Gallery/AppBar/common/FilterDrawer/FilterDuplicatesBtn.vue";
import FilterFavouritesBtn from "@/components/Gallery/AppBar/common/FilterDrawer/FilterFavouritesBtn.vue";
import FilterFavoritesBtn from "@/components/Gallery/AppBar/common/FilterDrawer/FilterFavoritesBtn.vue";
import FilterMatchedBtn from "@/components/Gallery/AppBar/common/FilterDrawer/FilterMatchedBtn.vue";
import FilterMissingBtn from "@/components/Gallery/AppBar/common/FilterDrawer/FilterMissingBtn.vue";
import FilterPlayablesBtn from "@/components/Gallery/AppBar/common/FilterDrawer/FilterPlayablesBtn.vue";
@@ -46,7 +46,7 @@ const {
activeFilterDrawer,
filterUnmatched,
filterMatched,
filterFavourites,
filterFavorites,
filterDuplicates,
filterPlayables,
filterRA,
@@ -86,7 +86,7 @@ const onFilterChange = debounce(
search: searchTerm.value,
filterMatched: filterMatched.value ? "1" : null,
filterUnmatched: filterUnmatched.value ? "1" : null,
filterFavourites: filterFavourites.value ? "1" : null,
filterFavorites: filterFavorites.value ? "1" : null,
filterDuplicates: filterDuplicates.value ? "1" : null,
filterPlayables: filterPlayables.value ? "1" : null,
filterMissing: filterMissing.value ? "1" : null,
@@ -215,7 +215,7 @@ onMounted(async () => {
search: urlSearch,
filterMatched: urlFilteredMatch,
filterUnmatched: urlFilteredUnmatched,
filterFavourites: urlFilteredFavourites,
filterFavorites: urlFilteredFavorites,
filterDuplicates: urlFilteredDuplicates,
filterPlayables: urlFilteredPlayables,
filterMissing: urlFilteredMissing,
@@ -239,8 +239,8 @@ onMounted(async () => {
if (urlFilteredUnmatched !== undefined) {
galleryFilterStore.setFilterUnmatched(true);
}
if (urlFilteredFavourites !== undefined) {
galleryFilterStore.setFilterFavourites(true);
if (urlFilteredFavorites !== undefined) {
galleryFilterStore.setFilterFavorites(true);
}
if (urlFilteredDuplicates !== undefined) {
galleryFilterStore.setFilterDuplicates(true);
@@ -336,7 +336,7 @@ onMounted(async () => {
class="mt-2"
:tabindex="activeFilterDrawer ? 0 : -1"
/>
<FilterFavouritesBtn
<FilterFavoritesBtn
class="mt-2"
:tabindex="activeFilterDrawer ? 0 : -1"
/>

View File

@@ -8,10 +8,10 @@ import type { Events } from "@/types/emitter";
const { t } = useI18n();
const galleryFilterStore = storeGalleryFilter();
const { filterFavourites } = storeToRefs(galleryFilterStore);
const { filterFavorites } = storeToRefs(galleryFilterStore);
const emitter = inject<Emitter<Events>>("emitter");
function setFavourites() {
galleryFilterStore.switchFilterFavourites();
function setFavorites() {
galleryFilterStore.switchFilterFavorites();
emitter?.emit("filterRoms", null);
}
</script>
@@ -20,16 +20,16 @@ function setFavourites() {
<v-btn
block
variant="tonal"
:color="filterFavourites ? 'primary' : ''"
@click="setFavourites"
:color="filterFavorites ? 'primary' : ''"
@click="setFavorites"
>
<v-icon :color="filterFavourites ? 'primary' : ''"> mdi-star </v-icon
<v-icon :color="filterFavorites ? 'primary' : ''"> mdi-star </v-icon
><span
class="ml-2"
:class="{
'text-primary': filterFavourites,
'text-primary': filterFavorites,
}"
>{{ t("platform.show-favourites") }}</span
>{{ t("platform.show-favorites") }}</span
>
</v-btn>
</template>

View File

@@ -68,7 +68,7 @@ function resetSelection() {
emitter?.emit("openFabMenu", false);
}
async function addToFavourites() {
async function addToFavorites() {
if (!favoriteCollection.value) return;
favoriteCollection.value.rom_ids = favoriteCollection.value.rom_ids.concat(
selectedRoms.value.map((r) => r.id),
@@ -77,7 +77,7 @@ async function addToFavourites() {
.updateCollection({ collection: favoriteCollection.value as Collection })
.then(() => {
emitter?.emit("snackbarShow", {
msg: "Roms added to favourites successfully!",
msg: "Roms added to favorites successfully!",
icon: "mdi-check-bold",
color: "green",
timeout: 2000,
@@ -97,19 +97,19 @@ async function addToFavourites() {
});
}
async function removeFromFavourites() {
async function removeFromFavorites() {
if (!favoriteCollection.value) return;
favoriteCollection.value.rom_ids = favoriteCollection.value.rom_ids.filter(
(value) => !selectedRoms.value.map((r) => r.id).includes(value),
);
if (romsStore.currentCollection?.name.toLowerCase() == "favourites") {
if (romsStore.currentCollection?.is_favorite) {
romsStore.remove(selectedRoms.value);
}
await collectionApi
.updateCollection({ collection: favoriteCollection.value as Collection })
.then(({ data }) => {
emitter?.emit("snackbarShow", {
msg: "Roms removed from favourites successfully!",
msg: "Roms removed from favorites successfully!",
icon: "mdi-check-bold",
color: "green",
timeout: 2000,
@@ -191,25 +191,25 @@ async function onDownload() {
/>
<v-btn
key="3"
:title="t('rom.add-to-fav')"
:title="t('rom.add-to-favorites')"
color="toplayer"
elevation="8"
icon="mdi-star"
class="rounded"
:size="35"
rounded="0"
@click="addToFavourites"
@click="addToFavorites"
/>
<v-btn
key="4"
:title="t('rom.remove-from-fav')"
:title="t('rom.remove-from-favorites')"
color="toplayer"
elevation="8"
icon="mdi-star-remove-outline"
class="rounded"
:size="35"
rounded="0"
@click="removeFromFavourites"
@click="removeFromFavorites"
/>
<v-btn
key="5"

View File

@@ -52,7 +52,7 @@ const memoizedCovers = ref({
});
const collectionCoverImage = computed(() =>
props.collection.name?.toLowerCase() == "favourites"
props.collection.is_favorite
? getFavoriteCoverImage(props.collection.name)
: getCollectionCoverImage(props.collection.name),
);

View File

@@ -20,7 +20,11 @@ const { mdAndUp } = useDisplay();
const router = useRouter();
const show = ref(false);
const heartbeat = storeHeartbeat();
const collection = ref<UpdatedCollection>({ name: "" } as UpdatedCollection);
const collection = ref<UpdatedCollection>({
name: "",
path_covers_large: [],
path_covers_small: [],
} as unknown as UpdatedCollection);
const collectionsStore = storeCollections();
const imagePreviewUrl = ref<string | undefined>("");
const removeCover = ref(false);
@@ -34,7 +38,7 @@ emitter?.on("updateUrlCover", (coverUrl) => {
});
const missingCoverImage = computed(() =>
getMissingCoverImage(collection.value.name),
getMissingCoverImage(collection.value.name || ""),
);
function triggerFileInput() {
@@ -84,9 +88,7 @@ async function createCollection() {
timeout: 2000,
});
collectionsStore.addCollection(data);
if (data.name.toLowerCase() == "favourites") {
collectionsStore.setFavoriteCollection(data);
}
if (data.is_favorite) collectionsStore.setFavoriteCollection(data);
emitter?.emit("showLoadingDialog", { loading: false, scrim: false });
router.push({ name: ROUTES.COLLECTION, params: { collection: data.id } });
closeDialog();

View File

@@ -28,7 +28,7 @@ const {
searchTerm,
filterUnmatched,
filterMatched,
filterFavourites,
filterFavorites,
filterDuplicates,
filterPlayables,
filterRA,
@@ -68,7 +68,7 @@ const filterSummary = computed(() => {
filters.push(`Platform: ${selectedPlatform.value.name}`);
if (filterMatched.value) filters.push("Matched only");
if (filterUnmatched.value) filters.push("Unmatched only");
if (filterFavourites.value) filters.push("Favourites");
if (filterFavorites.value) filters.push("Favorites");
if (filterDuplicates.value) filters.push("Duplicates");
if (filterPlayables.value) filters.push("Playable");
if (filterRA.value) filters.push("Has RetroAchievements");
@@ -117,7 +117,7 @@ async function createSmartCollection() {
filterCriteria.platform_id = selectedPlatform.value.id;
if (filterMatched.value) filterCriteria.matched = true;
if (filterUnmatched.value) filterCriteria.matched = false;
if (filterFavourites.value) filterCriteria.favourite = true;
if (filterFavorites.value) filterCriteria.favorite = true;
if (filterDuplicates.value) filterCriteria.duplicate = true;
if (filterPlayables.value) filterCriteria.playable = true;
if (filterRA.value) filterCriteria.has_ra = true;

View File

@@ -36,7 +36,7 @@ async function deleteCollection() {
color: "green",
});
collectionsStore.removeCollection(collection.value as Collection);
if (collection.value?.name.toLowerCase() == "favourites") {
if (collection.value?.is_favorite) {
collectionsStore.setFavoriteCollection(undefined);
}
})

View File

@@ -23,7 +23,7 @@ const { toggleFavorite } = useFavoriteToggle(emitter);
const romsStore = storeRoms();
const scanningStore = storeScanning();
async function switchFromFavourites() {
async function switchFromFavorites() {
await toggleFavorite(props.rom);
}
@@ -124,7 +124,7 @@ async function onScan() {
<v-list-item
v-if="auth.scopes.includes('collections.write')"
class="py-4 pr-5"
@click="switchFromFavourites"
@click="switchFromFavorites"
>
<v-list-item-title class="d-flex">
<v-icon
@@ -136,8 +136,8 @@ async function onScan() {
class="mr-2"
/>{{
collectionsStore.isFavorite(rom)
? t("rom.remove-from-fav")
: t("rom.add-to-fav")
? t("rom.remove-from-favorites")
: t("rom.add-to-favorites")
}}
</v-list-item-title>
</v-list-item>

View File

@@ -13,7 +13,7 @@ const auth = storeAuth();
const emitter = inject<Emitter<Events>>("emitter");
const { toggleFavorite } = useFavoriteToggle(emitter);
async function switchFromFavourites() {
async function switchFromFavorites() {
await toggleFavorite(props.rom);
}
</script>
@@ -25,7 +25,7 @@ async function switchFromFavourites() {
rouded="0"
size="small"
variant="text"
@click.stop="switchFromFavourites"
@click.stop="switchFromFavorites"
>
<v-icon color="primary">
{{ collectionsStore.isFavorite(rom) ? "mdi-star" : "mdi-star-outline" }}

View File

@@ -19,7 +19,12 @@ export function useFavoriteToggle(emitter?: Emitter<Events>) {
if (favoriteCollection.value) return favoriteCollection.value;
// Create if still missing
const { data } = await collectionApi.createCollection({
collection: { name: "Favourites", rom_ids: [] },
collection: {
name: "Favorites",
rom_ids: [],
is_favorite: true,
is_public: false,
},
});
collectionsStore.addCollection(data);
collectionsStore.setFavoriteCollection(data);
@@ -72,7 +77,7 @@ export function useFavoriteToggle(emitter?: Emitter<Events>) {
const detail = (error as { response?: { data?: { detail?: string } } })
?.response?.data?.detail;
emitter?.emit("snackbarShow", {
msg: detail || "Failed to update favourites",
msg: detail || "Failed to update favorites",
icon: "mdi-close-circle",
color: "red",
});

View File

@@ -46,7 +46,7 @@ const memoizedCovers = ref({
});
const fallbackCollectionCover = computed(() =>
props.collection.name?.toLowerCase() == "favourites"
props.collection.is_favorite
? getFavoriteCoverImage(props.collection.name)
: getCollectionCoverImage(props.collection.name),
);

View File

@@ -649,11 +649,7 @@ onMounted(async () => {
virtualCollections.value = virtualCols ?? [];
collectionsStore.setCollections(cols ?? []);
collectionsStore.setFavoriteCollection(
cols?.find(
(collection) => collection.name.toLowerCase() === "favourites",
),
);
collectionsStore.setFavoriteCollection(cols?.find((c) => c.is_favorite));
} catch (err: unknown) {
errorMessage.value = err instanceof Error ? err.message : "Failed to load";
} finally {

View File

@@ -24,7 +24,7 @@
"search-platform": "Plattform suchen",
"settings": "Einstellungen",
"show-duplicates": "Zeige Duplikate",
"show-favourites": "Zeige Favoriten",
"show-favorites": "Zeige Favoriten",
"show-firmwares": "Zeige Firmwares/BIOS",
"show-matched": "Zeige zugewiesene",
"show-playables": "Zeige spielbare",

View File

@@ -1,6 +1,6 @@
{
"add-to-collection": "Zu Sammlung hinzufügen",
"add-to-fav": "Zu Favoriten hinzufügen",
"add-to-favorites": "Zu Favoriten hinzufügen",
"adding-to-collection-part1": "Füge",
"adding-to-collection-part2": "ROMs zu Sammlung hinzu",
"additional-content": "Zustätliche Inhalte",
@@ -46,7 +46,7 @@
"regions": "Regionen",
"related-content": "Zugehörige Inhalte",
"remove-from-collection": "Aus Sammlung entfernen",
"remove-from-fav": "Aus Favoriten entfernen",
"remove-from-favorites": "Aus Favoriten entfernen",
"remove-from-playing": "vom Spielen entfernen",
"removing-from-collection-part1": "Entferne",
"removing-from-collection-part2": "ROMs aus Sammlung",

View File

@@ -24,7 +24,7 @@
"search-platform": "Search platform",
"settings": "Settings",
"show-duplicates": "Show duplicates",
"show-favourites": "Show favourites",
"show-favorites": "Show favourites",
"show-firmwares": "Show firmwares/BIOS",
"show-matched": "Show Matched",
"show-unmatched": "Show unmatched",

View File

@@ -1,6 +1,6 @@
{
"add-to-collection": "Add to collection",
"add-to-fav": "Add to favourites",
"add-to-favorites": "Add to favourites",
"adding-to-collection-part1": "Adding",
"adding-to-collection-part2": "ROMs to collection",
"additional-content": "Additional content",
@@ -46,7 +46,7 @@
"regions": "Regions",
"related-content": "Related content",
"remove-from-collection": "Remove from collection",
"remove-from-fav": "Remove from favourites",
"remove-from-favorites": "Remove from favourites",
"remove-from-playing": "Remove from playing",
"removing-from-collection-part1": "Removing",
"removing-from-collection-part2": "ROMs from collection",

View File

@@ -24,7 +24,7 @@
"search-platform": "Search platform",
"settings": "Settings",
"show-duplicates": "Show duplicates",
"show-favourites": "Show favourites",
"show-favorites": "Show favourites",
"show-firmwares": "Show firmwares/BIOS",
"show-matched": "Show Matched",
"show-unmatched": "Show unmatched",

View File

@@ -1,6 +1,6 @@
{
"add-to-collection": "Add to collection",
"add-to-fav": "Add to favourites",
"add-to-favorites": "Add to favourites",
"adding-to-collection-part1": "Adding",
"adding-to-collection-part2": "ROMs to collection",
"additional-content": "Additional content",
@@ -46,7 +46,7 @@
"regions": "Regions",
"related-content": "Related content",
"remove-from-collection": "Remove from collection",
"remove-from-fav": "Remove from favourites",
"remove-from-favorites": "Remove from favourites",
"remove-from-playing": "Remove from playing",
"removing-from-collection-part1": "Removing",
"removing-from-collection-part2": "ROMs from collection",

View File

@@ -24,7 +24,7 @@
"search-platform": "Buscar plataforma",
"settings": "Configuración",
"show-duplicates": "Mostrar duplicados",
"show-favourites": "Mostrar favoritos",
"show-favorites": "Mostrar favoritos",
"show-firmwares": "Mostrar firmwares/BIOS",
"show-matched": "Mostrar identificados",
"show-unmatched": "Mostrar no identificados",

View File

@@ -1,6 +1,6 @@
{
"add-to-collection": "Añadir a la colección",
"add-to-fav": "Añadir a favoritos",
"add-to-favorites": "Añadir a favoritos",
"adding-to-collection-part1": "Añadiendo",
"adding-to-collection-part2": "ROMs a la colección",
"additional-content": "Contenido adicional",
@@ -46,7 +46,7 @@
"regions": "Regiones",
"related-content": "Contenido relacionado",
"remove-from-collection": "Eliminar de la colección",
"remove-from-fav": "Eliminar de favoritos",
"remove-from-favorites": "Eliminar de favoritos",
"remove-from-playing": "Eliminar de jugar",
"removing-from-collection-part1": "Quitando",
"removing-from-collection-part2": "ROMs de la colección",

View File

@@ -24,7 +24,7 @@
"search-platform": "Recherche dans la plateforme",
"settings": "Paramètres",
"show-duplicates": "Jeux doubles",
"show-favourites": "Jeux favoris",
"show-favorites": "Jeux favoris",
"show-firmwares": "Afficher firmwares/BIOS",
"show-matched": "Jeux identifiés",
"show-unmatched": "Jeux non identifiés",

View File

@@ -1,6 +1,6 @@
{
"add-to-collection": "Ajouter à la collection",
"add-to-fav": "Ajouter aux favoris",
"add-to-favorites": "Ajouter aux favoris",
"adding-to-collection-part1": "Ajout de",
"adding-to-collection-part2": "ROMs à la collection",
"additional-content": "Contenu supplémentaire",
@@ -46,7 +46,7 @@
"regions": "Régions",
"related-content": "Contenu connexe",
"remove-from-collection": "Retirer de la collection",
"remove-from-fav": "Retirer des favoris",
"remove-from-favorites": "Retirer des favoris",
"remove-from-playing": "Retirer de jouant",
"removing-from-collection-part1": "Retrait de",
"removing-from-collection-part2": "ROMs de la collection",

View File

@@ -24,7 +24,7 @@
"search-platform": "Cerca Piattafroma",
"settings": "Impostazioni",
"show-duplicates": "Mostra duplicati",
"show-favourites": "Mostra preferiti",
"show-favorites": "Mostra preferiti",
"show-firmwares": "Mostra firmwares/BIOS",
"show-matched": "Mostra con corrispondenza",
"show-unmatched": "Mostra senza corrispondenza",

View File

@@ -1,6 +1,6 @@
{
"add-to-collection": "Aggiungi alla collezione",
"add-to-fav": "Aggiungi ai preferiti",
"add-to-favorites": "Aggiungi ai preferiti",
"adding-to-collection-part1": "Aggiungendo",
"adding-to-collection-part2": "rom alla collezione",
"additional-content": "Contenuti aggiuntivi",
@@ -46,7 +46,7 @@
"regions": "Regioni",
"related-content": "Contenuti correlati",
"remove-from-collection": "Rimuovi dalla collezione",
"remove-from-fav": "Rimuovi dai preferiti",
"remove-from-favorites": "Rimuovi dai preferiti",
"remove-from-playing": "Rimuovi da Stai giocando",
"removing-from-collection-part1": "Rimuovendo",
"removing-from-collection-part2": "rom dalla collezione",

View File

@@ -24,7 +24,7 @@
"search-platform": "プラットフォームを検索",
"settings": "設定",
"show-duplicates": "重複を表示",
"show-favourites": "お気に入りを表示",
"show-favorites": "お気に入りを表示",
"show-firmwares": "ファームウェア/BIOSを表示",
"show-matched": "該当を表示",
"show-unmatched": "非該当を表示",

View File

@@ -1,6 +1,6 @@
{
"add-to-collection": "コレクションに追加",
"add-to-fav": "お気に入りに追加",
"add-to-favorites": "お気に入りに追加",
"adding-to-collection-part1": "コレクションにROM",
"adding-to-collection-part2": "を追加しています",
"additional-content": "追加コンテンツ",
@@ -46,7 +46,7 @@
"regions": "リージョン",
"related-content": "関連コンテンツ",
"remove-from-collection": "コレクションから削除",
"remove-from-fav": "お気に入りから削除",
"remove-from-favorites": "お気に入りから削除",
"remove-from-playing": "プレイ中から削除",
"removing-from-collection-part1": "コレクションからROM",
"removing-from-collection-part2": "を削除しています",

View File

@@ -24,7 +24,7 @@
"search-platform": "플랫폼 검색",
"settings": "설정",
"show-duplicates": "중복",
"show-favourites": "즐겨찾기",
"show-favorites": "즐겨찾기",
"show-firmwares": "펌웨어/바이오스 보여주기",
"show-matched": "DB 대응 됨",
"show-unmatched": "DB 대응 안됨",

View File

@@ -1,6 +1,6 @@
{
"add-to-collection": "모음집에 추가",
"add-to-fav": "즐겨찾기에 추가",
"add-to-favorites": "즐겨찾기에 추가",
"adding-to-collection-part1": "",
"adding-to-collection-part2": "개의 롬을 모음집에 추가합니다",
"additional-content": "추가 컨텐츠",
@@ -46,7 +46,7 @@
"regions": "지역",
"related-content": "관련 컨텐츠",
"remove-from-collection": "모음집에서 제거",
"remove-from-fav": "즐겨찾기에서 제거",
"remove-from-favorites": "즐겨찾기에서 제거",
"remove-from-playing": "플레이 중지",
"removing-from-collection-part1": "",
"removing-from-collection-part2": "개의 롬을 모음집에서 제거합니다",

View File

@@ -24,7 +24,7 @@
"search-platform": "Szukaj platformy",
"settings": "Ustawienia",
"show-duplicates": "Pokaż duplikaty",
"show-favourites": "Pokaż ulubione",
"show-favorites": "Pokaż ulubione",
"show-firmwares": "Pokaż oprogramowanie/BIOS",
"show-matched": "Pokaż dopasowane",
"show-unmatched": "Pokaż niedopasowane",

View File

@@ -1,6 +1,6 @@
{
"add-to-collection": "Dodaj do kolekcji",
"add-to-fav": "Dodaj do ulubionych",
"add-to-favorites": "Dodaj do ulubionych",
"adding-to-collection-part1": "Dodawanie",
"adding-to-collection-part2": "ROM-ów do kolekcji",
"additional-content": "Dodatkowa zawartość",
@@ -46,7 +46,7 @@
"regions": "Regiony",
"related-content": "Powiązana zawartość",
"remove-from-collection": "Usuń z kolekcji",
"remove-from-fav": "Usuń z ulubionych",
"remove-from-favorites": "Usuń z ulubionych",
"remove-from-playing": "Usuń z aktualnie granych",
"removing-from-collection-part1": "Usuwanie",
"removing-from-collection-part2": "ROM-ów z kolekcji",

View File

@@ -24,7 +24,7 @@
"search-platform": "Buscar plataforma",
"settings": "Configurações",
"show-duplicates": "Mostrar duplicados",
"show-favourites": "Mostrar favoritos",
"show-favorites": "Mostrar favoritos",
"show-firmwares": "Mostrar firmwares/BIOS",
"show-matched": "Mostrar correspondentes",
"show-unmatched": "Mostrar não correspondentes",

View File

@@ -1,6 +1,6 @@
{
"add-to-collection": "Adicionar à coleção",
"add-to-fav": "Adicionar aos favoritos",
"add-to-favorites": "Adicionar aos favoritos",
"adding-to-collection-part1": "Adicionando",
"adding-to-collection-part2": "ROMs à coleção",
"additional-content": "Conteúdo adicional",
@@ -46,7 +46,7 @@
"regions": "Regiões",
"related-content": "Conteúdo relacionado",
"remove-from-collection": "Remover da coleção",
"remove-from-fav": "Remover dos favoritos",
"remove-from-favorites": "Remover dos favoritos",
"remove-from-playing": "Remover de jogando",
"removing-from-collection-part1": "Removendo",
"removing-from-collection-part2": "ROMs da coleção",

View File

@@ -24,7 +24,7 @@
"search-platform": "Căutare în platformă",
"settings": "Setări",
"show-duplicates": "Jocuri duplicate",
"show-favourites": "Jocuri favorite",
"show-favorites": "Jocuri favorite",
"show-firmwares": "Afișează firmware/BIOS",
"show-matched": "Jocuri identificate",
"show-unmatched": "Jocuri neidentificate",

View File

@@ -1,6 +1,6 @@
{
"add-to-collection": "Adaugă la colecție",
"add-to-fav": "Adaugă la favorite",
"add-to-favorites": "Adaugă la favorite",
"adding-to-collection-part1": "Adăugare a",
"adding-to-collection-part2": "ROM-urilor la colecție",
"additional-content": "Conținut suplimentar",
@@ -46,7 +46,7 @@
"regions": "Regiuni",
"related-content": "Conținut similar",
"remove-from-collection": "Elimină din colecție",
"remove-from-fav": "Elimină din favorite",
"remove-from-favorites": "Elimină din favorite",
"remove-from-playing": "Elimină din secțiunea de joc",
"removing-from-collection-part1": "Eliminare a",
"removing-from-collection-part2": "ROM-urilor din colecție",

View File

@@ -24,7 +24,7 @@
"search-platform": "Поиск платформы",
"settings": "Настройки",
"show-duplicates": "Показать дубликаты",
"show-favourites": "Показать избранное",
"show-favorites": "Показать избранное",
"show-firmwares": "Показать прошивки/BIOS",
"show-matched": "Показать соответствующие",
"show-unmatched": "Показать несоответствующие",

View File

@@ -1,6 +1,6 @@
{
"add-to-collection": "Добавить в коллекцию",
"add-to-fav": "Добавить в избранное",
"add-to-favorites": "Добавить в избранное",
"adding-to-collection-part1": "Добавление",
"adding-to-collection-part2": "ромов в коллекцию",
"additional-content": "Дополнительный контент",
@@ -46,7 +46,7 @@
"regions": "Регионы",
"related-content": "Связанный контент",
"remove-from-collection": "Удалить из коллекции",
"remove-from-fav": "Удалить из избранного",
"remove-from-favorites": "Удалить из избранного",
"remove-from-playing": "Удалить из играющих",
"removing-from-collection-part1": "Удаление",
"removing-from-collection-part2": "ромов из коллекции",

View File

@@ -24,7 +24,7 @@
"search-platform": "搜索平台",
"settings": "设置",
"show-duplicates": "显示重复项",
"show-favourites": "显示喜好项",
"show-favorites": "显示喜好项",
"show-firmwares": "显示固件与 BIOS",
"show-matched": "显示匹配项",
"show-unmatched": "显示不匹配项",

View File

@@ -1,6 +1,6 @@
{
"add-to-collection": "添加至收藏",
"add-to-fav": "添加至喜好",
"add-to-favorites": "添加至喜好",
"adding-to-collection-part1": "添加",
"adding-to-collection-part2": "ROMs 至收藏",
"additional-content": "附加内容",
@@ -46,7 +46,7 @@
"regions": "地区",
"related-content": "相关内容",
"remove-from-collection": "从收藏中移除",
"remove-from-fav": "从喜好中移除",
"remove-from-favorites": "从喜好中移除",
"remove-from-playing": "从正在游玩中移除",
"removing-from-collection-part1": "从收藏中移除",
"removing-from-collection-part2": "ROMs",

View File

@@ -24,7 +24,7 @@
"search-platform": "搜尋平台",
"settings": "設定",
"show-duplicates": "顯示重複項目",
"show-favourites": "顯示喜好項目",
"show-favorites": "顯示喜好項目",
"show-firmwares": "顯示韌體和 BIOS",
"show-matched": "顯示匹配項目",
"show-unmatched": "顯示不匹配項目",

View File

@@ -1,6 +1,6 @@
{
"add-to-collection": "加入收藏庫",
"add-to-fav": "加入喜好",
"add-to-favorites": "加入喜好",
"adding-to-collection-part1": "加入",
"adding-to-collection-part2": "個 ROM 至收藏庫",
"additional-content": "附加内容",
@@ -46,7 +46,7 @@
"regions": "地區",
"related-content": "相關內容",
"remove-from-collection": "從收藏庫中移除",
"remove-from-fav": "從喜好中移除",
"remove-from-favorites": "從喜好中移除",
"remove-from-playing": "從正在遊玩中移除",
"removing-from-collection-part1": "從收藏庫中移除",
"removing-from-collection-part2": "個 ROM",

View File

@@ -22,6 +22,8 @@ async function createCollection({
formData.append("description", collection.description || "");
formData.append("url_cover", collection.url_cover || "");
formData.append("rom_ids", JSON.stringify(collection.rom_ids || []));
formData.append("is_favorite", String(collection.is_favorite || false));
formData.append("is_public", String(collection.is_public || false));
if (collection.artwork) formData.append("artwork", collection.artwork);
return api.post(`/collections`, formData, {

View File

@@ -72,7 +72,7 @@ export interface GetRomsParams {
orderDir?: string | null;
filterUnmatched?: boolean;
filterMatched?: boolean;
filterFavourites?: boolean;
filterFavorites?: boolean;
filterDuplicates?: boolean;
filterPlayables?: boolean;
filterRA?: boolean;
@@ -101,7 +101,7 @@ async function getRoms({
orderDir = "asc",
filterUnmatched = false,
filterMatched = false,
filterFavourites = false,
filterFavorites = false,
filterDuplicates = false,
filterPlayables = false,
filterRA = false,
@@ -139,7 +139,7 @@ async function getRoms({
selected_language: selectedLanguage,
...(filterUnmatched ? { matched: false } : {}),
...(filterMatched ? { matched: true } : {}),
...(filterFavourites ? { favourite: true } : {}),
...(filterFavorites ? { favorite: true } : {}),
...(filterDuplicates ? { duplicate: true } : {}),
...(filterPlayables ? { playable: true } : {}),
...(filterMissing ? { missing: true } : {}),

View File

@@ -50,7 +50,7 @@ class CachedApiService {
selected_language: params.selectedLanguage,
...(params.filterUnmatched ? { matched: false } : {}),
...(params.filterMatched ? { matched: true } : {}),
...(params.filterFavourites ? { favourite: true } : {}),
...(params.filterFavorites ? { favorite: true } : {}),
...(params.filterDuplicates ? { duplicate: true } : {}),
...(params.filterPlayables ? { playable: true } : {}),
...(params.filterMissing ? { missing: true } : {}),

View File

@@ -28,7 +28,7 @@ const defaultFilterState = {
filterStatuses: Object.values(romStatusMap).map((status) => status.text),
filterUnmatched: false,
filterMatched: false,
filterFavourites: false,
filterFavorites: false,
filterDuplicates: false,
filterPlayables: false,
filterRA: false,
@@ -121,11 +121,11 @@ export default defineStore("galleryFilter", {
this.filterMatched = !this.filterMatched;
this.filterUnmatched = false;
},
setFilterFavourites(value: boolean) {
this.filterFavourites = value;
setFilterFavorites(value: boolean) {
this.filterFavorites = value;
},
switchFilterFavourites() {
this.filterFavourites = !this.filterFavourites;
switchFilterFavorites() {
this.filterFavorites = !this.filterFavorites;
},
setFilterDuplicates(value: boolean) {
this.filterDuplicates = value;
@@ -161,7 +161,7 @@ export default defineStore("galleryFilter", {
return Boolean(
this.filterUnmatched ||
this.filterMatched ||
this.filterFavourites ||
this.filterFavorites ||
this.filterDuplicates ||
this.filterPlayables ||
this.filterRA ||
@@ -193,7 +193,7 @@ export default defineStore("galleryFilter", {
this.selectedStatus = null;
this.filterUnmatched = false;
this.filterMatched = false;
this.filterFavourites = false;
this.filterFavorites = false;
this.filterDuplicates = false;
this.filterPlayables = false;
this.filterRA = false;