mirror of
https://github.com/maciejpedzich/racemash.git
synced 2024-11-27 16:15:47 +01:00
Remove Apprwite-related code
This commit is contained in:
parent
7bf979101f
commit
7c548bf1e4
@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npm run prettier-format && npm run lint
|
44
src/App.vue
44
src/App.vue
@ -1,54 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { useAuth } from './composables/useAuth';
|
||||
import { useSnackbar } from './composables/useSnackbar';
|
||||
|
||||
import NavMenu from './components/ui/NavMenu.vue';
|
||||
import Snackbar from './components/ui/Snackbar.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const { loadingUserFinished } = useAuth();
|
||||
const { showSnackbar } = useSnackbar();
|
||||
|
||||
onMounted(async () => {
|
||||
await router.isReady();
|
||||
|
||||
const loginStatusHashes = ['#login-error', '#login-success'];
|
||||
const routeHash = router.currentRoute.value.hash;
|
||||
|
||||
if (loginStatusHashes.includes(routeHash)) {
|
||||
localStorage.removeItem('redirectPath');
|
||||
|
||||
showSnackbar({
|
||||
status: routeHash.replace('#login-', '') as 'error' | 'success',
|
||||
message:
|
||||
routeHash === '#login-error'
|
||||
? 'Failed to log you in'
|
||||
: "You're logged in"
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-app>
|
||||
<Snackbar />
|
||||
<NavMenu />
|
||||
<v-main>
|
||||
<section
|
||||
v-if="!loadingUserFinished"
|
||||
class="w-100 h-100 pb-4 d-flex justify-center align-center"
|
||||
>
|
||||
<v-progress-circular
|
||||
:size="120"
|
||||
:width="7"
|
||||
color="primary"
|
||||
indeterminate
|
||||
></v-progress-circular>
|
||||
</section>
|
||||
<RouterView v-show="loadingUserFinished" />
|
||||
<RouterView />
|
||||
</v-main>
|
||||
<Snackbar />
|
||||
</v-app>
|
||||
</template>
|
||||
|
@ -1,12 +0,0 @@
|
||||
import { Account, Avatars, Client, Databases, Storage } from 'appwrite';
|
||||
|
||||
const appwriteClient = new Client();
|
||||
|
||||
appwriteClient
|
||||
.setEndpoint(import.meta.env.VITE_APPWRITE_ENDPOINT)
|
||||
.setProject(import.meta.env.VITE_APPWRITE_PROJECT_ID);
|
||||
|
||||
export const account = new Account(appwriteClient);
|
||||
export const avatars = new Avatars(appwriteClient);
|
||||
export const databases = new Databases(appwriteClient);
|
||||
export const storage = new Storage(appwriteClient);
|
@ -1,56 +0,0 @@
|
||||
import { ref, computed } from 'vue';
|
||||
import { Models } from 'appwrite';
|
||||
|
||||
import { account } from '@/appwrite';
|
||||
|
||||
const user = ref<Models.User<Models.Preferences> | null>(null);
|
||||
const loadingUserFinished = ref(false);
|
||||
const isLoggedIn = computed(() => !!user.value);
|
||||
|
||||
export function useAuth() {
|
||||
const logIn = (provider: 'github' | 'discord') => {
|
||||
const redirectPath = localStorage.getItem('redirectPath') || '/';
|
||||
const permissionScopes =
|
||||
provider === 'github'
|
||||
? ['read:user', 'user:email']
|
||||
: ['identify', 'email'];
|
||||
|
||||
account.createOAuth2Session(
|
||||
provider,
|
||||
`${location.origin}${redirectPath}#login-success`,
|
||||
`${location.origin}/log-in#login-error`,
|
||||
permissionScopes
|
||||
);
|
||||
};
|
||||
|
||||
const loadUser = async () => {
|
||||
try {
|
||||
if (loadingUserFinished.value) return;
|
||||
|
||||
const currentUser = await account.get();
|
||||
user.value = currentUser;
|
||||
} catch (error) {
|
||||
if (import.meta.env.DEV) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
user.value = null;
|
||||
} finally {
|
||||
loadingUserFinished.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
const logOut = async () => {
|
||||
await account.deleteSession('current');
|
||||
user.value = null;
|
||||
};
|
||||
|
||||
return {
|
||||
user,
|
||||
loadingUserFinished,
|
||||
isLoggedIn,
|
||||
logIn,
|
||||
loadUser,
|
||||
logOut
|
||||
};
|
||||
}
|
10
src/global.d.ts
vendored
10
src/global.d.ts
vendored
@ -1,10 +0,0 @@
|
||||
interface ImportMetaEnv {
|
||||
BASE_URL: string;
|
||||
VITE_APPWRITE_ENDPOINT: string;
|
||||
VITE_APPWRITE_PROJECT_ID: string;
|
||||
VITE_DATABASE_ID: string;
|
||||
VITE_PHOTOS_COLLECTION_ID: string;
|
||||
VITE_VOTES_COLLECTION_ID: string;
|
||||
VITE_PHOTO_APPEARANCE_COUNT_COLLECTION_ID: string;
|
||||
VITE_NUMBER_OF_PHOTOS: number;
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import { RouteLocationNormalized, NavigationGuardNext } from 'vue-router';
|
||||
import { useAuth } from '@/composables/useAuth';
|
||||
|
||||
export async function authGuard(
|
||||
to: RouteLocationNormalized,
|
||||
from: RouteLocationNormalized,
|
||||
next: NavigationGuardNext
|
||||
) {
|
||||
const { isLoggedIn, loadUser } = useAuth();
|
||||
|
||||
await loadUser();
|
||||
|
||||
if (!to.meta.authRequired || isLoggedIn.value) {
|
||||
return next();
|
||||
} else {
|
||||
localStorage.setItem('redirectPath', to.fullPath);
|
||||
return next('/log-in');
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
|
||||
import { authGuard } from '@/guards/auth';
|
||||
import Home from '@/views/Home.vue';
|
||||
|
||||
const routes = [
|
||||
@ -27,6 +26,4 @@ const router = createRouter({
|
||||
routes
|
||||
});
|
||||
|
||||
router.beforeEach(authGuard);
|
||||
|
||||
export default router;
|
||||
|
@ -1,25 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { useAuth } from '@/composables/useAuth';
|
||||
|
||||
const { logIn } = useAuth();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="w-100 h-100 d-flex flex-column justify-center align-center">
|
||||
<h1 class="text-h3 mb-3">Log in via:</h1>
|
||||
<v-container>
|
||||
<v-row align="center" justify="center">
|
||||
<v-col cols="auto">
|
||||
<v-btn color="github" size="large" @click="logIn('github')"
|
||||
>GitHub</v-btn
|
||||
>
|
||||
</v-col>
|
||||
<v-col cols="auto">
|
||||
<v-btn color="discord" size="large" @click="logIn('discord')"
|
||||
>Discord</v-btn
|
||||
>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</section>
|
||||
</template>
|
@ -1,9 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref, reactive } from 'vue';
|
||||
import { Query } from 'appwrite';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
import { databases } from '@/appwrite';
|
||||
import { useAuth } from '@/composables/useAuth';
|
||||
import { useSnackbar } from '@/composables/useSnackbar';
|
||||
import { useRandomNumber } from '@/composables/useRandomNumber';
|
||||
|
||||
@ -11,123 +8,11 @@ import { Vote } from '@/models/vote';
|
||||
import { Photo } from '@/models/photo';
|
||||
import { PhotoAppearanceCount } from '@/models/photoAppearanceCount';
|
||||
|
||||
const { user } = useAuth();
|
||||
const { showSnackbar } = useSnackbar();
|
||||
const randomNumber = useRandomNumber();
|
||||
|
||||
const isLoading = ref(true);
|
||||
const photoAppearanceCount = reactive<Record<string, number>>({});
|
||||
const photosInCurrentVote = ref<Photo[]>([]);
|
||||
|
||||
// Get IDs of photos that have appeared in votes between all the other photos
|
||||
const idsOfPhotosPairedWithAll = computed(() =>
|
||||
Object.entries(JSON.parse(JSON.stringify(photoAppearanceCount)))
|
||||
// You shouldn't be able to vote between two identical photos.
|
||||
// That's why we need to subtract one from the number of photos.
|
||||
.filter(([, count]) => count === import.meta.env.VITE_NUMBER_OF_PHOTOS - 1)
|
||||
.map(([photoId]) => photoId)
|
||||
);
|
||||
|
||||
const loadPhotosAppearanceCount = async () => {
|
||||
const { documents: photoAppearanceCountEntries } =
|
||||
await databases.listDocuments<PhotoAppearanceCount>(
|
||||
import.meta.env.VITE_DATABASE_ID,
|
||||
import.meta.env.VITE_PHOTO_APPEARANCE_COUNT_COLLECTION_ID,
|
||||
[Query.equal('voterId', user.value?.$id as string)]
|
||||
);
|
||||
|
||||
const appearanceCountRecord = photoAppearanceCountEntries.reduce(
|
||||
(record, { photoId, count }) => {
|
||||
record[photoId] = count;
|
||||
return record;
|
||||
},
|
||||
{} as Record<string, number>
|
||||
);
|
||||
|
||||
Object.assign(photoAppearanceCount, appearanceCountRecord);
|
||||
};
|
||||
|
||||
const pickRandomPhotosAndCreateVote = async () => {
|
||||
// Pick a random photo that hasn't been paired with every other photo in the user's votes
|
||||
|
||||
const { documents: allPhotos } = await databases.listDocuments<Photo>(
|
||||
import.meta.env.VITE_DATABASE_ID,
|
||||
import.meta.env.VITE_PHOTOS_COLLECTION_ID
|
||||
// [
|
||||
// Query.select(['photoId', 'url', 'altText']) -> Unsupported in Appwrite v1.1.2,
|
||||
// Query.notEqual('photoId', idsOfPhotosPairedWithAll.value) -> Throws ambiguous Server Error
|
||||
// ]
|
||||
);
|
||||
|
||||
const photo1Candidates = allPhotos.filter(
|
||||
({ $id }) => !idsOfPhotosPairedWithAll.value.includes($id)
|
||||
);
|
||||
|
||||
const maxPhoto1Index =
|
||||
import.meta.env.VITE_NUMBER_OF_PHOTOS -
|
||||
idsOfPhotosPairedWithAll.value.length -
|
||||
1;
|
||||
|
||||
const photo1 = photo1Candidates[randomNumber(0, maxPhoto1Index)];
|
||||
|
||||
// Get IDs of photos that have already been paired with photo1 in user's votes
|
||||
|
||||
const idsOfPhotosPairedWithPhoto1 =
|
||||
// Select votes where photo1Id is equal to photo1's ID
|
||||
(
|
||||
await databases.listDocuments<Vote>(
|
||||
import.meta.env.VITE_DATABASE_ID,
|
||||
import.meta.env.VITE_VOTES_COLLECTION_ID,
|
||||
[
|
||||
// Query.select(['photo2Id']),
|
||||
Query.equal('voterId', [user.value?.$id as string]),
|
||||
Query.search('photos', photo1.$id)
|
||||
]
|
||||
)
|
||||
).documents.flatMap((doc) =>
|
||||
Object.entries(doc)
|
||||
.filter(([key]) => key === 'photos')
|
||||
.map(([, arrayOfPhotoIds]) =>
|
||||
arrayOfPhotoIds.filter((id: string) => id !== photo1.$id)
|
||||
)
|
||||
);
|
||||
|
||||
// Pick a random photo that isn't photo1 and that hasn't been paired with it in any of the user's votes
|
||||
|
||||
const photo2IdsToExclude = [
|
||||
photo1.$id,
|
||||
...idsOfPhotosPairedWithPhoto1,
|
||||
...idsOfPhotosPairedWithAll.value
|
||||
];
|
||||
|
||||
const { documents: photo2Candidates } = await databases.listDocuments<Photo>(
|
||||
import.meta.env.VITE_DATABASE_ID,
|
||||
import.meta.env.VITE_PHOTOS_COLLECTION_ID,
|
||||
[Query.notEqual('$id', photo2IdsToExclude)]
|
||||
);
|
||||
|
||||
const maxPhoto2Index =
|
||||
import.meta.env.VITE_NUMBER_OF_PHOTOS - photo2IdsToExclude.length - 1;
|
||||
|
||||
const photo2 = photo2Candidates[randomNumber(0, maxPhoto2Index)];
|
||||
|
||||
photosInCurrentVote.value.push(photo1, photo2);
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
await loadPhotosAppearanceCount();
|
||||
await pickRandomPhotosAndCreateVote();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
showSnackbar({
|
||||
status: 'error',
|
||||
message: 'Failed to load photos'
|
||||
});
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
Loading…
Reference in New Issue
Block a user