diff --git a/src/composables/useVote.ts b/src/composables/useVote.ts index 36ba226..fe37284 100644 --- a/src/composables/useVote.ts +++ b/src/composables/useVote.ts @@ -1,13 +1,13 @@ -import { computed, reactive, ref, toRefs } from 'vue'; +import { computed, reactive, ref, toRefs, watchEffect } from 'vue'; import glicko2 from 'glicko2-lite'; -import photos from '@/photos.json'; +import defaultPhotos from '@/photos.json'; import { Database, Photo } from '@/models'; import { randomNumber } from '@/utils/randomNumber'; const db = reactive( JSON.parse(localStorage.getItem('db') as string) || { - photos, + photos: defaultPhotos, votes: [] } ); @@ -24,93 +24,100 @@ const photosForFirstPick = computed(() => }) ); -export function useVote() { - const pickPhotosForNewVote = () => { - const firstPick = - photosForFirstPick.value[ - randomNumber(0, photosForFirstPick.value.length - 1) - ]; +const pickPhotosForNewVote = () => { + const firstPick = + photosForFirstPick.value[ + randomNumber(0, photosForFirstPick.value.length - 1) + ]; - const photosForSecondPick = db.photos.filter(({ fileName }) => { - const votesWithFirstPick = db.votes.filter(({ photos }) => - photos.includes(firstPick.fileName) - ); + const photosForSecondPick = db.photos.filter(({ fileName }) => { + const votesWithFirstPick = db.votes.filter(({ photos }) => + photos.includes(firstPick.fileName) + ); - const fileNamesToExclude = [ - ...new Set(votesWithFirstPick.flatMap(({ photos }) => photos)) - ]; + const fileNamesToExclude = [ + ...new Set( + votesWithFirstPick + .flatMap(({ photos }) => photos) + .concat([firstPick.fileName]) + ) + ]; - return !fileNamesToExclude.includes(fileName); - }); + return !fileNamesToExclude.includes(fileName); + }); - const secondPick = - photosForSecondPick[randomNumber(0, photosForSecondPick.length - 1)]; + const secondPick = + photosForSecondPick[randomNumber(0, photosForSecondPick.length - 1)]; - photosInCurrentVote.value.push(firstPick, secondPick); - }; + photosInCurrentVote.value = [firstPick, secondPick]; +}; - const submitVote = (result: 0 | 0.5 | 1) => { - const fileNames = [...photosInCurrentVote.value].map( - ({ fileName }) => fileName - ) as [string, string]; +const submitVote = (result: 0 | 0.5 | 1) => { + const fileNames = ([...photosInCurrentVote.value] as Photo[]).map( + ({ fileName }) => fileName + ) as [string, string]; - db.votes.unshift({ photos: fileNames, result }); - photosInCurrentVote.value = []; - pickPhotosForNewVote(); - }; + db.votes.unshift({ photos: fileNames, result }); - const updateRatings = () => { - const twelveMostRecentVotes = db.votes.slice(0, 12).reverse(); + if (db.votes.length % 12 === 0) { + updateRatings(); + } - const votesGrouppedByPhotos = twelveMostRecentVotes.reduce((obj, vote) => { - const [firstPick, secondPick] = vote.photos; + pickPhotosForNewVote(); +}; - const firstPhoto = db.photos.find( - ({ fileName }) => fileName === firstPick - )!; - const secondPhoto = db.photos.find( - ({ fileName }) => fileName === secondPick - )!; +const updateRatings = () => { + const twelveMostRecentVotes = db.votes.slice(0, 12).reverse(); - const firstPhotoOpponentParams = [ - secondPhoto.rating, - secondPhoto.rd, - vote.result - ] as [number, number, number]; + const votesGrouppedByPhotos = twelveMostRecentVotes.reduce((obj, vote) => { + const [firstFileName, secondFileName] = vote.photos; - const secondPhotoOpponentParams = [ - firstPhoto.rating, - firstPhoto.rd, - 1 - vote.result - ] as [number, number, number]; + const firstPhoto = db.photos.find( + ({ fileName }) => fileName === firstFileName + )!; - if (obj[firstPick]) { - obj[firstPick].push(firstPhotoOpponentParams); - } else { - obj[firstPick] = [firstPhotoOpponentParams]; - } + const secondPhoto = db.photos.find( + ({ fileName }) => fileName === secondFileName + )!; - if (obj[secondPick]) { - obj[secondPick].push(secondPhotoOpponentParams); - } else { - obj[secondPick] = [secondPhotoOpponentParams]; - } + const firstPhotoOpponentParams = [ + secondPhoto.rating, + secondPhoto.rd, + vote.result + ] as [number, number, number]; - return obj; - }, {} as Record); + const secondPhotoOpponentParams = [ + firstPhoto.rating, + firstPhoto.rd, + 1 - vote.result + ] as [number, number, number]; - for (const [photoFileName, votes] of Object.entries( - votesGrouppedByPhotos - )) { - const photo = db.photos.find( - ({ fileName }) => fileName === photoFileName - )!; - const newRatingParams = glicko2(photo.rating, photo.rd, photo.vol, votes); - - Object.assign(photo, newRatingParams); + if (obj[firstFileName]) { + obj[firstFileName].push(firstPhotoOpponentParams); + } else { + obj[firstFileName] = [firstPhotoOpponentParams]; } - }; + if (obj[secondFileName]) { + obj[secondFileName].push(secondPhotoOpponentParams); + } else { + obj[secondFileName] = [secondPhotoOpponentParams]; + } + + return obj; + }, {} as Record); + + for (const [photoFileName, votes] of Object.entries(votesGrouppedByPhotos)) { + const photo = db.photos.find(({ fileName }) => fileName === photoFileName)!; + const newRatingParams = glicko2(photo.rating, photo.rd, photo.vol, votes); + + Object.assign(photo, newRatingParams); + } +}; + +watchEffect(() => localStorage.setItem('db', JSON.stringify(db))); + +export function useVote() { return { photosInCurrentVote, photosForFirstPick, diff --git a/src/views/Vote.vue b/src/views/Vote.vue index 0f0dc4b..ce6d1c9 100644 --- a/src/views/Vote.vue +++ b/src/views/Vote.vue @@ -2,7 +2,7 @@ import { onMounted } from 'vue'; import { useVote } from '@/composables/useVote'; -const { photosInCurrentVote, pickPhotosForNewVote } = useVote(); +const { photosInCurrentVote, pickPhotosForNewVote, submitVote } = useVote(); onMounted(pickPhotosForNewVote); @@ -26,17 +26,20 @@ onMounted(pickPhotosForNewVote); >

Photo {{ index + 1 }}

- Photo 1 - Photo 2 - I can't decide + Photo 1 + Photo 2 + I can't decide