Fix false "Rented" state for project owners + add "Your project" badge

The rental check catch block was incorrectly setting hasActiveRental=true
for project owners when the API call failed (e.g. auth token not synced).
This showed a "Rented" badge with no time remaining and hid the rent button.

- Separate "owner can play" from "has active rental" via new isOwner computed
- canPlay now includes isOwner so owners always have playback access
- Catch block no longer fakes a rental — keeps the badge accurate
- New purple "Your project" badge for owners (distinct from green "Rented")

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Dorian
2026-02-13 20:48:58 +00:00
parent 3e4279e252
commit f6c4b9f06c
2 changed files with 13 additions and 6 deletions

View File

@@ -20,7 +20,7 @@ services:
context: . context: .
dockerfile: Dockerfile dockerfile: Dockerfile
args: args:
CACHEBUST: "10" CACHEBUST: "11"
VITE_USE_MOCK_DATA: "false" VITE_USE_MOCK_DATA: "false"
VITE_CONTENT_ORIGIN: ${FRONTEND_URL} VITE_CONTENT_ORIGIN: ${FRONTEND_URL}
VITE_INDEEHUB_API_URL: /api VITE_INDEEHUB_API_URL: /api

View File

@@ -43,6 +43,9 @@
</svg> </svg>
Rented &middot; {{ rentalTimeRemaining }} Rented &middot; {{ rentalTimeRemaining }}
</span> </span>
<span v-else-if="isOwner" class="bg-purple-500/20 text-purple-400 border border-purple-500/30 px-2.5 py-0.5 rounded font-medium">
Your project
</span>
<span v-else-if="content.rentalPrice" class="bg-amber-500/20 text-amber-400 border border-amber-500/30 px-2.5 py-0.5 rounded font-medium"> <span v-else-if="content.rentalPrice" class="bg-amber-500/20 text-amber-400 border border-amber-500/30 px-2.5 py-0.5 rounded font-medium">
{{ content.rentalPrice.toLocaleString() }} sats {{ content.rentalPrice.toLocaleString() }} sats
</span> </span>
@@ -274,6 +277,9 @@ const relayConnected = ref(true)
const hasActiveRental = ref(false) const hasActiveRental = ref(false)
const rentalExpiresAt = ref<Date | null>(null) const rentalExpiresAt = ref<Date | null>(null)
/** True if the logged-in user is the project owner */
const isOwner = computed(() => !!props.content?.isOwnProject)
/** Human-readable time remaining on the rental (e.g. "23h 15m") */ /** Human-readable time remaining on the rental (e.g. "23h 15m") */
const rentalTimeRemaining = computed(() => { const rentalTimeRemaining = computed(() => {
if (!rentalExpiresAt.value) return '' if (!rentalExpiresAt.value) return ''
@@ -285,9 +291,9 @@ const rentalTimeRemaining = computed(() => {
return `${minutes}m remaining` return `${minutes}m remaining`
}) })
/** True when the user can play without paying (subscription or active rental) */ /** True when the user can play without paying (subscription, active rental, or project owner) */
const canPlay = computed(() => const canPlay = computed(() =>
hasActiveSubscription.value || hasActiveRental.value, hasActiveSubscription.value || hasActiveRental.value || isOwner.value,
) )
async function checkRentalAccess() { async function checkRentalAccess() {
@@ -304,9 +310,10 @@ async function checkRentalAccess() {
rentalExpiresAt.value = result.expiresAt ? new Date(result.expiresAt) : null rentalExpiresAt.value = result.expiresAt ? new Date(result.expiresAt) : null
} catch (err) { } catch (err) {
console.warn('Rental check failed:', err) console.warn('Rental check failed:', err)
// If the rental check fails (e.g. auth issue) but the user owns the // Owner playback is handled by the isOwner computed property,
// content, treat it as "can play" so the owner isn't blocked. // so we don't fake a rental here. This keeps the "Rented" badge
hasActiveRental.value = !!props.content.isOwnProject // accurate and still shows the rental price for non-owners.
hasActiveRental.value = false
rentalExpiresAt.value = null rentalExpiresAt.value = null
} }
} }