this repo has no description
0
fork

Configure Feed

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

Add toggle for quote posts

+286 -4
+8 -1
src/pages/sandbox.css
··· 56 56 display: flex; 57 57 align-items: safe center; 58 58 justify-content: center; 59 + overflow-anchor: auto; 59 60 60 61 > .status, 61 - > * > .status { 62 + > *:not(.status-card-link) > .status { 62 63 min-width: 320px; 63 64 max-width: 40em; 64 65 background-color: var(--bg-color); ··· 101 102 } 102 103 .card { 103 104 view-transition-name: status-card; 105 + } 106 + 107 + .status-card-link { 108 + * { 109 + view-transition-name: none !important; 110 + } 104 111 } 105 112 } 106 113 }
+278 -3
src/pages/sandbox.jsx
··· 10 10 import Status from '../components/status'; 11 11 import { getPreferences } from '../utils/api'; 12 12 import FilterContext from '../utils/filter-context'; 13 - import states from '../utils/states'; 13 + import states, { statusKey } from '../utils/states'; 14 14 import store from '../utils/store'; 15 15 16 16 function hashID(obj) { ··· 24 24 ) 25 25 .join('|'); 26 26 } 27 + 28 + const DEFAULT_INSTANCE = 'mastodon.social'; 27 29 28 30 const MOCK_STATUS = ({ toggles = {} } = {}) => { 29 31 console.log('toggles', toggles); ··· 303 305 pollMultiple: false, 304 306 pollExpired: false, 305 307 showCard: false, 308 + showQuotes: false, 309 + quotesCount: '1', 310 + quoteNestingLevel: '0', 306 311 size: 'medium', 307 312 filters: [false, false, false], // hide, blur, warn 308 313 mediaPreference: 'default', ··· 377 382 pollMultiple: toggleState.pollMultiple, 378 383 pollExpired: toggleState.pollExpired, 379 384 showCard: toggleState.showCard, 385 + showQuotes: toggleState.showQuotes, 386 + quotesCount: toggleState.quotesCount, 387 + quoteNestingLevel: toggleState.quoteNestingLevel, 380 388 size: toggleState.size, 381 389 filters: toggleState.filters, 382 390 }, 383 391 }); 384 392 393 + // Directly observe the statusQuotes object for debugging 394 + useEffect(() => { 395 + console.log('Current statusQuotes:', states.statusQuotes); 396 + }, [ 397 + toggleState.showQuotes, 398 + toggleState.quotesCount, 399 + toggleState.quoteNestingLevel, 400 + ]); 401 + 402 + // Create and update quote posts - using a fixed timestamp to keep IDs stable 403 + useEffect(() => { 404 + if (!mockStatus?.id) return; 405 + 406 + // Create a properly formatted sKey for the Status component to find quotes 407 + // Import statusKey from utils/states to create a proper key 408 + const sKey = statusKey(mockStatus.id, DEFAULT_INSTANCE); 409 + 410 + // Log the key we're using 411 + console.log('Quote posts key:', sKey); 412 + 413 + // Clear existing quotes for this status 414 + delete states.statusQuotes[sKey]; 415 + 416 + if (toggleState.showQuotes && parseInt(toggleState.quotesCount, 10) > 0) { 417 + console.log('Setting up quotes for status:', sKey); 418 + 419 + // First, clean up all existing quote posts and their nested quotes 420 + // This ensures that when we decrement nesting level, the nested quotes are removed 421 + const cleanupExistingQuotes = () => { 422 + // Find all existing quote keys for this status 423 + Object.keys(states.statusQuotes).forEach((key) => { 424 + if ( 425 + key.startsWith(DEFAULT_INSTANCE + '/quote-') || 426 + key.startsWith(DEFAULT_INSTANCE + '/nested-quote-') || 427 + key.startsWith(DEFAULT_INSTANCE + '/deep-nested-') 428 + ) { 429 + // Clean up nested quote references 430 + delete states.statusQuotes[key]; 431 + } 432 + }); 433 + 434 + // Also clean up the status objects 435 + Object.keys(states.statuses).forEach((key) => { 436 + if ( 437 + key.startsWith('quote-') || 438 + key.startsWith('nested-quote-') || 439 + key.startsWith('deep-nested-') 440 + ) { 441 + // Remove all quote-related status objects 442 + delete states.statuses[key]; 443 + } 444 + }); 445 + }; 446 + 447 + // Clean up all existing quotes and their status objects 448 + cleanupExistingQuotes(); 449 + 450 + // Create a set of simpler quote references - this is what QuoteStatuses component expects 451 + // Create only the exact number of quotes specified 452 + const quotesCount = parseInt(toggleState.quotesCount, 10); 453 + // Create exactly quotesCount number of quotes - no more, no less 454 + const quotes = Array(quotesCount) 455 + .fill(0) 456 + .map((_, i) => { 457 + // Use a stable ID format that doesn't change on re-render 458 + const quoteId = `quote-${i}-12345`; 459 + const nestingLevel = Math.min( 460 + parseInt(toggleState.quoteNestingLevel, 10) || 0, 461 + 2, 462 + ); 463 + 464 + // Create a simple reference object for QuoteStatuses 465 + const quoteRef = { 466 + id: quoteId, 467 + instance: DEFAULT_INSTANCE, 468 + url: `https://example.social/s/${quoteId}`, // Include URL to ensure uniqueness check works 469 + }; 470 + 471 + // First, delete any existing status with this ID to avoid duplicates 472 + delete states.statuses[quoteId]; 473 + 474 + // Create the actual status object that will be retrieved by QuoteStatuses 475 + states.statuses[quoteId] = { 476 + id: quoteId, 477 + content: `<p>This is quote post ${i + 1}${i % 2 === 0 ? '' : ' with some extra text'}</p>`, 478 + account: { 479 + id: `quote-account-${i}`, 480 + username: `quote${i}`, 481 + name: `Quote User ${i}`, 482 + avatar: '/logo-192.png', 483 + acct: `quote${i}@example.social`, 484 + url: `https://example.social/@quote${i}`, 485 + }, 486 + visibility: 'public', 487 + createdAt: new Date(Date.now() - i * 3600000).toISOString(), // Each post 1 hour older 488 + emojis: [], 489 + // First quote post should be plain (no media, no poll) 490 + mediaAttachments: 491 + i > 0 && i % 2 === 0 492 + ? [ 493 + { 494 + // Only non-first posts can have media (every 3rd post after the 1st) 495 + id: `quote-media-${i}`, 496 + type: 'image', 497 + url: `https://picsum.photos/seed/quote-${i}/600/400`, 498 + previewUrl: `https://picsum.photos/seed/quote-${i}/300/200`, 499 + meta: { 500 + original: { width: 600, height: 400 }, 501 + small: { width: 300, height: 200 }, 502 + }, 503 + }, 504 + ] 505 + : [], 506 + poll: 507 + i > 0 && i % 3 === 0 508 + ? { 509 + // Only non-first posts can have polls (every 4th post after the 1st) 510 + id: `quote-poll-${i}`, 511 + options: [ 512 + { 513 + title: 'Option A', 514 + votesCount: Math.floor(Math.random() * 50), 515 + }, 516 + { 517 + title: 'Option B', 518 + votesCount: Math.floor(Math.random() * 50), 519 + }, 520 + ], 521 + expiresAt: new Date( 522 + Date.now() + 24 * 60 * 60 * 1000, 523 + ).toISOString(), 524 + multiple: false, 525 + votesCount: Math.floor(Math.random() * 100), 526 + } 527 + : null, 528 + }; 529 + 530 + // If nesting level > 0, add nested quotes to each quote post 531 + if (nestingLevel > 0 && i % 2 === 0) { 532 + // Add nested quotes to every other quote - use stable ID 533 + const nestedQuoteId = `nested-quote-${i}-12345`; 534 + 535 + // Add the nested quote post to states.statuses 536 + states.statuses[nestedQuoteId] = { 537 + id: nestedQuoteId, 538 + content: `<p>This is a nested quote inside quote ${i + 1}</p>`, 539 + account: { 540 + id: `nested-account-${i}`, 541 + username: `nested${i}`, 542 + name: `Nested User ${i}`, 543 + avatar: '/logo-192.png', 544 + acct: `nested${i}@example.social`, 545 + url: `https://example.social/@nested${i}`, 546 + }, 547 + visibility: 'public', 548 + createdAt: new Date(Date.now() - (i + 1) * 3600000).toISOString(), 549 + emojis: [], 550 + mediaAttachments: [], // No media in nested quotes for simplicity 551 + }; 552 + 553 + // Create reference object for nested quote - critical for proper rendering 554 + const nestedQuoteRef = { 555 + id: nestedQuoteId, 556 + instance: DEFAULT_INSTANCE, 557 + url: `https://example.social/s/${nestedQuoteId}`, 558 + }; 559 + 560 + // Add another level of nesting if specified 561 + if (nestingLevel > 1 && i === 0) { 562 + // Only add deepest nesting to first quote 563 + const deepNestedId = `deep-nested-${i}-12345`; 564 + 565 + states.statuses[deepNestedId] = { 566 + id: deepNestedId, 567 + content: `<p>This is a deeply nested quote (level 2)</p>`, 568 + account: { 569 + id: `deep-account-${i}`, 570 + username: `deep${i}`, 571 + name: `Deep User ${i}`, 572 + avatar: '/logo-192.png', 573 + acct: `deep${i}@example.social`, 574 + url: `https://example.social/@deep${i}`, 575 + }, 576 + visibility: 'public', 577 + createdAt: new Date( 578 + Date.now() - (i + 2) * 3600000, 579 + ).toISOString(), 580 + emojis: [], 581 + }; 582 + 583 + // Create deep nested reference 584 + const deepNestedRef = { 585 + id: deepNestedId, 586 + instance: DEFAULT_INSTANCE, 587 + url: `https://example.social/s/${deepNestedId}`, 588 + }; 589 + 590 + // Important: Use the proper key format for the nested quote 591 + const nestedKey = statusKey(nestedQuoteId, DEFAULT_INSTANCE); 592 + states.statusQuotes[nestedKey] = [deepNestedRef]; 593 + } 594 + 595 + // Add nested quote to the quote's quotes using the proper key format 596 + const quoteKey = statusKey(quoteId, DEFAULT_INSTANCE); 597 + states.statusQuotes[quoteKey] = [nestedQuoteRef]; 598 + } 599 + 600 + return quoteRef; 601 + }); 602 + 603 + // Set the quotes for the main status 604 + states.statusQuotes[sKey] = quotes; 605 + } else { 606 + // Remove quotes when toggle is turned off 607 + delete states.statusQuotes[sKey]; 608 + } 609 + }, [ 610 + mockStatus?.id, 611 + toggleState.showQuotes, 612 + toggleState.quotesCount, 613 + toggleState.quoteNestingLevel, 614 + ]); 615 + 385 616 // Handler for filter checkboxes 386 617 const handleFilterChange = (index) => { 387 618 const newFilters = [...toggleState.filters]; ··· 416 647 ? 'm' 417 648 : 'l' 418 649 } 650 + instance={DEFAULT_INSTANCE} 419 651 allowFilters={true} 420 - // Add a key that changes when preferences change to force re-render 421 - key={`status-${toggleState.mediaPreference}-${toggleState.expandWarnings}-${mockStatus.id}`} 422 652 // Prevent opening as URL 423 653 onMediaClick={(e, i, media, status) => { 424 654 e.preventDefault(); ··· 784 1014 /> 785 1015 <span>Link preview card</span> 786 1016 </label> 1017 + </li> 1018 + <li> 1019 + <label> 1020 + <input 1021 + type="checkbox" 1022 + checked={toggleState.showQuotes} 1023 + onChange={() => 1024 + updateToggles({ showQuotes: !toggleState.showQuotes }) 1025 + } 1026 + /> 1027 + <span>Quote post</span> 1028 + <input 1029 + type="number" 1030 + min="1" 1031 + max="10" 1032 + value={toggleState.quotesCount} 1033 + step="1" 1034 + onChange={(e) => { 1035 + // Make sure to convert to a number first to avoid string concatenation 1036 + const count = parseInt(e.target.value, 10) || 1; 1037 + updateToggles({ quotesCount: String(count) }); 1038 + }} 1039 + disabled={!toggleState.showQuotes} 1040 + /> 1041 + </label> 1042 + <ul> 1043 + <li> 1044 + <label> 1045 + <span>Nested quote post</span> 1046 + <input 1047 + type="number" 1048 + min="0" 1049 + max="2" 1050 + value={toggleState.quoteNestingLevel} 1051 + step="1" 1052 + onChange={(e) => { 1053 + // Make sure to convert to a number first to avoid string concatenation 1054 + const level = parseInt(e.target.value, 10) || 0; 1055 + updateToggles({ quoteNestingLevel: String(level) }); 1056 + }} 1057 + disabled={!toggleState.showQuotes} 1058 + /> 1059 + </label> 1060 + </li> 1061 + </ul> 787 1062 </li> 788 1063 </ul> 789 1064 </li>