Prvni naznak verejne casti, sprava profilu

This commit is contained in:
Michal Kváček
2023-08-09 00:26:59 +02:00
parent c4603cf090
commit 25250ae00e
31 changed files with 6505 additions and 786 deletions
+2 -2
View File
@@ -1,3 +1,3 @@
# ULL Tracker (app)
# Polétání.cz
https://starter.dev/kits/vue3-apollo-quasar/
+5400
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -31,7 +31,8 @@
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-vue": "^9.0.0",
"postcss": "^8.4.14",
"prettier": "^2.5.1"
"prettier": "^2.5.1",
"vite-plugin-rewrite-all": "^1.0.1"
},
"engines": {
"npm": ">= 6.13.4",
+3 -4
View File
@@ -8,7 +8,6 @@
// Configuration for your app
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
const {configure} = require('quasar/wrappers');
@@ -82,9 +81,9 @@ module.exports = configure(function (/* ctx */) {
// viteVuePluginOptions: {},
// vitePlugins: [
// [ 'package-name', { ..options.. } ]
// ]
vitePlugins: [
[ require("vite-plugin-rewrite-all").default ] // slouzi k podpore tecky v params v routeru
]
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
+22 -6
View File
@@ -9,6 +9,16 @@ const props = defineProps({
items: {
type: Array,
required: true
},
routeDetail: {
type: Object,
required: false,
default: () => ({name: 'flightDetail', params: {}})
},
displayVisibilityInfo: {
type: Boolean,
required: false,
default: false
}
});
@@ -16,14 +26,16 @@ const props = defineProps({
<template>
<q-list bordered separator class="q-mx-md q-my-md">
<q-item v-for="flight of items" :key="flight" ripple clickable :to="{name: 'flightDetail', params:{id: flight.id}}">
<q-item
v-for="flight of items" :key="flight"
ripple
clickable
:to="{name: routeDetail.name, params:{...routeDetail.params, id: flight.id}}"
>
<q-item-section avatar>
<q-avatar size="100px">
<div v-if="flight.coverPhoto" :style="`width: 100px; height: 100px; background-image: url('${flight.coverPhoto.thumbnailUrl}'); background-size: cover`"></div>
</q-avatar>
</q-item-section>
<q-item-section>
{{ flightName(flight) }}
@@ -32,8 +44,8 @@ const props = defineProps({
{{ formatDatetime(flight.takeoffDatetime) }}
</div>
<div class="text-secondary">
<q-icon name="schedule" />
{{ minutesToHumanReadable(flight.durationMinCalculated) }}
<q-icon name="schedule"/>
{{ minutesToHumanReadable(flight.durationMinCalculated) }}
</div>
<div class="text-secondary">
@@ -41,6 +53,10 @@ const props = defineProps({
{{ flight.aircraft.callSign }}
</div>
</q-item-section>
<q-item-section side top v-if="flight.isPublic && displayVisibilityInfo">
<q-icon name="visibility"></q-icon>
</q-item-section>
</q-item>
</q-list>
</template>
@@ -26,8 +26,13 @@ const formData = reactive({
outlined
standout
multiple
accept=".jpg, .jpeg"
v-model="formData.photos"
label="Vybert fotky k nahrání"
/>
>
<template #prepend>
<q-icon name="upload" />
</template>
</q-file>
</FormWrapper>
</template>
@@ -51,7 +51,17 @@ onMounted(() => {
<template>
<FormWrapper v-model="formData">
<q-file v-model="formData.gpxTrack" v-bind="commonProps" label="Trasa v GPX" hint="Slouží pro vykreslení kompletní a detailní trasy"/>
<q-file
accept=".gpx"
v-model="formData.gpxTrack"
v-bind="commonProps"
label="Nahrát trasu v GPX"
hint="Slouží pro vykreslení kompletní a detailní trasy"
>
<template #prepend>
<q-icon name="upload" />
</template>
</q-file>
<template v-if="flight.gpxTrackUrl">
<q-icon name="check" color="green" /> Trasa nahrána
@@ -64,7 +74,7 @@ onMounted(() => {
:options="locations"
@select="addTrackItem"
clearable
label="Otočný bod"
label="Přidat otočný bod"
hint="Nastav významné otočné body"
/>
+3 -3
View File
@@ -1,5 +1,5 @@
<script setup>
import {onMounted, ref, watch} from "vue";
import {ref, watch} from "vue";
const props = defineProps({
modelValue: {
@@ -26,9 +26,9 @@ const props = defineProps({
const emit = defineEmits(["update:modelValue"]);
const content = ref("");
onMounted(() => {
watch(() => props.modelValue, (newVal) => {
content.value = props.modelValue;
})
}, {immediate: true})
watch(content, () => emit("update:modelValue", content.value));
+59 -14
View File
@@ -48,29 +48,74 @@ const prevPhoto = () => {
if (photoIndex.value > 0) photoIndex.value--;
}
const showExtra = ref(false);
</script>
<template>
<q-dialog v-model="open" full-width @keyup.left="prevPhoto" @keyup.right="nextPhoto">
<q-card>
<q-dialog
v-model="open"
full-width
>
<q-card
v-touch-swipe.mouse.left="prevPhoto"
v-touch-swipe.mouse.right="nextPhoto"
@keyup.left="prevPhoto"
@keyup.right="nextPhoto"
>
<q-card-section>
<div class="row">
<div class="col-12 col-sm-12 col-md-9">
<q-img :src="photo.url"/>
<div class="col-12">
<q-img
:draggable="false"
style="max-height: 90vh"
fit="contain"
:src="photo.url"
>
<q-btn
class="absolute all-pointer-events"
round
flat
icon="info"
:text-color="showExtra ? 'primary' : 'white'"
style="background: rgba(0, 0, 0, 0.25); top: 8px; right: 8px"
@click="showExtra = !showExtra"
/>
<q-btn
class="absolute all-pointer-events"
style="background: rgba(0, 0, 0, 0.25); top: 50%; left: 8px;"
@click="prevPhoto"
v-if="photoIndex > 0"
icon="chevron_left"
text-color="white"
flat
rounded
>
</q-btn>
<q-btn
class="absolute all-pointer-events"
style="background: rgba(0, 0, 0, 0.25); top: 50%; right: 8px;"
@click="nextPhoto"
v-if="photoIndex < photos.length - 1"
icon-right="chevron_right"
text-color="white"
flat
rounded
>
</q-btn>
</q-img>
<strong v-if="photo.name !== ''">{{ photo.name }}</strong>
<p v-if="photo.description !== ''">{{ photo.description }}</p>
<div class="text-center q-my-sm">
<q-btn @click="prevPhoto" v-if="photoIndex > 0" icon="chevron_left" flat rounded>Předchozí</q-btn>
<q-btn @click="nextPhoto" v-if="photoIndex < photos.length - 1" icon-right="chevron_right" flat rounded>Další</q-btn>
</div>
</div>
<div class="col-12 col-sm-12 col-md-3">
<EmbeddedMap class="q-mx-sm" v-if="photo.gpsLatitude && photo.gpsLongitude" :gps-latitude="photo.gpsLatitude" :gps-longitude="photo.gpsLongitude"/>
<q-list>
<div class="col-12 q-my-md" v-if="showExtra">
<EmbeddedMap
v-if="photo.gpsLatitude && photo.gpsLongitude"
class="q-mx-sm"
:gps-latitude="photo.gpsLatitude"
:gps-longitude="photo.gpsLongitude"
/>
<q-list dense>
<q-item v-if="photo.pointOfInterest">
<q-item-section avatar>
<q-avatar icon="marker"></q-avatar>
@@ -0,0 +1,40 @@
<script setup>
import {reactive, ref} from "vue";
import FormWrapper from "components/FormWrapper.vue";
import {commonProps} from "src/composables/inputs";
const formData = reactive({
oldPassword: null,
newPassword: null,
});
const visiblePassword = ref(false);
</script>
<template>
<FormWrapper v-model="formData">
<q-input
v-model="formData.oldPassword"
type="password"
label="Staré heslo"
v-bind="commonProps"
/>
<q-input
v-model="formData.newPassword"
:type="visiblePassword ? 'text' : 'password'"
label="Nové heslo"
v-bind="commonProps"
>
<template #append>
<q-btn
flat
round
:icon="visiblePassword ? 'visibility' : 'visibility_off'"
@click="visiblePassword = !visiblePassword"
/>
</template>
</q-input>
</FormWrapper>
</template>
+74
View File
@@ -0,0 +1,74 @@
<script setup>
import FormWrapper from "components/FormWrapper.vue";
import {commonProps} from "src/composables/inputs";
import {reactive, watch} from "vue";
import {setDefaultData} from "src/composables/form/formHandling";
import RichTextEditor from "components/inputs/RichTextEditor.vue";
const props = defineProps({
user: {
type: Object,
required: false,
default: null
}
});
const formData = reactive({
name: null,
publicUsername: null,
description: null,
avatarImage: null,
titleImage: null,
});
watch(() => props.user, (newVal) => {
setDefaultData(formData, props.user)
}, {immediate: true});
</script>
<template>
<FormWrapper v-model="formData">
<div class="text-center">
<q-avatar size="200px">
<img :src="user.avatarImageUrl" alt="Profilový obrázek"/>
</q-avatar>
</div>
<q-file
v-model="formData.avatarImage"
accept="image/jpeg"
v-bind="commonProps"
label="Nahrát avatar"
>
<template #prepend>
<q-icon name="upload"/>
</template>
</q-file>
<div class="text-center">
<q-img :src="user.titleImageUrl" width="50%"/>
</div>
<q-file
v-model="formData.titleImage"
accept="image/jpeg"
v-bind="commonProps" label="Nahrát titulní fotku"
>
<template #prepend>
<q-icon name="upload"/>
</template>
</q-file>
<q-input v-model="formData.name" label="Jméno" v-bind="commonProps"/>
<q-input
v-model="formData.publicUsername"
label="Veřejné uživatelské jméno"
hint="Vyplň jméno pro zpřístupnění záznamů ve veřejné části aplikace"
v-bind="commonProps"
/>
<RichTextEditor v-model="formData.description" label="Veřejný podpis" />
</FormWrapper>
</template>
@@ -0,0 +1,32 @@
<script setup>
import {onMounted} from "vue";
import {useRoute} from "vue-router";
import {useUserStore} from "stores/User";
const props = defineProps({
user: {
type: Object,
required: false,
default: null
}
})
const username = useRoute().params.username;
onMounted(() => {
useUserStore().fetchByUsername(username);
});
</script>
<template>
<q-list>
<q-item v-if="user.publicUsername">
<router-link target="_blank" :to="{name: 'publicProfileDetail', params: {username: user.publicUsername}}">
@{{user.publicUsername}}
</router-link>
</q-item>
<q-item v-else>
<q-icon name="" color="red" /> Nastav si veřejné uživatelské jméno pro zpřístupnění profilu a sdílení záznamů s ostatními
</q-item>
</q-list>
</template>
+1 -1
View File
@@ -12,7 +12,7 @@
// to match your app's branding.
// Tip: Use the "Theme Builder" on Quasar's documentation website.
$primary : #1976D2;
$primary : #71bbde; //#1976D2;
$secondary : #26A69A;
$accent : #9C27B0;
-1
View File
@@ -10,7 +10,6 @@ const uploadLink = createUploadLink({
uri: 'http://localhost:8000/graphql',
})
const errorLink = onError(
({graphQLErrors, networkError, operation, forward}) => {
if (networkError?.statusCode === 401) {
+2
View File
@@ -26,6 +26,7 @@ fragment flightFullInfo on Flight {
landingDatetime
landings
durationMinCalculated
isPublic
coverPhoto {
thumbnailUrl
@@ -79,6 +80,7 @@ fragment flightListInfo on Flight {
takeoffDatetime
landingDatetime
durationMinCalculated
isPublic
coverPhoto {
thumbnailUrl
+14
View File
@@ -0,0 +1,14 @@
import gql from "graphql-tag";
export const FRAGMENT_USER_FULL_INFO = gql`
fragment userFullInfo on User {
id
name
email
avatarImageUrl
titleImageUrl
publicUsername
description
createdAt
}
`
+12
View File
@@ -0,0 +1,12 @@
import gql from "graphql-tag";
import {FRAGMENT_USER_FULL_INFO} from "src/graphql/fragments/user";
export const EDIT_USER = gql`
mutation editLoggedUser($input: EditUserInput!) {
editLoggedUser(input: $input) {
...userFullInfo
}
}
${FRAGMENT_USER_FULL_INFO}
`;
+23
View File
@@ -0,0 +1,23 @@
import gql from "graphql-tag";
import {FRAGMENT_USER_FULL_INFO} from "src/graphql/fragments/user";
export const USER_DETAIL = gql`
query userDetail($username: String!) {
user(username: $username) {
...userFullInfo
}
}
${FRAGMENT_USER_FULL_INFO}
`;
export const LOGGED_USER_DETAIL = gql`
query loggedUserDetail {
loggedUser {
...userFullInfo
}
}
${FRAGMENT_USER_FULL_INFO}
`;
+16 -17
View File
@@ -15,6 +15,12 @@
{{ pageTitle || "Polétání.cz" }}
</q-toolbar-title>
<router-link :to="{name: 'profile'}">
<q-avatar>
<img :src="loggedUser.avatarImageUrl" alt=""/>
</q-avatar>
</router-link>
</q-toolbar>
</q-header>
@@ -24,22 +30,15 @@
bordered
>
<q-img class="absolute-top" src="https://galerie.kvacek.cz/_data/i/upload/2022/06/22/20220622102027-b104fc0d-me.jpg" style="height: 150px">
<div class="absolute-bottom bg-transparent">
<router-link :to="{name: 'profile'}">
<q-avatar size="56px" class="q-mb-sm" v-if="loggedUser.avatarImageUrl">
<q-img :src="loggedUser.avatarImageUrl"/>
</q-avatar>
<q-avatar icon="person" color="primary" text-color="white" size="56px" v-else/>
</router-link>
<router-link
v-if="loggedUser.name !== ''"
:to="{name: 'profile'}"
class="text-weight-bold">
{{ loggedUser.name }}
</router-link>
</div>
<q-img class="absolute-top" :src="loggedUser.titleImageUrl" style="height: 150px">
<!-- <div class="absolute-bottom bg-transparent">-->
<!-- <router-link :to="{name: 'profile'}">-->
<!-- <q-avatar size="56px" class="q-mb-sm" v-if="loggedUser.avatarImageUrl">-->
<!-- <q-img :src="loggedUser.avatarImageUrl"/>-->
<!-- </q-avatar>-->
<!-- <q-avatar icon="person" color="primary" text-color="white" size="56px" v-else/>-->
<!-- </router-link>-->
<!-- </div>-->
</q-img>
<q-scroll-area style="height: calc(100% - 150px); margin-top: 150px; border-right: 1px solid #ddd">
<q-list class="q-my-lg">
@@ -112,7 +111,7 @@ const toggleLeftDrawer = () => {
};
onMounted(() => {
useUserStore().setupCredentials();
useUserStore().fetchLoggedUserDetail();
})
const loggedUser = storeToRefs(useUserStore()).user;
</script>
+39 -3
View File
@@ -1,18 +1,54 @@
<template>
<q-layout view="lHh Lpr lFf">
<q-header elevated>
<q-header>
<q-toolbar>
<q-toolbar-title>
Polétání.cz
</q-toolbar-title>
<template v-if="loggedUser.id">
<router-link :to="{name: 'myFlights'}">
<q-avatar>
<img :src="loggedUser.avatarImageUrl" alt=""/>
</q-avatar>
</router-link>
</template>
<template v-else>
<router-link :to="{name: 'login'}">
<q-avatar icon="person">
<q-tooltip>
Login
</q-tooltip>
</q-avatar>
</router-link>
</template>
</q-toolbar>
</q-header>
<q-page-container>
<router-view/>
<router-view :loggedUser="loggedUser" class="q-py-md"/>
</q-page-container>
</q-layout>W
</q-layout>
</template>
<script setup>
import {storeToRefs} from "pinia";
import {useUserStore} from "stores/User";
import {onMounted} from "vue";
import {useAppStore} from "stores/App";
import {useMeta} from "quasar";
const {pageTitle} = storeToRefs(useAppStore());
const loggedUser = storeToRefs(useUserStore()).user;
useMeta(() => ({
title: pageTitle.value !== '' ? `${pageTitle.value} - Polétání.cz` : 'Polétání.cz'
}));
onMounted(() => {
if (useUserStore().getAccessToken()) {
useUserStore().fetchLoggedUserDetail();
}
})
</script>
+37 -5
View File
@@ -1,13 +1,45 @@
<script setup>
import {useUserStore} from "stores/User";
import {onMounted} from "vue";
import {useFlightsStore} from "stores/Flights";
import ProfileForm from "components/profile/ProfileForm.vue";
import {useAppStore} from "stores/App";
import {storeToRefs} from "pinia";
import {useQuasar} from "quasar";
import {successObject} from "src/composables/dialog";
import {ref} from "vue";
import ChangePasswordForm from "components/profile/ChangePasswordForm.vue";
import PublicProfileInfo from "components/profile/PublicProfileInfo.vue";
const loggedUser = useUserStore().user
useAppStore().setPageTitle("Profil");
const loggedUser = storeToRefs(useUserStore()).user;
const $q = useQuasar();
const handleSave = (formData) => {
useUserStore().update(loggedUser.id, formData).then((user) => {
$q.notify(successObject("Uspesne upraveno"));
});
};
const openedTab = ref("profile");
</script>
<template>
Profile page
<q-tabs class="q-my-lg" v-model="openedTab">
<q-tab name="profile">Profil</q-tab>
<q-tab name="changePassword">Změna hesla</q-tab>
<q-tab name="publicProfile">Veřejný profil</q-tab>
</q-tabs>
{{ loggedUser }}
<q-tab-panels v-model="openedTab">
<q-tab-panel name="profile">
<ProfileForm :user="loggedUser" @save="handleSave"/>
</q-tab-panel>
<q-tab-panel name="changePassword">
<ChangePasswordForm @save="handleSave" />
</q-tab-panel>
<q-tab-panel name="publicProfile">
<PublicProfileInfo :user="loggedUser" />
</q-tab-panel>
</q-tab-panels>
</template>
@@ -41,7 +41,7 @@ watch(aircraft, (newVal) => {
<AircraftDetailSkeleton v-if="loading || !aircraft"/>
<template v-else>
<q-card>
<q-card class="q-mx-md q-my-md flight-detail-card">
<q-img :src="aircraft.photoUrl"/>
<q-card-section>
+33 -4
View File
@@ -10,14 +10,16 @@ import {successObject} from "src/composables/dialog";
import MapWithMarkers from "components/map/MapWithMarkers.vue";
import FAB from "components/FAB.vue";
import PhotoDetail from "components/photos/PhotoDetail.vue";
import {useFlightsStore} from "stores/Flights";
const $q = useQuasar();
const $router = useRouter();
const displayedPhoto = ref(null);
const flightId = parseInt(useRoute().params.id);
onMounted(() => {
fetchFlight(useRoute().params.id).then(() => {
fetchFlight(flightId).then(() => {
if (flight.value.photos.length === 0) return;
displayedPhoto.value = flight.value.coverPhoto.url || flight.value.photos[0].url;
})
@@ -91,6 +93,12 @@ const displayMap = computed(() => {
flight.value.landingAirport.id !== flight.value.takeoffAirport.id
)
});
const togglePublicVisibility = () => {
useFlightsStore().update(flightId, {isPublic: !flight.value.isPublic}).then(() => {
$q.notify(successObject("Uspesne nastaveno"));
})
}
</script>
@@ -98,7 +106,7 @@ const displayMap = computed(() => {
<FlightDetailSkeleton v-if="loading || !flight"/>
<template v-else>
<q-card class="q-mx-md q-my-md">
<q-card class="q-mx-md q-my-md flight-detail-card">
<q-carousel
v-if="flight.photos.length > 0"
animated
@@ -128,13 +136,34 @@ const displayMap = computed(() => {
</q-card-section>
<q-card-actions align="right">
<q-btn @click="togglePublicVisibility" :icon="flight.isPublic ? 'visibility' : 'visibility_off'" rounded flat text-color="primary">
<q-tooltip>
{{ flight.isPublic ? 'je verejny' : 'neni verejny'}}
</q-tooltip>
</q-btn>
<q-btn @click="handleDeleteAndRedirect" icon="delete" rounded flat text-color="red"/>
</q-card-actions>
</q-card>
<PhotoDetail v-if="photoData" v-model="openPhotoDetail" :photo="photoData.photo" :photos="photoData.allPhotos" :opened-index="photoData.idx"/>
<PhotoDetail
v-if="photoData"
v-model="openPhotoDetail"
:photo="photoData.photo"
:photos="photoData.allPhotos"
:opened-index="photoData.idx"
/>
<FAB :router-link="{name: 'flightEdit', params:{id: flight.id}}" mode="edit"/>
</template>
</template>
<style>
.flight-detail-card {
width: 100%;
max-width: 700px;
margin: 0 auto;
}
</style>
+1 -3
View File
@@ -1,5 +1,5 @@
<script setup>
import {onMounted, ref} from "vue";
import {onMounted} from "vue";
import {useAircraftsStore} from "stores/Aircrafts";
import {useFlightsStore} from "stores/Flights";
import {useRouter} from "vue-router";
@@ -39,7 +39,5 @@ const handleSave = (formData) => {
<template>
<FlightFormSteps :disabled="['copilot', 'track', 'photos', 'notes']"/>
<FlightFormBasic @save="handleSave" :aircrafts="aircrafts" :airports="airports" :locations="locations" />
</template>
+9
View File
@@ -0,0 +1,9 @@
<script setup>
</script>
<template>
<div>
Detail letu
</div>
</template>
+73
View File
@@ -0,0 +1,73 @@
<script setup>
import {FORMAT_DATE, formatDatetime} from "src/composables/datetimeUtils";
import {useAppStore} from "stores/App";
import {onMounted, ref} from "vue";
import {useUserStore} from "stores/User";
import {useRoute} from "vue-router";
import {useFlightsStore} from "stores/Flights";
import {storeToRefs} from "pinia";
import FlightsListSkeleton from "components/flights/FlightsListSkeleton.vue";
import FlightsList from "components/flights/FlightsList.vue";
const props = defineProps({
loggedUser: {
type: Object,
required: false
}
})
const username = useRoute().params.username;
const pilot = ref(null)
const loading = ref(true);
onMounted(() => {
loading.value = true;
useUserStore().fetchDetail(username).then((user) => {
pilot.value = user;
useAppStore().setPageTitle(pilot.value.name);
}).finally(() => {
loading.value = false
});
useFlightsStore().fetch(username);
})
const flights = storeToRefs(useFlightsStore()).items;
</script>
<template>
<FlightsListSkeleton v-if="loading || !pilot"/>
<div class="row" v-else>
<div class="col">
<q-banner class="bg-primary text-white" v-if="flights.length === 0">
Tento pilot zatím nepublikoval žádný let.
</q-banner>
<template v-else>
<h3>Lety</h3>
<FlightsList
:items="flights"
:displayVisibilityInfo="false"
:route-detail="{name: 'publicFlightDetail', params: {username: username}}"
/>
</template>
</div>
<div class="col-4 sm-hide">
<div class="text-center">
<q-avatar size="150px">
<img :src="pilot.avatarImageUrl" alt="">
</q-avatar>
</div>
<h3>{{ pilot.name }}</h3>
<div class="q-my-lg" v-html="pilot.description"/>
Registrován: {{ formatDatetime(pilot.createdAt, FORMAT_DATE) }}
</div>
</div>
</template>
+2
View File
@@ -7,6 +7,8 @@ const routes = [
{path: 'login', name: "login", component: () => import('pages/public/LoginPage.vue')},
{path: 'register', name: "registration", component: () => import('pages/public/RegisterPage.vue')},
{path: 'about', name: "about", component: () => import('pages/public/AboutPage.vue')},
{path: ':username', name: "publicProfileDetail", component: () => import('pages/public/PilotProfilePage.vue')},
{path: ':username/:id', name: "publicFlightDetail", component: () => import('pages/public/FlightDetailPage.vue')},
]
},
{
+2 -2
View File
@@ -35,9 +35,9 @@ export const useFlightsStore = defineStore('Flights', {
}
return apolloWrapper.query(FLIGHT_DETAIL, {id}).then(result => result.data.flight);
},
fetch() {
fetch(username) {
return apolloWrapper
.query(FLIGHTS_LIST)
.query(FLIGHTS_LIST, {username})
.then(({data}) => {
this.items = data.flights;
+51 -31
View File
@@ -1,45 +1,33 @@
import {defineStore} from 'pinia'
import {api} from "boot/axios";
import {apolloWrapper} from "src/graphql/apollo";
import {EDIT_USER} from "src/graphql/mutations/user";
import {LOGGED_USER_DETAIL, USER_DETAIL} from "src/graphql/queries/user";
export const useUserStore = defineStore('LoggedUser', {
state: () => ({
user: {},
isRefreshing: false,
}),
actions: {
setTokens({access_token, access_token_validity}) {
const now = new Date().getTime() / 1000;
localStorage.setItem("accessToken", access_token);
localStorage.setItem("accessTokenValidity", now + access_token_validity);
},
getAccessToken() {
const tokenValidity = localStorage.getItem("accessTokenValidity");
const now = new Date().getTime() / 1000;
if (tokenValidity < now) {
localStorage.removeItem("accessToken");
localStorage.removeItem("accessTokenValidity");
}
return localStorage.getItem(`accessToken`);
},
setupCredentials() {
return api.get("/me").then(({data}) => {
this.user = data;
return this.user;
}).catch(async ({response}) => {
console.log(response);
return response;
update(id, formData) {
apolloWrapper.mutate(EDIT_USER, {id, input: formData}).then(({data}) => {
this.user = data.editLoggedUser;
return data.editLoggedUser;
});
},
async refreshAccessToken() {
return api.post("/refresh").then(({data}) => {
this.setTokens(data);
return data.access_token;
fetchLoggedUserDetail() {
return apolloWrapper.query(LOGGED_USER_DETAIL).then(({data}) => {
this.user = data.loggedUser;
return data.loggedUser;
})
},
fetchDetail(username) {
return apolloWrapper.query(USER_DETAIL, {username}).then(({data}) => {
return data.user
});
},
@@ -60,6 +48,38 @@ export const useUserStore = defineStore('LoggedUser', {
Object.assign(this.user, {});
localStorage.removeItem("accessToken");
localStorage.removeItem("accessTokenValidity");
}
},
setTokens({access_token, access_token_validity}) {
const now = new Date().getTime() / 1000;
localStorage.setItem("accessToken", access_token);
localStorage.setItem("accessTokenValidity", now + access_token_validity);
},
getAccessToken() {
const tokenValidity = localStorage.getItem("accessTokenValidity");
const now = new Date().getTime() / 1000;
if (tokenValidity < now) {
localStorage.removeItem("accessToken");
localStorage.removeItem("accessTokenValidity");
}
return localStorage.getItem(`accessToken`);
},
async refreshAccessToken() {
if (this.isRefreshing) return;
this.isRefreshing = true;
return api.post("/refresh")
.then(({data}) => {
this.setTokens(data);
return data.access_token;
})
.finally(() => {
this.isRefreshing = false;
});
},
}
})
+534 -685
View File
File diff suppressed because it is too large Load Diff