Enhance content management and user interaction features

- Introduced a new content source toggle in the profile and app header to switch between IndeeHub and TopDoc films.
- Updated the content fetching logic to dynamically load content based on the selected source.
- Enhanced the seeding process to include a combined catalog of IndeeHub and TopDoc films, ensuring diverse content availability.
- Improved user interaction by preventing duplicate reactions and ensuring a smoother voting experience across comments and content.
- Added support for Amber login (NIP-55) for Android users, integrating it into the existing authentication flow.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Dorian
2026-02-12 14:24:52 +00:00
parent ab0560de00
commit 35bc78b890
38 changed files with 1107 additions and 185 deletions

View File

@@ -58,10 +58,10 @@
<!-- Like Button -->
<button
@click="handleLike"
:disabled="hasVoted || !isNostrLoggedIn"
:disabled="isNostrLoggedIn && (hasVoted || isReacting)"
class="action-btn"
:class="{ 'action-btn-active': userReaction === '+' }"
:style="{ opacity: userReaction === '+' ? 1 : hasVoted ? 0.4 : 1 }"
:style="{ opacity: userReaction === '+' ? 1 : (isNostrLoggedIn && (hasVoted || isReacting)) ? 0.4 : 1 }"
>
<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="M14 10h4.764a2 2 0 011.789 2.894l-3.5 7A2 2 0 0115.263 21h-4.017c-.163 0-.326-.02-.485-.06L7 20m7-10V5a2 2 0 00-2-2h-.095c-.5 0-.905.405-.905.905 0 .714-.211 1.412-.608 2.006L7 11v9m7-10h-2M7 20H5a2 2 0 01-2-2v-6a2 2 0 012-2h2.5" />
@@ -72,10 +72,10 @@
<!-- Dislike Button -->
<button
@click="handleDislike"
:disabled="hasVoted || !isNostrLoggedIn"
:disabled="isNostrLoggedIn && (hasVoted || isReacting)"
class="action-btn"
:class="{ 'action-btn-active': userReaction === '-' }"
:style="{ opacity: userReaction === '-' ? 1 : hasVoted ? 0.4 : 1 }"
:style="{ opacity: userReaction === '-' ? 1 : (isNostrLoggedIn && (hasVoted || isReacting)) ? 0.4 : 1 }"
>
<svg class="w-5 h-5 rotate-180" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 10h4.764a2 2 0 011.789 2.894l-3.5 7A2 2 0 0115.263 21h-4.017c-.163 0-.326-.02-.485-.06L7 20m7-10V5a2 2 0 00-2-2h-.095c-.5 0-.905.405-.905.905 0 .714-.211 1.412-.608 2.006L7 11v9m7-10h-2M7 20H5a2 2 0 01-2-2v-6a2 2 0 012-2h2.5" />
@@ -241,13 +241,14 @@ interface Emits {
}
const props = defineProps<Props>()
defineEmits<Emits>()
const emit = defineEmits<Emits>()
const { isAuthenticated, hasActiveSubscription } = useAuth()
const { isLoggedIn: isNostrLoggedIn, activePubkey } = useAccounts()
const newComment = ref('')
const isPostingComment = ref(false)
const isReacting = ref(false)
const isInMyList = ref(false)
const showVideoPlayer = ref(false)
const showSubscriptionModal = ref(false)
@@ -296,7 +297,16 @@ function getProfile(pubkey: string) {
}
function handlePlay() {
if (!isAuthenticated.value) return
// Free content with a streaming URL can play without auth
if (props.content?.streamingUrl) {
showVideoPlayer.value = true
return
}
if (!isAuthenticated.value && !isNostrLoggedIn.value) {
emit('openAuth')
return
}
if (hasActiveSubscription.value) {
showVideoPlayer.value = true
@@ -307,27 +317,45 @@ function handlePlay() {
}
function toggleMyList() {
if (!isAuthenticated.value && !isNostrLoggedIn.value) {
emit('openAuth')
return
}
isInMyList.value = !isInMyList.value
}
async function handleLike() {
if (!isNostrLoggedIn.value || hasVoted.value) return
if (!isNostrLoggedIn.value) {
emit('openAuth')
return
}
if (hasVoted.value || isReacting.value) return
if (props.content?.id) {
isReacting.value = true
try {
await nostr.postReaction(true, props.content.id)
} catch (err) {
console.error('Failed to post reaction:', err)
} finally {
isReacting.value = false
}
}
}
async function handleDislike() {
if (!isNostrLoggedIn.value || hasVoted.value) return
if (!isNostrLoggedIn.value) {
emit('openAuth')
return
}
if (hasVoted.value || isReacting.value) return
if (props.content?.id) {
isReacting.value = true
try {
await nostr.postReaction(false, props.content.id)
} catch (err) {
console.error('Failed to post reaction:', err)
} finally {
isReacting.value = false
}
}
}