beatufitull front end for ozone modration ,, wit catpucoin and ebergarden !
ozone moderation
5
fork

Configure Feed

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

rm events for now

-170
-170
src/routes/events/+page.svelte
··· 1 - <script lang="ts"> 2 - import { session, type AuthStore } from '$lib/stores/auth'; 3 - import { 4 - LoaderCircle, 5 - ExternalLink, 6 - TriangleAlert, 7 - CheckIcon, 8 - CircleCheck, 9 - Scale 10 - } from 'lucide-svelte'; 11 - import { createInfiniteQuery, createQuery, QueryClient } from '@tanstack/svelte-query'; 12 - import { AtUri, ComAtprotoRepoStrongRef } from '@atproto/api'; 13 - import Header from '../components/Header.svelte'; 14 - import Modal from '../components/Modal.svelte'; 15 - import UserModal from '../components/view/User.svelte'; 16 - import Button from '../components/ui/Button.svelte'; 17 - import type { Report } from '$lib/types'; 18 - import { isRepoRef } from '@atproto/api/dist/client/types/com/atproto/admin/defs'; 19 - import { formatDate } from '$lib/time'; 20 - import Post from '$components/view/Post.svelte'; 21 - import { page } from '$app/state'; 22 - import Sidebar from '$components/Sidebar.svelte'; 23 - 24 - let selectedDid: string | null = null; 25 - let selectedPostUri: AtUri | null = null; 26 - function getCollectionName(report: Report) { 27 - try { 28 - return report.subject.handle ? 'Post' : 'unknown'; 29 - } catch (e) { 30 - console.error('Invalid report:', report); 31 - return 'unknown'; 32 - } 33 - } 34 - // switch to infinite query so we can page with the cursor returned by the API 35 - const reportsQuery = createInfiniteQuery(() => ({ 36 - queryKey: ['reports'], 37 - queryFn: async ({ pageParam }) => { 38 - if (!$session) { 39 - throw new Error('No active session'); 40 - } 41 - const statuses = await $session.agent.tools.ozone.moderation.queryStatuses({ 42 - limit: 10, 43 - sortField: 'lastReportedAt', 44 - sortDirection: 'desc', 45 - // pageParam will be the cursor string if present 46 - cursor: pageParam == '' ? undefined : pageParam 47 - }); 48 - 49 - return statuses.data; 50 - }, 51 - initialPageParam: '', 52 - getNextPageParam: (lastPage) => lastPage.cursor ?? undefined, 53 - retry(failureCount, error) { 54 - console.log('Fetch reports error:', error); 55 - return failureCount < 3; 56 - }, 57 - enabled: !!$session 58 - })); 59 - 60 - function infiniteScroll(node: HTMLElement) { 61 - const observer = new IntersectionObserver( 62 - (entries) => { 63 - if ( 64 - entries[0].isIntersecting && 65 - reportsQuery.hasNextPage && 66 - !reportsQuery.isFetchingNextPage 67 - ) { 68 - reportsQuery.fetchNextPage(); 69 - } 70 - }, 71 - { 72 - rootMargin: '200px' 73 - } 74 - ); 75 - 76 - observer.observe(node); 77 - 78 - return { 79 - destroy() { 80 - observer.disconnect(); 81 - } 82 - }; 83 - } 84 - </script> 85 - 86 - <div class="min-h-screen w-full bg-ctp-base p-4"> 87 - {#if selectedDid} 88 - {#key selectedDid} 89 - <UserModal isOpen={true} did={selectedDid} onClose={() => (selectedDid = null)} /> 90 - {/key} 91 - {:else if selectedPostUri} 92 - {#key selectedPostUri} 93 - <Post isOpen={true} uri={selectedPostUri} onClose={() => (selectedPostUri = null)} /> 94 - {/key} 95 - {/if} 96 - <div class="mx-auto max-w-2xl"> 97 - {#if reportsQuery.isLoading} 98 - <div class="flex min-h-screen items-center justify-center"> 99 - <div class="text-center"> 100 - <LoaderCircle class="mx-auto mb-4 h-8 w-8 animate-spin text-ctp-subtext0" /> 101 - </div> 102 - </div> 103 - {:else if reportsQuery.isError} 104 - <div class="rounded-lg bg-ctp-red/20 p-4 text-ctp-red shadow-lg"> 105 - <h2 class="font-semibold">Error</h2> 106 - <p>{reportsQuery.error?.message}</p> 107 - </div> 108 - {:else if reportsQuery.data} 109 - <h2 class="pb-5 text-xl font-medium text-ctp-text">Recent Reports</h2> 110 - <ul class="space-y-4 text-ctp-text"> 111 - {#each reportsQuery.data.pages as page} 112 - {#each page.subjectStatuses as report} 113 - {@const subject = report.subject} 114 - <li class="rounded-lg border border-ctp-surface1 bg-ctp-surface0 p-4"> 115 - <div class="flex items-center justify-between"> 116 - <div class="flex items-center gap-2"> 117 - {#if report.appealed} 118 - <Scale class="size-5 text-ctp-yellow" /> 119 - {:else if report.reviewState == 'tools.ozone.moderation.defs#reviewClosed'} 120 - <CircleCheck class="size-5 text-ctp-green" /> 121 - {:else} 122 - <TriangleAlert class="size-5 text-ctp-yellow" /> 123 - {/if} 124 - <p> 125 - {#if ComAtprotoRepoStrongRef.isMain(subject)} 126 - <button 127 - on:click={() => (selectedPostUri = new AtUri(subject.uri))} 128 - class="inline-flex cursor-pointer items-center px-0 py-0 text-ctp-blue hover:underline" 129 - > 130 - Post 131 - <ExternalLink size={15} class="ml-1" /> 132 - </button> 133 - created by 134 - <button 135 - on:click={() => (selectedDid = new AtUri(subject.uri).host)} 136 - class="inline-flex cursor-pointer items-center px-0 py-0 text-ctp-blue hover:underline" 137 - > 138 - @{report.subjectRepoHandle} 139 - <ExternalLink size={15} class="ml-1" /> 140 - </button> 141 - {:else if isRepoRef(subject)} 142 - <button 143 - on:click={() => (selectedDid = subject.did)} 144 - class="inline-flex cursor-pointer items-center px-0 py-0 text-ctp-blue hover:underline" 145 - > 146 - @{report.subjectRepoHandle} 147 - <ExternalLink size={15} class="ml-1" /> 148 - </button> 149 - {/if} 150 - </p> 151 - </div> 152 - <p class="text-sm text-ctp-subtext0"> 153 - {formatDate(report.lastReportedAt ?? report.createdAt)} 154 - </p> 155 - </div> 156 - </li> 157 - {/each} 158 - {/each} 159 - {#if reportsQuery.hasNextPage} 160 - <div use:infiniteScroll class="h-10"></div> 161 - {/if} 162 - {#if reportsQuery.isFetchingNextPage} 163 - <div class="flex justify-center py-4"> 164 - <LoaderCircle class="h-6 w-6 animate-spin text-ctp-subtext0" /> 165 - </div> 166 - {/if} 167 - </ul> 168 - {/if} 169 - </div> 170 - </div>