Skip to content

Commit

Permalink
feat: add Random page and random sorting option (#109)
Browse files Browse the repository at this point in the history
* Implement "Random" as a sorting option, add standalone "Random" page for entire collection.
  • Loading branch information
DarthNerdus authored Apr 29, 2024
1 parent 01023b0 commit 186f8f0
Show file tree
Hide file tree
Showing 9 changed files with 385 additions and 9 deletions.
18 changes: 18 additions & 0 deletions src/jelu-ui/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,15 @@ const collapseDropdown = () => {
{{ t('nav.to_read') }}
</router-link>
</li>
<li @click="collapseDropdown()">
<router-link
v-if="isLogged"
class="font-sans text-base capitalize"
:to="{ name: 'random' }"
>
{{ t('nav.random') }}
</router-link>
</li>
<li @click="collapseDropdown()">
<router-link
v-if="isLogged"
Expand Down Expand Up @@ -307,6 +316,15 @@ const collapseDropdown = () => {
{{ t('nav.to_read') }}
</router-link>
</li>
<li>
<router-link
v-if="isLogged"
class="font-sans text-xl capitalize"
:to="{ name: 'random' }"
>
{{ "Random" }}
</router-link>
</li>
<li>
<router-link
v-if="isLogged"
Expand Down
6 changes: 6 additions & 0 deletions src/jelu-ui/src/components/BookList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,12 @@ try {
>
{{ t('sorting.avg_rating') }}
</o-radio>
<o-radio
v-model="sortBy"
native-value="random"
>
{{ t('sorting.random') }}
</o-radio>
</div>
</template>
<template #filters>
Expand Down
285 changes: 285 additions & 0 deletions src/jelu-ui/src/components/RandomList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
<script setup lang="ts">
import { useThrottleFn, useTitle } from '@vueuse/core';
import { useRouteQuery } from '@vueuse/router';
import { computed, Ref, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import usePagination from '../composables/pagination';
import useSort from '../composables/sort';
import useBulkEdition from '../composables/bulkEdition';
import { UserBook } from '../model/Book';
import { ReadingEventType } from '../model/ReadingEvent';
import dataService from "../services/DataService";
import BookCard from "./BookCard.vue";
import SortFilterBarVue from "./SortFilterBar.vue";
const { t } = useI18n({
inheritLocale: true,
useScope: 'global'
})
useTitle('Jelu | Random')
const books: Ref<Array<UserBook>> = ref([]);
const { total, page, pageAsNumber, perPage, updatePage, getPageIsLoading, updatePageLoading } = usePagination()
const { sortQuery, sortOrder, sortBy, sortOrderUpdated } = useSort('random,desc')
const { showSelect, selectAll, checkedCards, cardChecked, toggleEdit } = useBulkEdition(modalClosed)
const eventTypes: Ref<Array<ReadingEventType>> = useRouteQuery('lastEventTypes', [])
const userId: Ref<string|null> = useRouteQuery('userId', null)
const username = ref("")
const getUsername = async () => {
if (userId.value != null) {
username.value = await dataService.usernameById(userId.value)
}
}
getUsername()
const open = ref(false)
const owned: Ref<string|null> = useRouteQuery('owned', "null")
const ownedAsBool = computed(() => {
if (owned.value?.toLowerCase() === "null") {
return null
} else if (owned.value?.toLowerCase() === "true") {
return true
} else {
return false
}
}
)
const getRandomIsLoading: Ref<boolean> = ref(false)
const getRandom = async () => {
getRandomIsLoading.value = true
try {
const res = await dataService.findUserBookByCriteria(
eventTypes.value, null, userId.value,
null, ownedAsBool.value, null,
pageAsNumber.value - 1, perPage.value, sortQuery.value)
total.value = res.totalElements
books.value = res.content
if (! res.empty) {
page.value = (res.number + 1).toString(10)
}
else {
page.value = "1"
}
getRandomIsLoading.value = false
updatePageLoading(false)
} catch (error) {
console.log("failed get books : " + error);
getRandomIsLoading.value = false
updatePageLoading(false)
}
};
// watches set above sometimes called twice
// so getBooks was sometimes called twice at the same instant
const throttledGetRandom = useThrottleFn(() => {
getRandom()
}, 100, false)
watch([page, eventTypes, sortQuery, owned], (newVal, oldVal) => {
console.log("all " + newVal + " " + oldVal)
if (newVal !== oldVal) {
throttledGetRandom()
}
})
function modalClosed() {
console.log("modal closed")
throttledGetRandom()
}
const message = computed(() => {
if (userId.value != null) {
return t('labels.reading_list_from_name', { name: username.value })
} else {
return "Random"
}
} )
getRandom()
</script>

<template>
<sort-filter-bar-vue
:open="open"
:order="sortOrder"
class="sort-filter-bar"
@update:open="open = $event"
@update:sort-order="sortOrderUpdated"
>
<template #filters>
<div class="field flex flex-col capitalize gap-1">
<label class="label">{{ t('reading_events.last_event_type') }} : </label>
<o-checkbox
v-model="eventTypes"
native-value="FINISHED"
>
{{ t('reading_events.finished') }}
</o-checkbox>
<o-checkbox
v-model="eventTypes"
native-value="CURRENTLY_READING"
>
{{ t('reading_events.currently_reading') }}
</o-checkbox>
<o-checkbox
v-model="eventTypes"
native-value="DROPPED"
>
{{ t('reading_events.dropped') }}
</o-checkbox>
</div>
<div class="field">
<label class="label">{{ t('filtering.owned') }} : </label>
<div class="field">
<o-radio
v-model="owned"
native-value="null"
>
{{ t('filtering.unset') }}
</o-radio>
</div>
<div class="field">
<o-radio
v-model="owned"
native-value="false"
>
{{ t('labels.false') }}
</o-radio>
</div>
<div class="field">
<o-radio
v-model="owned"
native-value="true"
>
{{ t('labels.true') }}
</o-radio>
</div>
</div>
</template>
</sort-filter-bar-vue>
<div class="flex flex-row justify-between">
<div class="flex flex-row gap-1 order-last sm:order-first">
<o-button
variant="success"
outlined
@click="open = !open"
>
<span class="icon text-lg">
<i class="mdi mdi-filter-variant" />
</span>
</o-button>
<button
v-tooltip="t('bulk.toggle')"
class="btn btn-outline btn-primary"
@click="showSelect = !showSelect"
>
<span class="icon text-lg">
<i class="mdi mdi-pencil" />
</span>
</button>
<button
v-if="showSelect"
v-tooltip="t('bulk.select_all')"
class="btn btn-outline btn-accent"
@click="selectAll = !selectAll"
>
<span class="icon text-lg">
<i class="mdi mdi-checkbox-multiple-marked" />
</span>
</button>
<button
v-if="showSelect && checkedCards.length > 0"
v-tooltip="t('bulk.edit')"
class="btn btn-outline btn-info"
@click="toggleEdit(checkedCards)"
>
<span class="icon text-lg">
<i class="mdi mdi-book-edit" />
</span>
</button>
</div>
<h2 class="text-3xl typewriter capitalize">
{{ message }} :
</h2>
<div />
</div>
<div
v-if="books.length > 0"
class="grid grid-cols-2 md:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8 gap-0 my-3"
>
<div
v-for="book in books"
:key="book.id"
class="books-grid-item m-2"
>
<book-card
:book="book"
:force-select="selectAll"
:show-select="showSelect"
:propose-add="true"
class="h-full"
@update:modal-closed="modalClosed"
@update:checked="cardChecked"
/>
</div>
</div>
<div
v-else-if="getRandomIsLoading"
class="flex flex-row justify-center justify-items-center gap-3"
>
<o-skeleton
class="justify-self-center basis-36"
height="250px"
:animated="true"
/>
<o-skeleton
class="justify-self-center basis-36"
height="250px"
:animated="true"
/>
<o-skeleton
class="justify-self-center basis-36"
height="250px"
:animated="true"
/>
</div>
<div v-else>
<h2 class="text-3xl typewriter">
"Random"
</h2>
<span class="icon">
<i class="mdi mdi-book-open-page-variant-outline mdi-48px" />
</span>
</div>
<o-loading
v-model:active="getPageIsLoading"
:full-page="true"
:can-cancel="true"
/>
</template>

<style scoped>
label {
margin: 0 0.5em;
font-weight: bold;
}
/* fields in side bar slots are shifted to the right and alignment is broken */
.field {
margin-left: -8px;
}
</style>
6 changes: 6 additions & 0 deletions src/jelu-ui/src/components/TagBooks.vue
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@ getBooks()
>
{{ t('sorting.modification_date') }}
</o-radio>
<o-radio
v-model="sortBy"
native-value="random"
>
{{ t('sorting.random') }}
</o-radio>
</div>
<div class="field">
<o-radio
Expand Down
6 changes: 6 additions & 0 deletions src/jelu-ui/src/components/ToReadList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ getToRead()
>
{{ t('sorting.avg_rating') }}
</o-radio>
<o-radio
v-model="sortBy"
native-value="random"
>
{{ t('sorting.random') }}
</o-radio>
</div>
</template>
<template #filters>
Expand Down
2 changes: 2 additions & 0 deletions src/jelu-ui/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"nav": {
"my_books": "my books",
"to_read": "to read list",
"random": "random",
"add_book" : "add book",
"dashboard" : "dashboard",
"login" : "login",
Expand Down Expand Up @@ -144,6 +145,7 @@
"date_added" : "Date added",
"date_added_to_list" : "Date added to list",
"title" : "Title",
"random": "Random",
"publisher" : "Publisher",
"series" : "Series",
"series_number" : "Series number",
Expand Down
6 changes: 6 additions & 0 deletions src/jelu-ui/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ const router = createRouter({
name: 'to-read',
beforeEnter: [isLogged],
},
{
path: '/random',
component: () => import(/* webpackChunkName: "recommend" */ './components/RandomList.vue'),
name: 'random',
beforeEnter: [isLogged],
},
{
path: '/history',
component: () => import(/* webpackChunkName: "recommend" */ './components/History.vue'),
Expand Down
Loading

0 comments on commit 186f8f0

Please sign in to comment.