Highly ambitious ATProtocol AppView service and sdks
138
fork

Configure Feed

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

fix lexicon getRecords weirdness, fix lexicon delete handlers and add oob for count

+62 -58
+4 -28
api/src/database/records.rs
··· 221 221 let mut where_clauses = Vec::new(); 222 222 let mut param_count = 1; 223 223 224 - let is_lexicon = where_clause 225 - .as_ref() 226 - .and_then(|wc| wc.conditions.get("collection")) 227 - .and_then(|c| c.eq.as_ref()) 228 - .and_then(|v| v.as_str()) 229 - == Some("network.slices.lexicon"); 230 - 231 224 // Extract collection name from where clause for lexicon lookup 232 225 let collection = where_clause 233 226 .as_ref() ··· 262 255 // Build ORDER BY clause with datetime field information 263 256 let order_by = build_order_by_clause_with_field_info(sort_by, primary_field_is_datetime); 264 257 265 - if is_lexicon { 266 - where_clauses.push(format!("json->>'slice' = ${}", param_count)); 267 - } else { 268 - where_clauses.push(format!("slice_uri = ${}", param_count)); 269 - } 258 + where_clauses.push(format!("slice_uri = ${}", param_count)); 270 259 param_count += 1; 271 260 272 261 // Build all other WHERE conditions first (including collection filter) 273 - // For non-lexicon records, exclude the 'slice' field since we handle it via slice_uri 262 + // Exclude the 'slice' field since we handle it via slice_uri 274 263 let mut filtered_where_clause = None; 275 264 let filtered_clause; 276 265 277 - if is_lexicon { 278 - filtered_where_clause = where_clause; 279 - } else if let Some(wc) = where_clause { 266 + if let Some(wc) = where_clause { 280 267 let mut filtered_conditions = std::collections::HashMap::new(); 281 268 for (field, condition) in &wc.conditions { 282 269 if field != "slice" { ··· 372 359 let mut where_clauses = Vec::new(); 373 360 let mut param_count = 1; 374 361 375 - let is_lexicon = where_clause 376 - .as_ref() 377 - .and_then(|wc| wc.conditions.get("collection")) 378 - .and_then(|c| c.eq.as_ref()) 379 - .and_then(|v| v.as_str()) 380 - == Some("network.slices.lexicon"); 381 - 382 - if is_lexicon { 383 - where_clauses.push(format!("json->>'slice' = ${}", param_count)); 384 - } else { 385 - where_clauses.push(format!("slice_uri = ${}", param_count)); 386 - } 362 + where_clauses.push(format!("slice_uri = ${}", param_count)); 387 363 param_count += 1; 388 364 389 365 let (and_conditions, or_conditions) =
+53 -25
frontend/src/features/slices/lexicon/handlers.tsx
··· 12 12 import { LexiconsList } from "./templates/fragments/LexiconsList.tsx"; 13 13 import { LexiconFormModal } from "./templates/fragments/LexiconFormModal.tsx"; 14 14 import { Badge } from "../../../shared/fragments/Badge.tsx"; 15 + import { Card } from "../../../shared/fragments/Card.tsx"; 15 16 import { FileCode } from "lucide-preact"; 16 17 import { buildSliceUrl } from "../../../utils/slice-params.ts"; 17 18 import type { NetworkSlicesLexicon } from "../../../client.ts"; 18 19 import type { RecordResponse } from "@slices/client"; 20 + import { publicClient } from "../../../config.ts"; 19 21 20 22 async function handleListLexicons( 21 23 req: Request, ··· 287 289 const remainingLexicons = 288 290 await sliceClient.network.slices.lexicon.getRecords(); 289 291 292 + const countHeader = ( 293 + <Card.Header 294 + title={`${remainingLexicons.records.length} ${ 295 + remainingLexicons.records.length === 1 ? "Lexicon" : "Lexicons" 296 + }`} 297 + id="lexicon-count-header" 298 + hx-swap-oob="true" 299 + /> 300 + ); 301 + 290 302 if (remainingLexicons.records.length === 0) { 291 303 return renderHTML( 292 - <EmptyState 293 - icon={<FileCode size={64} strokeWidth={1} />} 294 - title="No lexicons uploaded" 295 - description="Upload lexicon definitions to define custom schemas for this slice." 296 - withPadding 297 - />, 304 + <div> 305 + <EmptyState 306 + icon={<FileCode size={64} strokeWidth={1} />} 307 + title="No lexicons uploaded" 308 + description="Upload lexicon definitions to define custom schemas for this slice." 309 + withPadding 310 + /> 311 + {countHeader} 312 + </div>, 298 313 { 299 314 headers: { 300 315 "HX-Retarget": "#lexicon-list", ··· 302 317 } 303 318 ); 304 319 } else { 305 - return new Response("", { 306 - status: 200, 307 - headers: { "content-type": "text/html" }, 308 - }); 320 + return renderHTML(countHeader); 309 321 } 310 322 } catch (error) { 311 323 console.error("Failed to delete lexicon:", error); ··· 414 426 const remainingLexicons = 415 427 await sliceClient.network.slices.lexicon.getRecords(); 416 428 429 + const countHeader = ( 430 + <Card.Header 431 + title={`${remainingLexicons.records.length} ${ 432 + remainingLexicons.records.length === 1 ? "Lexicon" : "Lexicons" 433 + }`} 434 + id="lexicon-count-header" 435 + hx-swap-oob="true" 436 + /> 437 + ); 438 + 417 439 if (remainingLexicons.records.length === 0) { 418 440 return renderHTML( 419 - <EmptyState 420 - icon={<FileCode size={64} strokeWidth={1} />} 421 - title="No lexicons uploaded" 422 - description="Upload lexicon definitions to define custom schemas for this slice." 423 - withPadding 424 - /> 441 + <div> 442 + <EmptyState 443 + icon={<FileCode size={64} strokeWidth={1} />} 444 + title="No lexicons uploaded" 445 + description="Upload lexicon definitions to define custom schemas for this slice." 446 + withPadding 447 + /> 448 + {countHeader} 449 + </div> 425 450 ); 426 451 } else { 427 452 // Get slice info for domain comparison 428 453 const sliceUri = buildSliceUri(context.currentUser.sub!, sliceId); 429 - const sliceResponse = await sliceClient.network.slices.slice.getRecord({ 454 + const sliceResponse = await publicClient.network.slices.slice.getRecord({ 430 455 uri: sliceUri, 431 456 }); 432 457 const sliceDomain = sliceResponse.value.domain; 433 458 434 459 return renderHTML( 435 - <LexiconsList 436 - records={remainingLexicons.records} 437 - sliceId={sliceId} 438 - handle={handle || undefined} 439 - sliceDomain={sliceDomain} 440 - hasSliceAccess 441 - /> 460 + <div> 461 + <LexiconsList 462 + records={remainingLexicons.records} 463 + sliceId={sliceId} 464 + handle={handle || undefined} 465 + sliceDomain={sliceDomain} 466 + hasSliceAccess 467 + /> 468 + {countHeader} 469 + </div> 442 470 ); 443 471 } 444 472 } catch (error) { ··· 548 576 549 577 // Get the current lexicon record using the URI 550 578 const currentRecord = await sliceClient.network.slices.lexicon.getRecord({ 551 - uri 579 + uri, 552 580 }); 553 581 554 582 // Update the record with the new exclusion status
+1
frontend/src/features/slices/lexicon/templates/SliceLexiconPage.tsx
··· 72 72 title={`${lexicons.length} ${ 73 73 lexicons.length === 1 ? "Lexicon" : "Lexicons" 74 74 }`} 75 + id="lexicon-count-header" 75 76 /> 76 77 <Card.Content id="lexicon-list"> 77 78 {lexicons.length > 0 ? (
+4 -5
frontend/src/shared/fragments/Card.tsx
··· 11 11 children: ComponentChildren; 12 12 } 13 13 14 - interface CardHeaderProps { 14 + type CardHeaderProps = JSX.IntrinsicElements['div'] & { 15 15 title: string; 16 16 action?: JSX.Element; 17 - className?: string; 18 - } 17 + }; 19 18 20 19 interface CardContentProps extends JSX.HTMLAttributes<HTMLDivElement> { 21 20 children: ComponentChildren; ··· 55 54 ); 56 55 } 57 56 58 - Card.Header = function CardHeader({ title, action, className }: CardHeaderProps): JSX.Element { 57 + Card.Header = function CardHeader({ title, action, className, ...props }: CardHeaderProps): JSX.Element { 59 58 return ( 60 - <div className={cn("bg-zinc-50 dark:bg-zinc-800 px-6 py-3 border-b border-zinc-200 dark:border-zinc-700 rounded-t-sm", className)}> 59 + <div className={cn("bg-zinc-50 dark:bg-zinc-800 px-6 py-3 border-b border-zinc-200 dark:border-zinc-700 rounded-t-sm", className)} {...props}> 61 60 <div className="flex items-center justify-between"> 62 61 <Text as="h2" size="base" className="font-semibold"> 63 62 {title}