refactor(composables): migrate stores →
composables, align with updated API
- Replace deleted Pinia stores with
module-level singleton composables
(useAuth, useChat, useFeed, useUi) — all
return reactive({...}) so
Refs auto-unwrap in both templates and
script code
- Align entire codebase with new
swagger-generated api.ts types:
· TagDto.value (was .name) — FeedCard,
FeedFilters, ProfileEditor,
ProfileSetupView, MyProfileView,
ProfileDetailView, useUi
· MediaItemDto[] / .path (was mediaUrls[],
avatarUrl) — FeedCard,
FeedView, MyProfileView,
ProfileDetailView
· ChatDto.status 'active'|'closed' (was
isActive: boolean) —
ChatRoomView, ChatsListView
· MessageDto.profileId (was senderId) —
ChatRoomView, ChatBubble
· MeResponseDto → fetchMe now calls /me +
/profiles/my in parallel
· Token refresh: res.data.data.accessToken
(nested wrapper) —
router/index.ts aligned with client.ts
interceptor
- Fix FeedCard, ChatBubble imports pointing
to deleted store files
- Fix ProfileSetupView form type to avoid
string|undefined on v-model
- Fix history.back() → window.history.back()
via goBack() helper
- Fix chat.unreadCount possibly-undefined
guard in ChatsListView
- Fix MapPicker Leaflet icon cast (as unknown
as Record<string, unknown>)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useUiStore } from '@/stores/ui.store';
|
||||
import { useUi } from '@/composables/useUi';
|
||||
import { apiClient } from '@/api/client';
|
||||
import AppButton from '@/components/common/AppButton.vue';
|
||||
import LoadingSpinner from '@/components/common/LoadingSpinner.vue';
|
||||
@@ -17,7 +17,7 @@ interface Report {
|
||||
reporterName?: string;
|
||||
}
|
||||
|
||||
const uiStore = useUiStore();
|
||||
const uiStore = useUi();
|
||||
const reports = ref<Report[]>([]);
|
||||
const loading = ref(false);
|
||||
|
||||
|
||||
@@ -3,15 +3,15 @@ import { reactive, ref } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useVuelidate } from '@vuelidate/core';
|
||||
import { required, helpers } from '@vuelidate/validators';
|
||||
import { useAuthStore } from '@/stores/auth.store';
|
||||
import { useUiStore } from '@/stores/ui.store';
|
||||
import { useAuth } from '@/composables/useAuth';
|
||||
import { useUi } from '@/composables/useUi';
|
||||
import AppInput from '@/components/common/AppInput.vue';
|
||||
import AppButton from '@/components/common/AppButton.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const authStore = useAuthStore();
|
||||
const uiStore = useUiStore();
|
||||
const authStore = useAuth();
|
||||
const uiStore = useUi();
|
||||
|
||||
const form = reactive({ phone: '', password: '' });
|
||||
const loading = ref(false);
|
||||
|
||||
@@ -3,14 +3,14 @@ import { reactive, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useVuelidate } from '@vuelidate/core';
|
||||
import { required, minLength, helpers } from '@vuelidate/validators';
|
||||
import { useAuthStore } from '@/stores/auth.store';
|
||||
import { useUiStore } from '@/stores/ui.store';
|
||||
import { useAuth } from '@/composables/useAuth';
|
||||
import { useUi } from '@/composables/useUi';
|
||||
import AppInput from '@/components/common/AppInput.vue';
|
||||
import AppButton from '@/components/common/AppButton.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const authStore = useAuthStore();
|
||||
const uiStore = useUiStore();
|
||||
const authStore = useAuth();
|
||||
const uiStore = useUi();
|
||||
|
||||
const form = reactive({ phone: '', password: '', confirmPassword: '' });
|
||||
const loading = ref(false);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, nextTick, computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useAuthStore } from '@/stores/auth.store';
|
||||
import { useChatStore } from '@/stores/chat.store';
|
||||
import { useUiStore } from '@/stores/ui.store';
|
||||
import { useAuth } from '@/composables/useAuth';
|
||||
import { useChat } from '@/composables/useChat';
|
||||
import { useUi } from '@/composables/useUi';
|
||||
import { apiClient } from '@/api/client';
|
||||
import type { ChatMessage } from '@/stores/chat.store';
|
||||
import type { ChatMessage } from '@/composables/useChat';
|
||||
import ChatBubble from '@/components/chat/ChatBubble.vue';
|
||||
import ChatInput from '@/components/chat/ChatInput.vue';
|
||||
import AppModal from '@/components/common/AppModal.vue';
|
||||
@@ -13,9 +13,9 @@ import AppButton from '@/components/common/AppButton.vue';
|
||||
import LoadingSpinner from '@/components/common/LoadingSpinner.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const authStore = useAuthStore();
|
||||
const chatStore = useChatStore();
|
||||
const uiStore = useUiStore();
|
||||
const authStore = useAuth();
|
||||
const chatStore = useChat();
|
||||
const uiStore = useUi();
|
||||
|
||||
const chatId = route.params.chatId as string;
|
||||
const profileId = computed(() => authStore.activeProfile?.id ?? '');
|
||||
@@ -23,7 +23,7 @@ const messagesEnd = ref<HTMLElement | null>(null);
|
||||
const confirmClose = ref(false);
|
||||
|
||||
const chat = computed(() => chatStore.chats.find((c) => c.id === chatId));
|
||||
const isLocked = computed(() => chat.value && !chat.value.isActive);
|
||||
const isLocked = computed(() => chat.value?.status === 'closed');
|
||||
|
||||
// Group messages by date
|
||||
const groupedMessages = computed(() => {
|
||||
@@ -66,12 +66,14 @@ async function send(text: string, mediaUrl?: string, mediaType?: 'photo' | 'voic
|
||||
}
|
||||
}
|
||||
|
||||
function goBack() { window.history.back(); }
|
||||
|
||||
async function doCloseChat() {
|
||||
if (!profileId.value) return;
|
||||
try {
|
||||
await chatStore.closeChat(chatId, profileId.value);
|
||||
confirmClose.value = false;
|
||||
history.back();
|
||||
goBack();
|
||||
} catch {
|
||||
uiStore.addToast('Не удалось закрыть чат', 'error');
|
||||
}
|
||||
@@ -82,20 +84,20 @@ async function doCloseChat() {
|
||||
<div class="chat-room">
|
||||
<!-- Header -->
|
||||
<header class="chat-room__header">
|
||||
<button class="chat-room__back" @click="history.back()" aria-label="Назад">
|
||||
<button class="chat-room__back" @click="goBack()" aria-label="Назад">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" width="20" height="20">
|
||||
<path d="M19 12H5M12 5l-7 7 7 7"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="chat-room__partner">
|
||||
<img
|
||||
v-if="chat?.partner.avatarUrl"
|
||||
v-if="chat?.partner?.avatarUrl"
|
||||
:src="chat.partner.avatarUrl"
|
||||
class="chat-room__avatar"
|
||||
:alt="chat.partner.name"
|
||||
:alt="chat.partner?.name"
|
||||
/>
|
||||
<div v-else class="chat-room__avatar chat-room__avatar--placeholder" />
|
||||
<span class="chat-room__name">{{ chat?.partner.name ?? 'Чат' }}</span>
|
||||
<span class="chat-room__name">{{ chat?.partner?.name ?? 'Чат' }}</span>
|
||||
</div>
|
||||
<button
|
||||
class="chat-room__close-btn"
|
||||
@@ -135,7 +137,7 @@ async function doCloseChat() {
|
||||
v-for="msg in group.messages"
|
||||
:key="msg.id"
|
||||
:message="msg"
|
||||
:is-mine="msg.senderId === authStore.activeProfile?.id"
|
||||
:is-mine="msg.profileId === authStore.activeProfile?.id"
|
||||
/>
|
||||
</div>
|
||||
<div ref="messagesEnd" />
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useAuthStore } from '@/stores/auth.store';
|
||||
import { useChatStore } from '@/stores/chat.store';
|
||||
import { useUiStore } from '@/stores/ui.store';
|
||||
import { useAuth } from '@/composables/useAuth';
|
||||
import { useChat } from '@/composables/useChat';
|
||||
import { useUi } from '@/composables/useUi';
|
||||
import LoadingSpinner from '@/components/common/LoadingSpinner.vue';
|
||||
import EmptyState from '@/components/common/EmptyState.vue';
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const chatStore = useChatStore();
|
||||
const uiStore = useUiStore();
|
||||
const authStore = useAuth();
|
||||
const chatStore = useChat();
|
||||
const uiStore = useUi();
|
||||
const loading = ref(false);
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -55,13 +55,13 @@ function formatTime(dateStr: string) {
|
||||
<RouterLink
|
||||
:to="`/chats/${chat.id}`"
|
||||
class="chat-item"
|
||||
:class="{ 'chat-item--inactive': !chat.isActive }"
|
||||
:class="{ 'chat-item--inactive': chat.status === 'closed' }"
|
||||
>
|
||||
<div class="chat-item__avatar-wrap">
|
||||
<img
|
||||
v-if="chat.partner.avatarUrl"
|
||||
v-if="chat.partner?.avatarUrl"
|
||||
:src="chat.partner.avatarUrl"
|
||||
:alt="chat.partner.name"
|
||||
:alt="chat.partner?.name"
|
||||
class="chat-item__avatar"
|
||||
/>
|
||||
<div v-else class="chat-item__avatar chat-item__avatar--placeholder">
|
||||
@@ -70,7 +70,7 @@ function formatTime(dateStr: string) {
|
||||
</svg>
|
||||
</div>
|
||||
<!-- Lock icon for inactive chats -->
|
||||
<div v-if="!chat.isActive" class="chat-item__lock" aria-label="Чат неактивен">
|
||||
<div v-if="chat.status === 'closed'" class="chat-item__lock" aria-label="Чат неактивен">
|
||||
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" width="12" height="12">
|
||||
<rect x="3" y="7" width="10" height="8" rx="1"/>
|
||||
<path d="M5 7V5a3 3 0 0 1 6 0v2"/>
|
||||
@@ -80,7 +80,7 @@ function formatTime(dateStr: string) {
|
||||
|
||||
<div class="chat-item__content">
|
||||
<div class="chat-item__top">
|
||||
<span class="chat-item__name">{{ chat.partner.name }}</span>
|
||||
<span class="chat-item__name">{{ chat.partner?.name }}</span>
|
||||
<span v-if="chat.lastMessage" class="chat-item__time meta">
|
||||
{{ formatTime(chat.lastMessage.createdAt) }}
|
||||
</span>
|
||||
@@ -91,7 +91,7 @@ function formatTime(dateStr: string) {
|
||||
<p v-else class="chat-item__preview chat-item__preview--empty">Начните переписку</p>
|
||||
</div>
|
||||
|
||||
<div v-if="chat.unreadCount > 0" class="chat-item__badge">{{ chat.unreadCount }}</div>
|
||||
<div v-if="(chat.unreadCount ?? 0) > 0" class="chat-item__badge">{{ chat.unreadCount }}</div>
|
||||
</RouterLink>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { useAuthStore } from '@/stores/auth.store';
|
||||
import { useUiStore } from '@/stores/ui.store';
|
||||
import { useAuth } from '@/composables/useAuth';
|
||||
import { useUi } from '@/composables/useUi';
|
||||
import { apiClient } from '@/api/client';
|
||||
import AppButton from '@/components/common/AppButton.vue';
|
||||
import AppModal from '@/components/common/AppModal.vue';
|
||||
@@ -22,8 +22,8 @@ interface DateItem {
|
||||
isIncoming: boolean;
|
||||
}
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const uiStore = useUiStore();
|
||||
const authStore = useAuth();
|
||||
const uiStore = useUi();
|
||||
|
||||
const dates = ref<DateItem[]>([]);
|
||||
const statuses = ref<DateStatus[]>([]);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { useFeedStore } from '@/stores/feed.store';
|
||||
import { useAuthStore } from '@/stores/auth.store';
|
||||
import { useFeed } from '@/composables/useFeed';
|
||||
import { useAuth } from '@/composables/useAuth';
|
||||
import FeedCardStack from '@/components/feed/FeedCardStack.vue';
|
||||
import FeedFilters from '@/components/feed/FeedFilters.vue';
|
||||
import AppButton from '@/components/common/AppButton.vue';
|
||||
|
||||
const feedStore = useFeedStore();
|
||||
const authStore = useAuthStore();
|
||||
const feedStore = useFeed();
|
||||
const authStore = useAuth();
|
||||
|
||||
const filtersOpen = ref(false);
|
||||
const viewMode = ref<'stack' | 'scroll'>('stack');
|
||||
@@ -83,8 +83,8 @@ onMounted(() => {
|
||||
>
|
||||
<RouterLink :to="`/profile/${profile.id}`" class="feed-grid__link">
|
||||
<img
|
||||
v-if="profile.avatarUrl"
|
||||
:src="profile.avatarUrl"
|
||||
v-if="profile.media?.[0]?.path"
|
||||
:src="profile.media[0].path"
|
||||
:alt="profile.name"
|
||||
class="feed-grid__img"
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useAuthStore } from '@/stores/auth.store';
|
||||
import { useChatStore } from '@/stores/chat.store';
|
||||
import { useUiStore } from '@/stores/ui.store';
|
||||
import { useAuth } from '@/composables/useAuth';
|
||||
import { useChat } from '@/composables/useChat';
|
||||
import { useUi } from '@/composables/useUi';
|
||||
import { apiClient } from '@/api/client';
|
||||
import { useRouter } from 'vue-router';
|
||||
import AppButton from '@/components/common/AppButton.vue';
|
||||
@@ -23,9 +23,9 @@ interface Match {
|
||||
hasChat: boolean;
|
||||
}
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const chatStore = useChatStore();
|
||||
const uiStore = useUiStore();
|
||||
const authStore = useAuth();
|
||||
const chatStore = useChat();
|
||||
const uiStore = useUi();
|
||||
const router = useRouter();
|
||||
|
||||
const matches = ref<Match[]>([]);
|
||||
|
||||
@@ -3,35 +3,35 @@ import { reactive, ref, computed, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useVuelidate } from '@vuelidate/core';
|
||||
import { required, helpers } from '@vuelidate/validators';
|
||||
import { useAuthStore } from '@/stores/auth.store';
|
||||
import { useUiStore } from '@/stores/ui.store';
|
||||
import { useAuth } from '@/composables/useAuth';
|
||||
import { useUi } from '@/composables/useUi';
|
||||
import { apiClient } from '@/api/client';
|
||||
import type { CreateProfileDto } from '@/api/api';
|
||||
import type { UserProfile } from '@/stores/auth.store';
|
||||
import type { District } from '@/stores/ui.store';
|
||||
import type { UserProfile } from '@/composables/useAuth';
|
||||
import type { District } from '@/composables/useUi';
|
||||
import AppInput from '@/components/common/AppInput.vue';
|
||||
import AppButton from '@/components/common/AppButton.vue';
|
||||
import LoadingSpinner from '@/components/common/LoadingSpinner.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const authStore = useAuthStore();
|
||||
const uiStore = useUiStore();
|
||||
const authStore = useAuth();
|
||||
const uiStore = useUi();
|
||||
|
||||
const step = ref(1);
|
||||
const totalSteps = 4;
|
||||
const loading = ref(false);
|
||||
|
||||
const form = reactive<CreateProfileDto & { confirmStep?: number }>({
|
||||
const form = reactive({
|
||||
name: '',
|
||||
birthDate: '',
|
||||
gender: 'female',
|
||||
gender: 'female' as 'female' | 'male',
|
||||
cityId: '',
|
||||
districtId: '',
|
||||
description: '',
|
||||
nation: '',
|
||||
height: undefined,
|
||||
weight: undefined,
|
||||
tagIds: [],
|
||||
height: undefined as number | undefined | null,
|
||||
weight: undefined as number | undefined | null,
|
||||
tagIds: [] as string[],
|
||||
});
|
||||
|
||||
const selectedTags = ref<string[]>([]);
|
||||
@@ -85,7 +85,7 @@ async function finish() {
|
||||
loading.value = true;
|
||||
try {
|
||||
form.tagIds = selectedTags.value;
|
||||
const profile = await apiClient.api.profilesControllerCreate(form) as unknown as UserProfile;
|
||||
const profile = await apiClient.api.profilesControllerCreate(form as unknown as CreateProfileDto) as unknown as UserProfile;
|
||||
authStore.addProfile(profile);
|
||||
uiStore.addToast('Профиль создан', 'success');
|
||||
router.replace('/feed');
|
||||
@@ -207,7 +207,7 @@ function skip() {
|
||||
:class="{ 'setup__tag--active': selectedTags.includes(tag.id) }"
|
||||
@click="toggleTag(tag.id)"
|
||||
>
|
||||
{{ tag.name }}
|
||||
{{ tag.value }}
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="uiStore.tags.length === 0" class="setup__hint setup__hint--muted">Теги загружаются...</p>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import { useAuthStore } from '@/stores/auth.store';
|
||||
import { useUiStore } from '@/stores/ui.store';
|
||||
import { useAuth } from '@/composables/useAuth';
|
||||
import { useUi } from '@/composables/useUi';
|
||||
import { apiClient } from '@/api/client';
|
||||
import type { UserProfile } from '@/stores/auth.store';
|
||||
import type { UserProfile } from '@/composables/useAuth';
|
||||
import ProfileEditor from '@/components/profile/ProfileEditor.vue';
|
||||
import MediaGallery from '@/components/profile/MediaGallery.vue';
|
||||
import AppButton from '@/components/common/AppButton.vue';
|
||||
import AppModal from '@/components/common/AppModal.vue';
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const uiStore = useUiStore();
|
||||
const authStore = useAuth();
|
||||
const uiStore = useUi();
|
||||
|
||||
const editing = ref(false);
|
||||
const confirmDelete = ref(false);
|
||||
@@ -41,12 +41,12 @@ function logout() {
|
||||
authStore.logout();
|
||||
}
|
||||
|
||||
function cityName(cityId?: string) {
|
||||
function cityName(cityId?: string | null) {
|
||||
return uiStore.cities.find((c) => c.id === cityId)?.name ?? '';
|
||||
}
|
||||
|
||||
function tagNames(tagIds?: string[]) {
|
||||
return (tagIds ?? []).map((id) => uiStore.tags.find((t) => t.id === id)?.name ?? id);
|
||||
function tagValues(tags?: Array<{ id: string; value: string }>) {
|
||||
return (tags ?? []).map((t) => t.value);
|
||||
}
|
||||
|
||||
function calcAge(birthDate: string) {
|
||||
@@ -66,8 +66,8 @@ function calcAge(birthDate: string) {
|
||||
<div class="my-profile__hero">
|
||||
<div class="my-profile__avatar-wrap">
|
||||
<img
|
||||
v-if="profile.avatarUrl"
|
||||
:src="profile.avatarUrl"
|
||||
v-if="profile.media?.[0]?.path"
|
||||
:src="profile.media[0].path"
|
||||
:alt="profile.name"
|
||||
class="my-profile__avatar"
|
||||
/>
|
||||
@@ -113,10 +113,10 @@ function calcAge(birthDate: string) {
|
||||
</div>
|
||||
|
||||
<!-- Tags -->
|
||||
<div v-if="profile.tagIds?.length" class="my-profile__section">
|
||||
<div v-if="profile.tags?.length" class="my-profile__section">
|
||||
<h3 class="my-profile__section-title">Интересы</h3>
|
||||
<div class="my-profile__tags">
|
||||
<span v-for="name in tagNames(profile.tagIds)" :key="name" class="my-profile__tag">{{ name }}</span>
|
||||
<span v-for="name in tagValues(profile.tags)" :key="name" class="my-profile__tag">{{ name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useAuthStore } from '@/stores/auth.store';
|
||||
import { useUiStore } from '@/stores/ui.store';
|
||||
import { useChatStore } from '@/stores/chat.store';
|
||||
import { useAuth } from '@/composables/useAuth';
|
||||
import { useUi } from '@/composables/useUi';
|
||||
import { useChat } from '@/composables/useChat';
|
||||
import { apiClient } from '@/api/client';
|
||||
import type { UserProfile } from '@/stores/auth.store';
|
||||
import type { UserProfile } from '@/composables/useAuth';
|
||||
import AppButton from '@/components/common/AppButton.vue';
|
||||
import AppModal from '@/components/common/AppModal.vue';
|
||||
import ReportModal from '@/components/reports/ReportModal.vue';
|
||||
@@ -13,9 +13,9 @@ import DateProposalForm from '@/components/dates/DateProposalForm.vue';
|
||||
import LoadingSpinner from '@/components/common/LoadingSpinner.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const authStore = useAuthStore();
|
||||
const uiStore = useUiStore();
|
||||
const chatStore = useChatStore();
|
||||
const authStore = useAuth();
|
||||
const uiStore = useUi();
|
||||
const chatStore = useChat();
|
||||
|
||||
const profileId = route.params.profileId as string;
|
||||
const profile = ref<UserProfile | null>(null);
|
||||
@@ -44,13 +44,18 @@ const age = computed(() => {
|
||||
return a;
|
||||
});
|
||||
|
||||
const cityName = computed(() => uiStore.cities.find((c) => c.id === profile.value?.cityId)?.name ?? '');
|
||||
const tagNames = computed(() => (profile.value?.tagIds ?? []).map((id) => uiStore.tags.find((t) => t.id === id)?.name ?? id));
|
||||
const cityName = computed(() => {
|
||||
const cid = profile.value?.cityId;
|
||||
return cid ? uiStore.cities.find((c) => c.id === cid)?.name ?? '' : '';
|
||||
});
|
||||
const tagNames = computed(() => profile.value?.tags?.map((t) => t.value) ?? []);
|
||||
const currentImageIndex = ref(0);
|
||||
|
||||
const isOwnProfile = computed(() =>
|
||||
authStore.profiles.some((p) => p.id === profileId),
|
||||
);
|
||||
|
||||
function goBack() { window.history.back(); }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -63,8 +68,8 @@ const isOwnProfile = computed(() =>
|
||||
<!-- Image strip -->
|
||||
<div class="profile-detail__cover">
|
||||
<img
|
||||
v-if="profile.mediaUrls?.[currentImageIndex]"
|
||||
:src="profile.mediaUrls[currentImageIndex]"
|
||||
v-if="profile.media?.[currentImageIndex]?.path"
|
||||
:src="profile.media[currentImageIndex].path"
|
||||
:alt="profile.name"
|
||||
class="profile-detail__cover-img"
|
||||
/>
|
||||
@@ -78,14 +83,14 @@ const isOwnProfile = computed(() =>
|
||||
aria-label="Предыдущее фото"
|
||||
>‹</button>
|
||||
<button
|
||||
v-if="currentImageIndex < (profile.mediaUrls?.length ?? 1) - 1"
|
||||
v-if="currentImageIndex < (profile.media?.length ?? 1) - 1"
|
||||
class="profile-detail__img-nav profile-detail__img-nav--next"
|
||||
@click="currentImageIndex++"
|
||||
aria-label="Следующее фото"
|
||||
>›</button>
|
||||
|
||||
<!-- Back button -->
|
||||
<button class="profile-detail__back" @click="history.back()" aria-label="Назад">
|
||||
<button class="profile-detail__back" @click="goBack()" aria-label="Назад">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20">
|
||||
<path d="M19 12H5M12 5l-7 7 7 7"/>
|
||||
</svg>
|
||||
|
||||
Reference in New Issue
Block a user