239 lines
5.9 KiB
Vue
239 lines
5.9 KiB
Vue
<script setup lang="ts">
|
|
import MainLayout from "@/Layouts/MainLayout.vue";
|
|
import { User, UserAction } from "@/Types/types";
|
|
import {Head, Link} from "@inertiajs/vue3";
|
|
import FeedItem from "@/Components/FlightsGoneBy/Feed/FeedItem.vue";
|
|
import ProfileLayout from "@/Components/FlightsGoneBy/ProfileLayout.vue";
|
|
import FlightMap from "@/Components/FlightsGoneBy/FlightMap.vue";
|
|
import PlaneLoader from "@/Components/FlightsGoneBy/PlaneLoader.vue";
|
|
import { useFlights } from "@/Composables/useFlights";
|
|
import { useApiResource } from "@/Composables/useApiResource";
|
|
import Avatar from "@/Components/FlightsGoneBy/Feed/Avatar.vue";
|
|
|
|
defineOptions({ layout: MainLayout })
|
|
|
|
const props = defineProps<{
|
|
user: User
|
|
feed: UserAction[]
|
|
}>()
|
|
|
|
// Flights belonging to people the user follows — adjust endpoint to match your API
|
|
const { flights: followingFlights, flightsLoading } = useFlights('/internal/feed/following-flights')
|
|
|
|
// People the user follows — adjust shape/endpoint to match your API
|
|
const { data: following, loading: followingLoading } = useApiResource<User[]>('/internal/feed/following')
|
|
</script>
|
|
|
|
<template>
|
|
<ProfileLayout :showCount="false" :user="user" followStatus="none" :canView="true" :loading="false">
|
|
<Head title="Feed" />
|
|
|
|
<div class="feed-page">
|
|
<header class="feed-header">
|
|
<h1>Feed</h1>
|
|
<span class="feed-count">{{ feed.length }} updates</span>
|
|
</header>
|
|
|
|
<!-- MAP: the headline content -->
|
|
<section class="map-section">
|
|
<div class="map-caption">
|
|
<span class="caption-icon">🛫</span>
|
|
Recently logged by people you follow
|
|
</div>
|
|
<FlightMap v-if="!flightsLoading" :flights="followingFlights" :show-legend="false" />
|
|
<div v-else class="loading-state">
|
|
<PlaneLoader />
|
|
</div>
|
|
</section>
|
|
|
|
<div class="feed-grid">
|
|
<!-- FOLLOWING: secondary, supporting content -->
|
|
<aside class="following-box">
|
|
<h2>Following</h2>
|
|
<div v-if="followingLoading" class="following-loading">
|
|
<PlaneLoader />
|
|
</div>
|
|
<div v-else-if="!following?.length" class="empty-small">
|
|
<p>You're not following anyone yet.</p>
|
|
</div>
|
|
<ul v-else class="following-list">
|
|
<li v-for="person in following" :key="person.id" class="following-item">
|
|
<Avatar :user="person" />
|
|
<Link :href="route('profile.view', { user: person?.name })">
|
|
<span class="following-name">{{ person.name }}</span>
|
|
</Link>
|
|
</li>
|
|
</ul>
|
|
</aside>
|
|
|
|
<!-- FEED: condensed, de-emphasised -->
|
|
<div class="feed-condensed">
|
|
<div v-if="feed.length === 0" class="empty">
|
|
<p>Nothing here yet — follow someone to see their flight updates!</p>
|
|
</div>
|
|
<div v-else class="feed-list">
|
|
<FeedItem v-for="action in feed" :key="action.id" :action="action" compact />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</ProfileLayout>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.feed-page {
|
|
margin: 0 auto;
|
|
padding: 2rem 1rem;
|
|
width: 70%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2rem;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.feed-page {
|
|
width: 100%;
|
|
padding: 0;
|
|
}
|
|
}
|
|
|
|
.feed-header {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.feed-header h1 {
|
|
font-size: 1.5rem;
|
|
font-weight: 700;
|
|
margin: 0;
|
|
}
|
|
|
|
.feed-count {
|
|
font-size: 0.85rem;
|
|
color: #6b7280;
|
|
}
|
|
|
|
/* MAP */
|
|
.map-section {
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.map-caption {
|
|
position: absolute;
|
|
top: 1rem;
|
|
left: 1rem;
|
|
z-index: 10;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
padding: 0.5rem 1rem;
|
|
background: rgba(10, 15, 30, 0.65);
|
|
backdrop-filter: blur(8px);
|
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
border-radius: 999px;
|
|
font-size: 0.85rem;
|
|
font-weight: 500;
|
|
color: rgba(255, 255, 255, 0.9);
|
|
}
|
|
|
|
.caption-icon {
|
|
font-size: 1rem;
|
|
line-height: 1;
|
|
}
|
|
|
|
.loading-state {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
min-height: 40dvh;
|
|
}
|
|
|
|
/* GRID: following sidebar + condensed feed */
|
|
.feed-grid {
|
|
display: grid;
|
|
grid-template-columns: 240px minmax(0, 1fr);
|
|
gap: 2rem;
|
|
align-items: start;
|
|
min-width: 0;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.feed-grid {
|
|
grid-template-columns: minmax(0, 1fr);
|
|
}
|
|
}
|
|
|
|
.following-box,
|
|
.feed-condensed {
|
|
min-width: 0;
|
|
}
|
|
|
|
.following-box {
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
padding: 1rem;
|
|
}
|
|
|
|
.following-box h2 {
|
|
font-size: 1rem;
|
|
font-weight: 600;
|
|
margin: 0 0 0.75rem;
|
|
}
|
|
|
|
.following-list {
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.6rem;
|
|
}
|
|
|
|
.following-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.6rem;
|
|
}
|
|
|
|
.following-avatar {
|
|
width: 28px;
|
|
height: 28px;
|
|
border-radius: 50%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.following-name {
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.following-loading {
|
|
display: flex;
|
|
justify-content: center;
|
|
padding: 1rem 0;
|
|
}
|
|
|
|
.empty-small p {
|
|
font-size: 0.8rem;
|
|
color: #6b7280;
|
|
margin: 0;
|
|
}
|
|
|
|
/* CONDENSED FEED — de-emphasised vs before */
|
|
.feed-condensed {
|
|
opacity: 0.92;
|
|
}
|
|
|
|
.empty p {
|
|
text-align: center;
|
|
color: #6b7280;
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.feed-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem; /* tightened from 2rem */
|
|
}
|
|
</style>
|