Enhance deployment script and update package dependencies

- Added detailed labels to the deployment script for IndeedHub, including title, version, description, license, icon, and repository URL.
- Updated package dependencies in package.json and package-lock.json, including upgrading 'nostr-tools' to version 2.23.0 and adding 'axios' and '@tanstack/vue-query'.
- Improved README with a modern description of the platform and updated project structure details.

This commit enhances the clarity of the deployment process and ensures the project is using the latest dependencies for better performance and features.
This commit is contained in:
Dorian
2026-02-12 10:30:47 +00:00
parent dacfa7a822
commit c970f5b29f
43 changed files with 6906 additions and 603 deletions

View File

@@ -4,84 +4,7 @@
<div class="browse-view">
<!-- Header / Navigation -->
<header class="fixed top-0 left-0 right-0 z-50 pt-4 px-4">
<div class="floating-glass-header mx-auto px-4 md:px-6 py-3.5 rounded-2xl transition-all duration-300" style="max-width: 100%;">
<div class="flex items-center justify-between">
<!-- Logo + Navigation (Left Side) -->
<div class="flex items-center gap-10">
<img src="/assets/images/logo.svg" alt="IndeedHub" class="h-10 ml-2 md:ml-0" />
<!-- Navigation - Next to Logo on Desktop -->
<nav class="hidden md:flex items-center gap-3">
<a href="#" class="nav-button-active">Films</a>
<a href="#" class="nav-button">Series</a>
<a href="#" class="nav-button">Creators</a>
<a href="#" class="nav-button">My List</a>
</nav>
</div>
<!-- Right Side Actions -->
<div class="flex items-center gap-4">
<!-- Search -->
<button class="hidden md:block p-2 hover:bg-white/10 rounded-lg transition-colors" @click="toggleSearch">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</button>
<!-- Profile Dropdown -->
<div class="hidden md:block relative profile-dropdown">
<button
@click="toggleProfileMenu"
class="profile-button flex items-center gap-2"
>
<div class="profile-avatar">
<span>D</span>
</div>
<span class="text-white text-sm font-medium">Dorian</span>
<svg class="w-4 h-4 transition-transform" :class="{ 'rotate-180': profileMenuOpen }" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
<!-- Dropdown Menu -->
<div v-if="profileMenuOpen" class="profile-menu absolute right-0 mt-2 w-48">
<div class="floating-glass-header py-2 rounded-xl">
<a href="#" class="profile-menu-item flex items-center gap-3 px-4 py-2.5">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
<span>Profile</span>
</a>
<a href="#" class="profile-menu-item flex items-center gap-3 px-4 py-2.5">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<span>Settings</span>
</a>
<div class="border-t border-white/10 my-1"></div>
<a href="#" class="profile-menu-item flex items-center gap-3 px-4 py-2.5 text-red-400">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
</svg>
<span>Sign Out</span>
</a>
</div>
</div>
</div>
<!-- Mobile User Avatar + Name (No Dropdown) -->
<div class="md:hidden flex items-center gap-2 mr-2">
<div class="profile-avatar">
<span>D</span>
</div>
<span class="text-white text-sm font-medium">Dorian</span>
</div>
</div>
</div>
</div>
</header>
<AppHeader @openAuth="showAuthModal = true" />
<!-- Hero / Featured Content -->
<section class="relative h-[56vh] md:h-[61vh] overflow-hidden">
@@ -119,13 +42,13 @@
<!-- Action Buttons -->
<div class="flex items-center gap-2.5 md:gap-3 pt-1.5 md:pt-2">
<button class="hero-play-button flex items-center gap-2">
<button @click="handlePlayClick" class="hero-play-button flex items-center gap-2">
<svg class="w-4 h-4 md:w-5 md:h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M8 5v14l11-7z"/>
</svg>
Play
</button>
<button class="hero-info-button flex items-center gap-2">
<button @click="handleInfoClick" class="hero-info-button flex items-center gap-2">
<svg class="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
@@ -183,18 +106,51 @@
/>
</div>
</section>
<!-- Modals -->
<AuthModal
:isOpen="showAuthModal"
@close="showAuthModal = false"
@success="handleAuthSuccess"
/>
<ContentDetailModal
:isOpen="showDetailModal"
:content="selectedContent"
@close="showDetailModal = false"
@openAuth="showAuthModal = true"
/>
<!-- Hero-only modals (for direct Play button) -->
<SubscriptionModal
:isOpen="showSubscriptionModal"
@close="showSubscriptionModal = false"
@success="handleSubscriptionSuccess"
/>
<VideoPlayer
:isOpen="showVideoPlayer"
:content="selectedContent"
@close="showVideoPlayer = false"
/>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, computed } from 'vue'
import { ref, onMounted, computed } from 'vue'
import ContentRow from '../components/ContentRow.vue'
import SplashIntro from '../components/SplashIntro.vue'
import AppHeader from '../components/AppHeader.vue'
import AuthModal from '../components/AuthModal.vue'
import ContentDetailModal from '../components/ContentDetailModal.vue'
import SubscriptionModal from '../components/SubscriptionModal.vue'
import VideoPlayer from '../components/VideoPlayer.vue'
import { useContentStore } from '../stores/content'
import { useAuth } from '../composables/useAuth'
import type { Content } from '../types/content'
const contentStore = useContentStore()
const scrolled = ref(false)
const { isAuthenticated, hasActiveSubscription } = useAuth()
const featuredContent = computed(() => contentStore.featuredContent)
const featuredFilms = computed(() => contentStore.contentRows.featured)
@@ -204,46 +160,52 @@ const independentCinema = computed(() => contentStore.contentRows.independent)
const dramas = computed(() => contentStore.contentRows.dramas)
const documentaries = computed(() => contentStore.contentRows.documentaries)
const profileMenuOpen = ref(false)
const handleScroll = () => {
// Calculate 30% of the page height
const scrollThreshold = document.documentElement.scrollHeight * 0.3
scrolled.value = window.scrollY > scrollThreshold
}
const toggleSearch = () => {
// TODO: Implement search modal
console.log('Search clicked')
}
const toggleProfileMenu = () => {
profileMenuOpen.value = !profileMenuOpen.value
}
// Close profile menu when clicking outside
const handleClickOutside = (event: MouseEvent) => {
const dropdown = document.querySelector('.profile-dropdown')
if (dropdown && !dropdown.contains(event.target as Node)) {
profileMenuOpen.value = false
}
}
const showAuthModal = ref(false)
const showDetailModal = ref(false)
const showSubscriptionModal = ref(false)
const showVideoPlayer = ref(false)
const selectedContent = ref<Content | null>(null)
// Content card click -> always open detail modal
const handleContentClick = (content: Content) => {
console.log('Content clicked:', content)
// TODO: Navigate to content detail page
selectedContent.value = content
showDetailModal.value = true
}
// Hero Play button -> direct play flow (skips detail modal)
const handlePlayClick = () => {
if (!isAuthenticated.value) {
showAuthModal.value = true
return
}
if (hasActiveSubscription.value) {
selectedContent.value = featuredContent.value
showVideoPlayer.value = true
return
}
// No subscription - show subscription modal
showSubscriptionModal.value = true
}
// Hero More Info button -> open detail modal for featured content
const handleInfoClick = () => {
selectedContent.value = featuredContent.value
showDetailModal.value = true
}
function handleAuthSuccess() {
showAuthModal.value = false
}
function handleSubscriptionSuccess() {
showSubscriptionModal.value = false
}
onMounted(() => {
window.addEventListener('scroll', handleScroll)
window.addEventListener('click', handleClickOutside)
contentStore.fetchContent()
})
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll)
window.removeEventListener('click', handleClickOutside)
})
</script>
<style scoped>
@@ -252,143 +214,13 @@ onUnmounted(() => {
overflow-x: hidden;
}
.floating-glass-header {
background: rgba(0, 0, 0, 0.65);
backdrop-filter: blur(40px);
-webkit-backdrop-filter: blur(40px);
border-radius: 24px;
border: 1px solid rgba(255, 255, 255, 0.06);
box-shadow:
0 20px 60px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
/* Navigation Button Styles - EXACT from Archy Onboarding */
.nav-button {
position: relative;
padding: 12px 24px;
font-size: 16px;
font-weight: 500;
line-height: 1.4;
border-radius: 16px;
background: rgba(0, 0, 0, 0.25);
color: rgba(255, 255, 255, 0.96);
box-shadow:
0 8px 24px rgba(0, 0, 0, 0.45),
inset 0 1px 0 rgba(255, 255, 255, 0.22);
backdrop-filter: blur(24px);
-webkit-backdrop-filter: blur(24px);
border: none;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-block;
white-space: nowrap;
letter-spacing: 0.02em;
}
.nav-button::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 2px;
background: linear-gradient(135deg, rgba(0, 0, 0, 0.8), transparent);
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
.nav-button:hover {
transform: translateY(-2px);
background: rgba(0, 0, 0, 0.35);
box-shadow:
0 12px 32px rgba(0, 0, 0, 0.6),
inset 0 1px 0 rgba(255, 255, 255, 0.25);
}
.nav-button:hover::before {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.3), transparent);
}
.nav-button-active {
position: relative;
padding: 12px 24px;
font-size: 16px;
font-weight: 600;
line-height: 1.4;
border-radius: 16px;
background: rgba(0, 0, 0, 0.35);
color: rgba(255, 255, 255, 1);
box-shadow:
0 12px 32px rgba(0, 0, 0, 0.6),
inset 0 1px 0 rgba(255, 255, 255, 0.25);
backdrop-filter: blur(24px);
-webkit-backdrop-filter: blur(24px);
border: none;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-block;
white-space: nowrap;
letter-spacing: 0.02em;
}
.nav-button-active::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 2px;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.3), transparent);
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
.nav-button-active:hover {
transform: translateY(-2px);
background: rgba(0, 0, 0, 0.40);
box-shadow:
0 12px 32px rgba(0, 0, 0, 0.6),
inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
/* Mobile Tab Bar Styles */
.nav-tab {
color: rgba(255, 255, 255, 0.7);
text-decoration: none;
transition: all 0.3s ease;
padding: 8px 12px;
border-radius: 12px;
}
.nav-tab:active {
background: rgba(255, 255, 255, 0.1);
}
.nav-tab-active {
color: rgba(255, 255, 255, 1);
text-decoration: none;
transition: all 0.3s ease;
padding: 8px 12px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.1);
}
/* Hero Title Styles */
.hero-title {
background: linear-gradient(to right, #fafafa, #9ca3af);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
letter-spacing: 0.05em; /* 5% character spacing */
letter-spacing: 0.05em;
}
/* Hero Button Styles */
@@ -487,59 +319,4 @@ onUnmounted(() => {
font-size: 16px;
}
}
/* Profile Dropdown Styles */
.profile-button {
padding: 6px 12px 6px 6px;
border-radius: 12px;
background: transparent;
border: none;
cursor: pointer;
transition: all 0.3s ease;
}
.profile-button:hover {
background: rgba(255, 255, 255, 0.05);
}
.profile-menu {
z-index: 100;
animation: slideDown 0.2s ease-out;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.profile-menu-item {
color: rgba(255, 255, 255, 0.9);
text-decoration: none;
font-size: 14px;
font-weight: 500;
transition: all 0.2s ease;
display: flex;
align-items: center;
}
.profile-menu-item:hover {
background: rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 1);
}
.profile-menu-item svg {
opacity: 0.8;
}
.profile-menu-item:hover svg {
opacity: 1;
}
</style>