[READ-ONLY] Mirror of https://github.com/flo-bit/skywatched. review movies and tv shows, based on at proto skywatched.app
0

Configure Feed

Select the types of activity you want to include in your feed.

skywatched / src / lib / Components / RateMovieModal.svelte
5.2 kB 145 lines
1<script lang="ts"> 2 import { crosspostModal, rateMovieModal, settings, watchedItems } from '$lib/state.svelte'; 3 import { toast } from 'svelte-sonner'; 4 import Rating from './Rating.svelte'; 5 import SearchCombobox from './SearchCombobox.svelte'; 6 7 let rating = $state(rateMovieModal.selectedItem.currentRating ?? 0); 8 let review = $state(rateMovieModal.selectedItem.currentReview ?? ''); 9 10 $effect(() => { 11 rating = rateMovieModal.selectedItem.currentRating ?? 0; 12 review = rateMovieModal.selectedItem.currentReview ?? ''; 13 }); 14 15 let sending = $state(false); 16</script> 17 18{#if rateMovieModal.showModal} 19 <div class="relative z-50" aria-labelledby="modal-title" role="dialog" aria-modal="true"> 20 <div 21 class="fixed inset-0 bg-base-950/90 backdrop-blur-sm transition-opacity" 22 onclick={() => (rateMovieModal.showModal = false)} 23 aria-hidden="true" 24 ></div> 25 <div class="pointer-events-none fixed inset-0 z-50 h-[100dvh] w-screen overflow-y-auto"> 26 <div class="flex h-[100dvh] items-end justify-center p-4 text-center sm:items-center sm:p-0"> 27 <div 28 class="pointer-events-auto relative w-full transform overflow-hidden rounded-lg border border-base-800 bg-base-900 px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:max-w-sm sm:p-6" 29 > 30 <button 31 class="absolute right-2 top-2 rounded-full bg-base-800/50 p-1 hover:bg-base-800/80" 32 onclick={() => (rateMovieModal.showModal = false)} 33 > 34 <svg 35 xmlns="http://www.w3.org/2000/svg" 36 fill="none" 37 viewBox="0 0 24 24" 38 stroke-width="1.5" 39 stroke="currentColor" 40 class="size-4" 41 > 42 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" /> 43 </svg> 44 45 <span class="sr-only">close</span> 46 </button> 47 48 <div> 49 <h3 class="text-md mb-4 font-semibold text-base-50" id="modal-title"> 50 Rate and review 51 </h3> 52 53 {#if rateMovieModal.selectedItem.name} 54 <div class="relative flex items-center gap-4"> 55 <div 56 class="relative z-20 aspect-[2/3] h-32 w-auto shrink-0 overflow-hidden rounded-md border border-base-800 bg-base-900/50" 57 > 58 {#if rateMovieModal.selectedItem.posterPath} 59 <img 60 src="https://image.tmdb.org/t/p/w154{rateMovieModal.selectedItem.posterPath}" 61 alt="movie poster for {rateMovieModal.selectedItem.name}" 62 class="size-full object-cover object-center lg:size-full" 63 /> 64 {/if} 65 </div> 66 <h3 67 class="mb-4 flex flex-col gap-2 text-xl font-semibold text-base-50" 68 id="modal-title" 69 > 70 {rateMovieModal.selectedItem.name} 71 72 <Rating bind:rating canChange size="size-7" /> 73 </h3> 74 <!-- <div class="absolute right-2 top-0"> 75 <button 76 onclick={() => (rateMovieModal.showModal = false)} 77 class="rounded-full bg-base-800/50 p-1 px-2 text-xs text-base-50 hover:bg-base-800/70 hover:text-base-100" 78 > 79 change 80 </button> 81 </div> --> 82 </div> 83 {:else} 84 <SearchCombobox /> 85 {/if} 86 87 <div class="mt-4"> 88 <label for="comment" class="block text-xs font-medium text-base-50">review</label> 89 <div class="mt-2"> 90 <textarea 91 rows="4" 92 name="comment" 93 id="comment" 94 bind:value={review} 95 placeholder="write a review" 96 class="outline-nonse block w-full rounded-lg border border-base-800 bg-base-950 px-3 py-1.5 text-base text-base-50 -outline-offset-1 placeholder:text-base-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-accent-400 sm:text-sm/6" 97 ></textarea> 98 </div> 99 </div> 100 </div> 101 <div class="mt-5 sm:mt-6"> 102 <button 103 onclick={async () => { 104 const response = await fetch(`/api/rate`, { 105 method: 'POST', 106 body: JSON.stringify({ 107 rating, 108 review, 109 kind: rateMovieModal.selectedItem.kind, 110 id: rateMovieModal.selectedItem.movieId ?? rateMovieModal.selectedItem.showId 111 }) 112 }); 113 114 if (!response.ok) { 115 toast.error('Failed to save rating'); 116 117 return; 118 } 119 rateMovieModal.showModal = false; 120 121 const data = await response.json(); 122 123 if (settings.crosspostEnabled && review.length > 0) { 124 crosspostModal.show(data.uri, review, rating, rateMovieModal.selectedItem.name ?? ''); 125 } 126 127 watchedItems.addRated({ 128 movieId: rateMovieModal.selectedItem.movieId, 129 showId: rateMovieModal.selectedItem.showId, 130 rating 131 }); 132 133 toast.success('Rating saved'); 134 }} 135 type="button" 136 disabled={sending || !rateMovieModal.selectedItem.name} 137 class="inline-flex w-full justify-center rounded-md border border-accent-900 bg-accent-950/80 px-3 py-2 text-sm font-semibold text-accent-300 shadow-sm hover:bg-accent-950 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-600 disabled:opacity-50 disabled:cursor-not-allowed" 138 >{sending ? 'Sending...' : 'Review'}</button 139 > 140 </div> 141 </div> 142 </div> 143 </div> 144 </div> 145{/if}