Fotogalerie se zoomem, uprava verejnych avataru

This commit is contained in:
Michal Kváček
2023-08-10 00:44:17 +02:00
parent 3e33187ba5
commit 85bec30ca9
17 changed files with 466 additions and 239 deletions
+6
View File
@@ -0,0 +1,6 @@
import {boot} from "quasar/wrappers";
import InnerImageZoom from 'vue-inner-image-zoom';
export default boot(({app}) => {
app.component("inner-image-zoom", InnerImageZoom);
})
+20
View File
@@ -0,0 +1,20 @@
<script setup>
const props = defineProps({
size: {
type: Number,
required: false,
default: 120
},
src: {
type: String,
required: true
}
});
</script>
<template>
<q-avatar :size="`${size}px`">
<div :style="`width: ${size}px; height: ${size}px; background-image: url('${src}'); background-size: cover`">
</div>
</q-avatar>
</template>
+14 -12
View File
@@ -1,6 +1,6 @@
<script setup>
import {flight} from "src/composables/objects/flight";
import {formatDatetime, minutesToHumanReadable} from "src/composables/datetimeUtils";
import {FORMAT_DATE, FORMAT_TIME, formatDatetime, minutesToHumanReadable} from "src/composables/datetimeUtils";
import WeatherInfo from "components/weather/WeatherInfo.vue";
const props = defineProps({
@@ -14,7 +14,15 @@ const props = defineProps({
<template>
<q-list bordered separator>
<q-item>
<q-item-section avatar>
<q-icon name="event"/>
</q-item-section>
<q-item-section>
{{ formatDatetime(flight.takeoffDatetime, FORMAT_DATE) }}
</q-item-section>
</q-item>
<q-item>
<q-list>
<q-item>
<q-item-section avatar>
@@ -22,7 +30,8 @@ const props = defineProps({
</q-item-section>
<q-item-section>
<div>
{{ flight.takeoffAirport.name }}
<strong>{{ flight.takeoffAirport.name }}</strong>,
čas vzletu: {{ formatDatetime(flight.takeoffDatetime, FORMAT_TIME) }}
<WeatherInfo class="q-mx-sm" v-if="flight.takeoffWeatherInfo" :weather="flight.takeoffWeatherInfo"/>
</div>
@@ -44,7 +53,8 @@ const props = defineProps({
</q-item-section>
<q-item-section>
<div>
{{ flight.landingAirport.name }}
<strong>{{ flight.landingAirport.name }}</strong>,
čas přistání: {{ formatDatetime(flight.landingDatetime, FORMAT_TIME) }}
<WeatherInfo class="q-mx-sm" v-if="flight.landingWeatherInfo" :weather="flight.landingWeatherInfo"/>
</div>
</q-item-section>
@@ -52,15 +62,6 @@ const props = defineProps({
</q-list>
</q-item>
<q-item>
<q-item-section avatar>
<q-icon name="event"/>
</q-item-section>
<q-item-section>
{{ formatDatetime(flight.takeoffDatetime) }}
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-icon name="schedule"/>
@@ -74,6 +75,7 @@ const props = defineProps({
<q-icon name="people"/>
</q-item-section>
<q-item-section>
Link jen pro prihlasene!
<router-link :to="{name: 'copilotDetail', params: {id: flight.copilot.id}}">
{{ flight.copilot.name }}
</router-link>
+12 -7
View File
@@ -1,9 +1,10 @@
<script setup>
import {formatDatetime, minutesToHumanReadable} from "src/composables/datetimeUtils";
import {FORMAT_DATE, formatDatetime, minutesToHumanReadable} from "src/composables/datetimeUtils";
import {computed} from "vue";
import {useQuasar} from "quasar";
import {useFlightsStore} from "stores/Flights";
import {flightName} from "../../composables/objects/flight";
import AvatarFromPhoto from "components/AvatarFromPhoto.vue";
const props = defineProps({
items: {
@@ -33,15 +34,13 @@ const props = defineProps({
:to="{name: routeDetail.name, params:{...routeDetail.params, id: flight.id}}"
>
<q-item-section avatar>
<q-avatar size="150px">
<div v-if="flight.coverPhoto" :style="`width: 150px; height: 150px; background-image: url('${flight.coverPhoto.thumbnailUrl}'); background-size: cover`"></div>
</q-avatar>
<AvatarFromPhoto :size="120" v-if="flight.coverPhoto" :src="flight.coverPhoto.thumbnailUrl" />
</q-item-section>
<q-item-section>
<h5>{{ flightName(flight) }}</h5>
<div class="text-secondary">
<div class="text-h5">{{ flightName(flight) }}</div>
<div class="text-secondary q-mt-md">
<q-icon name="flight_takeoff"/>
{{ formatDatetime(flight.takeoffDatetime) }}
{{ formatDatetime(flight.takeoffDatetime, FORMAT_DATE) }}
</div>
<div class="text-secondary">
<q-icon name="schedule"/>
@@ -52,6 +51,12 @@ const props = defineProps({
<q-icon name="flight"/>
{{ flight.aircraft.callSign }}
</div>
<div class="text-secondary" v-if="flight.copilot">
<q-icon name="person" />
{{ flight.copilot.name }}
</div>
</q-item-section>
<q-item-section side top v-if="flight.isPublic && displayVisibilityInfo">
+47 -37
View File
@@ -2,6 +2,7 @@
import EmbeddedMap from "components/map/EmbeddedMap.vue";
import {computed, onMounted, ref} from "vue";
import {formatDatetime} from "src/composables/datetimeUtils";
import 'vue-inner-image-zoom/lib/vue-inner-image-zoom.css'
const props = defineProps({
photos: {
@@ -65,44 +66,41 @@ const showExtra = ref(false);
<q-card-section>
<div class="row">
<div class="col-12">
<q-img
:draggable="false"
style="max-height: 90vh"
fit="contain"
:src="photo.url"
<div class="text-center">
<inner-image-zoom move-type="pan" :zoomScale="0.75" :hide-hint="true" :src="photo.url"/>
</div>
<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
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>
</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>
<strong v-if="photo.name !== ''">{{ photo.name }}</strong>
<p v-if="photo.description !== ''">{{ photo.description }}</p>
</div>
@@ -148,3 +146,15 @@ const showExtra = ref(false);
</q-card>
</q-dialog>
</template>
<style>
.iiz {
width: 100%;
height: 90vh;
}
.iiz__img {
max-height: 90vh !important;
margin: 0 auto;
}
</style>
-13
View File
@@ -13,9 +13,6 @@ const props = defineProps({
}
});
const formData = reactive({
name: '',
publicUsername: '',
description: '',
avatarImage: null,
titleImage: null,
});
@@ -59,16 +56,6 @@ watch(() => props.user, (newVal) => {
<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>
+24 -5
View File
@@ -1,7 +1,11 @@
<script setup>
import {onMounted} from "vue";
import {onMounted, reactive, watch} from "vue";
import {useRoute} from "vue-router";
import {useUserStore} from "stores/User";
import {commonProps} from "src/composables/inputs";
import RichTextEditor from "components/inputs/RichTextEditor.vue";
import FormWrapper from "components/FormWrapper.vue";
import {setDefaultData} from "src/composables/form/formHandling";
const props = defineProps({
user: {
@@ -11,11 +15,14 @@ const props = defineProps({
}
})
const username = useRoute().params.username;
onMounted(() => {
useUserStore().fetchByUsername(username);
const formData = reactive({
name: '',
publicUsername: '',
description: '',
});
watch(() => props.user, (newVal) => {
setDefaultData(formData, props.user)
}, {immediate: true});
</script>
<template>
@@ -29,4 +36,16 @@ onMounted(() => {
<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>
<FormWrapper v-model="formData">
<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>
+20 -11
View File
@@ -38,15 +38,6 @@ const openDetail = ref(false);
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
QNH
</q-item-section>
<q-item-section>
{{ weather.qnh }} hPa
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-icon name="air"/>
@@ -58,14 +49,32 @@ const openDetail = ref(false);
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-icon name="water_drop"/>
</q-item-section>
<q-item-section>
{{ weather.rain }}mm
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-icon name="mdi-thermometer"/>
</q-item-section>
<q-item-section>
Teplota: {{ weather.temperatureSurface }} &deg; C
Teplota: {{ weather.temperatureSurface }}&deg; C
<br>
Rosný bod: {{ weather.dewpointSurface }} &deg; C
Rosný bod: {{ weather.dewpointSurface }}&deg; C
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
QNH
</q-item-section>
<q-item-section>
{{ weather.qnh }} hPa
</q-item-section>
</q-item>
</q-list>
+5
View File
@@ -91,6 +91,11 @@ fragment flightListInfo on Flight {
thumbnailUrl
}
copilot {
id
name
}
takeoffAirport {
...airportInfo
}
+4 -13
View File
@@ -15,11 +15,9 @@
{{ pageTitle || "Polétání.cz" }}
</q-toolbar-title>
<router-link :to="{name: 'profile'}">
<q-avatar>
<img :src="loggedUser.avatarImageUrl" alt=""/>
</q-avatar>
</router-link>
<router-link :to="{name: 'profile'}">
<AvatarFromPhoto :size="32" :src="loggedUser.avatarImageUrl"/>
</router-link>
</q-toolbar>
</q-header>
@@ -31,14 +29,6 @@
>
<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">
@@ -68,6 +58,7 @@ import {storeToRefs} from "pinia";
import BreadcrumbsNavigation from "components/BreadcrumbsNavigation.vue";
import {useAppStore} from "stores/App";
import {useMeta} from "quasar";
import AvatarFromPhoto from "components/AvatarFromPhoto.vue";
const {pageTitle, breadcrumbs} = storeToRefs(useAppStore());
useMeta(() => ({
+2 -3
View File
@@ -8,9 +8,7 @@
<template v-if="loggedUser.id">
<router-link :to="{name: 'myFlights'}">
<q-avatar>
<img :src="loggedUser.avatarImageUrl" alt=""/>
</q-avatar>
<AvatarFromPhoto :size="35" :src="loggedUser.avatarImageUrl"/>
</router-link>
</template>
<template v-else>
@@ -39,6 +37,7 @@ import {onMounted} from "vue";
import {useAppStore} from "stores/App";
import {useMeta} from "quasar";
import BreadcrumbsNavigation from "components/BreadcrumbsNavigation.vue";
import AvatarFromPhoto from "components/AvatarFromPhoto.vue";
const {pageTitle, breadcrumbs} = storeToRefs(useAppStore());
const loggedUser = storeToRefs(useUserStore()).user;
+3 -3
View File
@@ -1,6 +1,6 @@
<script setup>
import {useUserStore} from "stores/User";
import ProfileForm from "components/profile/ProfileForm.vue";
import ProfileImagesForm from "components/profile/ProfileForm.vue";
import {useAppStore} from "stores/App";
import {storeToRefs} from "pinia";
import {useQuasar} from "quasar";
@@ -23,14 +23,14 @@ const openedTab = ref("profile");
<template>
<q-tabs class="q-my-lg" v-model="openedTab">
<q-tab name="profile">Profil</q-tab>
<q-tab name="profile">Profilové fotky</q-tab>
<q-tab name="changePassword">Změna hesla</q-tab>
<q-tab name="publicProfile">Veřejný profil</q-tab>
</q-tabs>
<q-tab-panels v-model="openedTab">
<q-tab-panel name="profile">
<ProfileForm :user="loggedUser" @save="handleSave"/>
<ProfileImagesForm :user="loggedUser" @save="handleSave"/>
</q-tab-panel>
<q-tab-panel name="changePassword">
+20 -15
View File
@@ -27,24 +27,29 @@ watch(flight, (newVal) => {
<template>
<FlightDetailSkeleton v-if="loading || !flight"/>
<div>
<FlightDetailSkeleton v-if="loading || !flight"/>
<template v-else>
<q-banner class="bg-warning text-white q-mx-md q-my-md" v-if="!flight.isPublic">
Pozor! Tento let není publikován a nebude tak dostupný pro ostatní!
</q-banner>
<template v-else>
<q-banner class="bg-warning text-white q-mx-md q-my-md" v-if="!flight.isPublic">
Pozor! Tento let není publikován a nebude tak dostupný pro ostatní!
</q-banner>
<q-card class="q-mx-md q-my-md flight-detail-card">
<PhotoCarousel :cover-photo-url="flight.coverPhoto.url" :photos="flight.photos"/>
<q-card-section>
<FlightBasicInfo :flight="flight"/>
<q-card class="q-mx-md q-my-md flight-detail-card">
<PhotoCarousel
v-if="flight.coverPhoto"
:cover-photo-url="flight.coverPhoto.url"
:photos="flight.photos"
/>
<q-card-section>
<FlightBasicInfo :flight="flight"/>
<article class="q-my-lg" v-html="flight.description"/>
<article class="q-my-lg" v-html="flight.description"/>
<FlightTrackMap :flight="flight"/>
</q-card-section>
</q-card>
</template>
<FlightTrackMap :flight="flight"/>
</q-card-section>
</q-card>
</template>
</div>
</template>
+2 -3
View File
@@ -8,6 +8,7 @@ import {useFlightsStore} from "stores/Flights";
import {storeToRefs} from "pinia";
import FlightsListSkeleton from "components/flights/FlightsListSkeleton.vue";
import FlightsList from "components/flights/FlightsList.vue";
import AvatarFromPhoto from "components/AvatarFromPhoto.vue";
const props = defineProps({
loggedUser: {
@@ -55,9 +56,7 @@ const flights = storeToRefs(useFlightsStore()).items;
</div>
<div class="col-4 sm-hide">
<div class="text-center">
<q-avatar size="150px">
<img :src="pilot.avatarImageUrl" alt="">
</q-avatar>
<AvatarFromPhoto :size="150" :src="pilot.avatarImageUrl"/>
</div>
<h3>{{ pilot.name }}</h3>