this repo has no description
0
fork

Configure Feed

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

Initial composer relayout

+757 -572
+2 -2
src/components/ICONS.jsx
··· 16 16 }, 17 17 'arrow-up': () => import('@iconify-icons/mingcute/arrow-up-line'), 18 18 'arrow-down': () => import('@iconify-icons/mingcute/arrow-down-line'), 19 - earth: () => import('@iconify-icons/mingcute/earth-line'), 19 + earth: () => import('@iconify-icons/mingcute/world-2-line'), 20 20 lock: () => import('@iconify-icons/mingcute/lock-line'), 21 21 unlock: () => import('@iconify-icons/mingcute/unlock-line'), 22 22 'eye-close': () => import('@iconify-icons/mingcute/eye-close-line'), ··· 174 174 settings: () => import('@iconify-icons/mingcute/settings-6-line'), 175 175 'heart-break': () => import('@iconify-icons/mingcute/heart-crack-line'), 176 176 'user-x': () => import('@iconify-icons/mingcute/user-x-line'), 177 - minimize: () => import('@iconify-icons/mingcute/arrows-down-line'), 177 + minimize: () => import('@iconify-icons/mingcute/down-line'), 178 178 celebrate: () => import('@iconify-icons/mingcute/celebrate-line'), 179 179 schedule: () => import('@iconify-icons/mingcute/calendar-time-add-line'), 180 180 day: () => import('@iconify-icons/mingcute/calendar-day-line'),
+49 -46
src/components/compose-poll.jsx
··· 55 55 </TextExpander> 56 56 <button 57 57 type="button" 58 - class="plain2 poll-button" 58 + class="plain4 poll-button" 59 59 disabled={disabled || options.length <= 1} 60 60 onClick={() => { 61 61 options.splice(i, 1); 62 62 onInput(poll); 63 63 }} 64 + title={t`Remove`} 64 65 > 65 - <Icon icon="x" size="s" alt={t`Remove`} /> 66 + 66 67 </button> 67 68 </div> 68 69 ))} ··· 76 77 options.push(''); 77 78 onInput(poll); 78 79 }} 80 + title={t`Add`} 79 81 > 80 82 + 81 83 </button>{' '} 82 - <label class="multiple-choices"> 83 - <input 84 - type="checkbox" 85 - checked={multiple} 84 + <div class="poll-config"> 85 + <label class="multiple-choices"> 86 + <input 87 + type="checkbox" 88 + checked={multiple} 89 + disabled={disabled} 90 + onChange={(e) => { 91 + const { checked } = e.target; 92 + poll.multiple = checked; 93 + onInput(poll); 94 + }} 95 + />{' '} 96 + <Trans>Multiple choice</Trans> 97 + </label> 98 + <label class="expires-in"> 99 + <Trans>Duration</Trans>{' '} 100 + <select 101 + value={expiresIn} 102 + disabled={disabled} 103 + onChange={(e) => { 104 + const { value } = e.target; 105 + poll.expiresIn = value; 106 + onInput(poll); 107 + }} 108 + > 109 + {Object.entries(expiryOptions) 110 + .filter(([value]) => { 111 + return value >= minExpiration && value <= maxExpiration; 112 + }) 113 + .map(([value, label]) => ( 114 + <option value={value} key={value}> 115 + {label()} 116 + </option> 117 + ))} 118 + </select> 119 + </label> 120 + <div class="spacer" /> 121 + <button 122 + type="button" 123 + class="light danger small" 86 124 disabled={disabled} 87 - onChange={(e) => { 88 - const { checked } = e.target; 89 - poll.multiple = checked; 90 - onInput(poll); 91 - }} 92 - />{' '} 93 - <Trans>Multiple choices</Trans> 94 - </label> 95 - <label class="expires-in"> 96 - <Trans>Duration</Trans>{' '} 97 - <select 98 - value={expiresIn} 99 - disabled={disabled} 100 - onChange={(e) => { 101 - const { value } = e.target; 102 - poll.expiresIn = value; 103 - onInput(poll); 125 + onClick={() => { 126 + onInput(null); 104 127 }} 105 128 > 106 - {Object.entries(expiryOptions) 107 - .filter(([value]) => { 108 - return value >= minExpiration && value <= maxExpiration; 109 - }) 110 - .map(([value, label]) => ( 111 - <option value={value} key={value}> 112 - {label()} 113 - </option> 114 - ))} 115 - </select> 116 - </label> 117 - </div> 118 - <div class="poll-toolbar"> 119 - <button 120 - type="button" 121 - class="plain remove-poll-button" 122 - disabled={disabled} 123 - onClick={() => { 124 - onInput(null); 125 - }} 126 - > 127 - <Trans>Remove poll</Trans> 128 - </button> 129 + <Trans>Remove poll</Trans> 130 + </button> 131 + </div> 129 132 </div> 130 133 </div> 131 134 );
+190 -91
src/components/compose.css
··· 10 10 #compose-container { 11 11 margin: auto; 12 12 width: var(--main-width); 13 - max-width: 100vw; 13 + max-width: 100%; 14 14 align-self: stretch; 15 15 animation: fade-in 0.2s ease-out; 16 16 } ··· 27 27 white-space: nowrap; 28 28 29 29 @media (min-width: 480px) { 30 - padding: 16px; 30 + padding-block: 16px; 31 + } 32 + 33 + .compose-controls { 34 + display: flex; 35 + background-color: var(--bg-faded-blur-color); 36 + backdrop-filter: blur(16px); 37 + border-radius: 9999px; 38 + border: 2px solid var(--bg-faded-color); 39 + 40 + button:is(:hover, :focus) { 41 + background-color: var(--bg-blur-color); 42 + } 31 43 } 32 44 } 33 45 #compose-container .compose-top .account-block { ··· 41 53 backdrop-filter: blur(16px); 42 54 padding-inline-end: 1em; 43 55 border-radius: 9999px; 56 + border: 2px solid var(--bg-faded-color); 44 57 } 45 58 46 59 @keyframes appear-up { ··· 57 70 border-radius: 16px 16px 0 0; 58 71 max-height: 160px; 59 72 background-color: var(--bg-color); 60 - margin: 0 12px; 73 + margin: 0 8px; 61 74 border: 1px solid var(--outline-color); 62 75 border-bottom: 0; 63 76 animation: appear-up 1s ease-in-out; 64 77 overflow: auto; 65 - box-shadow: 0 -3px 12px -3px var(--drop-shadow-color); 78 + box-shadow: 0 0px 12px -6px var(--drop-shadow-color); 66 79 } 67 80 #compose-container .status-preview:has(.status-badge:not(:empty)) { 68 81 border-start-end-radius: 8px; ··· 117 130 } 118 131 119 132 #compose-container form { 120 - --form-spacing-inline: 4px; 121 - --form-spacing-block: 0; 122 - /* border-radius: 16px; */ 133 + --form-spacing-inline: 0; 134 + --form-spacing-block: 8px; 123 135 padding: var(--form-spacing-block) var(--form-spacing-inline); 124 136 background-color: var(--bg-blur-color); 125 - /* background-image: linear-gradient(var(--bg-color) 85%, transparent); */ 126 137 position: relative; 127 138 z-index: 2; 128 - --drop-shadow: 0 3px 6px -3px var(--drop-shadow-color); 139 + --drop-shadow: 0 3px 6px -4px var(--drop-shadow-color); 129 140 box-shadow: var(--drop-shadow); 141 + display: flex; 142 + flex-direction: column; 143 + gap: var(--form-spacing-block); 130 144 131 - @media (min-width: 480px) { 145 + @media (min-width: 40em) { 132 146 --form-spacing-inline: 8px; 133 - } 134 - 135 - @media (min-width: 40em) { 136 147 border-radius: 16px; 148 + border-start-start-radius: 8px; 149 + border-start-end-radius: 8px; 137 150 } 138 151 } 139 152 #compose-container .status-preview ~ form { ··· 145 158 #compose-container textarea { 146 159 width: 100%; 147 160 max-width: 100%; 161 + line-height: 1.4; 162 + border-color: transparent; 163 + 164 + &:hover { 165 + border-color: var(--divider-color); 166 + } 167 + } 168 + 169 + #compose-container .compose-field { 148 170 height: 5em; 171 + height: var(--height); 149 172 min-height: 5em; 150 173 max-height: 50vh; 151 174 resize: vertical; 152 - line-height: 1.4; 153 - border-color: transparent; 175 + border-radius: 0; 154 176 155 - &.compose-field { 156 - @media (min-width: 40em) { 157 - max-height: 65vh; 158 - } 177 + @media (min-width: 40em) { 178 + max-height: 65vh; 179 + border-radius: 4px; 159 180 } 160 181 } 161 - #compose-container textarea:hover { 162 - border-color: var(--divider-color); 163 - } 164 182 165 183 #compose-container .toolbar { 166 184 display: flex; 167 185 justify-content: space-between; 168 186 align-items: center; 169 - padding: var(--form-spacing-inline) 0; 170 - gap: var(--form-spacing-inline); 187 + gap: 4px; 188 + padding-inline: 8px; 189 + 190 + @media (min-width: 40em) { 191 + padding-inline: 0; 192 + } 171 193 } 172 194 #compose-container .toolbar.wrap { 173 195 flex-wrap: wrap; ··· 175 197 #compose-container .toolbar.stretch { 176 198 justify-content: stretch; 177 199 } 178 - #compose-container .toolbar { 179 - .spoiler-text-field-container { 180 - flex: 1; 181 - min-width: 0; 182 - 183 - .spoiler-text-field { 184 - width: 100%; 185 - } 186 - } 187 - } 188 200 #compose-container .toolbar-button { 189 201 display: inline-block; 190 - color: var(--link-color); 202 + color: var(--text-insignificant-color); 191 203 background-color: transparent; 192 204 padding: 0 8px; 193 205 border-radius: 8px; 194 206 min-height: 2.4em; 195 - line-height: 2.4em; 196 207 min-width: 2.6em; 197 208 text-align: center; 198 209 overflow: hidden; 199 210 position: relative; 200 - white-space: nowrap; 201 211 border: 2px solid transparent; 202 212 vertical-align: middle; 213 + align-content: center; 214 + word-wrap: normal; 215 + overflow: hidden; 203 216 204 217 &.active { 205 218 filter: brightness(0.8); ··· 218 231 opacity: 0.5; 219 232 } 220 233 #compose-container 221 - .toolbar-button:not(.show-field) 234 + .toolbar-button 222 235 :is(input[type='checkbox'], select, input[type='file']) { 223 236 opacity: 0; 224 237 position: absolute; 225 - left: 0; 226 - height: 100%; 238 + inset: 0; 227 239 margin: 0; 228 240 } 229 241 #compose-container .toolbar-button input[type='file'] { ··· 238 250 appearance: none; 239 251 line-height: 1em; 240 252 } 241 - #compose-container .toolbar-button:not(.show-field) select { 242 - inset-inline-end: 0; 243 - inset-inline-start: auto !important; 244 - } 245 253 #compose-container 246 254 .toolbar-button:not(:disabled):is( 247 255 :hover, ··· 254 262 background-color: var(--bg-color); 255 263 border-color: var(--link-faded-color); 256 264 outline: 0; 265 + color: var(--link-color); 257 266 } 258 267 #compose-container .toolbar-button:not(:disabled).highlight { 259 - border-color: var(--link-color); 260 - box-shadow: inset 0 0 8px var(--link-faded-color); 268 + color: var(--text-color); 269 + border-color: var(--link-faded-color); 270 + background-color: var(--bg-blur-color); 271 + 272 + &:where(:focus-within, :hover) { 273 + background-color: var(--link-bg-color); 274 + } 261 275 } 262 276 #compose-container .toolbar-button:not(:disabled):active { 263 277 filter: brightness(0.8); ··· 266 280 #compose-container .toolbar-button .icon-text { 267 281 display: inline-block; 268 282 font-size: 14px; 269 - font-weight: 500; 270 283 text-overflow: ellipsis; 271 284 overflow: hidden; 272 285 max-width: 100%; 273 286 min-width: 4ch; 287 + display: -webkit-box; 288 + -webkit-box-orient: vertical; 289 + -webkit-line-clamp: 2; 290 + line-clamp: 2; 291 + } 292 + 293 + #compose-container .toolbar-divider { 294 + width: 1px; 295 + height: 100%; 296 + margin: 0 var(--hairline-width); 297 + background-image: linear-gradient( 298 + to bottom, 299 + transparent, 300 + var(--divider-color), 301 + transparent 302 + ); 303 + flex-shrink: 0; 274 304 } 275 305 276 306 #compose-container .compose-footer { 277 307 .add-toolbar-button-group { 278 308 display: flex; 279 309 overflow: auto; 310 + min-width: 42px; 280 311 } 281 312 .add-sub-toolbar-button-group { 282 313 flex-grow: 1; ··· 387 418 } 388 419 389 420 #compose-container .media-attachments { 390 - background-color: var(--bg-faded-color); 421 + background-color: var(--bg-faded-blur-color); 391 422 padding: 8px; 392 - border-radius: 8px; 393 - margin: 8px 0 0; 423 + /* margin: 8px 0 0; */ 394 424 display: flex; 395 425 flex-direction: column; 396 426 gap: 8px; 427 + 428 + @media (min-width: 40em) { 429 + border-radius: 8px; 430 + } 397 431 } 398 432 #compose-container .media-attachment { 399 433 display: flex; ··· 473 507 justify-content: space-between; 474 508 align-items: center; 475 509 } 476 - #compose-container .media-aside .close-button { 477 - padding: 4px; 478 - align-self: flex-start; 479 - color: var(--text-insignificant-color); 480 - } 481 - #compose-container .media-aside .close-button:is(:hover, :focus) { 482 - color: var(--text-color); 483 - } 484 510 #compose-container .media-aside .uploaded { 485 511 color: var(--green-color); 486 512 margin-bottom: 4px; 487 513 } 488 514 515 + #compose-container form .close-button { 516 + padding: 4px; 517 + align-self: center; 518 + color: var(--text-insignificant-color); 519 + 520 + &:is(:hover, :focus) { 521 + color: var(--text-color); 522 + background-color: var(--bg-color); 523 + } 524 + } 525 + 489 526 #compose-container .media-sensitive { 490 527 padding: 8px; 491 528 background-color: var(--bg-blur-color); ··· 497 534 } 498 535 499 536 #compose-container form .poll { 500 - background-color: var(--bg-faded-color); 501 - border-radius: 8px; 502 - margin: 8px 0 0; 537 + background-color: var(--bg-faded-blur-color); 538 + background-image: none; 539 + box-shadow: none; 540 + border: 0; 541 + margin: 0; 503 542 display: block; 543 + border-radius: 0; 544 + 545 + @media (min-width: 40em) { 546 + border-radius: 8px; 547 + } 504 548 } 505 549 506 550 #compose-container .poll-choices { ··· 533 577 flex-shrink: 0; 534 578 line-height: 0; 535 579 overflow: hidden; 536 - transition: border-radius 1s ease-out; 580 + transition: border-radius 0.5s ease-out; 537 581 font-size: 14px; 538 582 } 539 583 #compose-container .multiple .poll-button { ··· 546 590 align-items: stretch; 547 591 justify-content: space-between; 548 592 font-size: 90%; 549 - border-top: 1px solid var(--outline-color); 593 + border-top: 1px solid var(--bg-color); 550 594 padding: 8px; 551 595 } 552 596 #compose-container .poll-toolbar select { 553 597 padding: 4px; 554 598 } 555 599 556 - #compose-container .multiple-choices { 600 + #compose-container .poll-config { 557 601 flex-grow: 1; 558 602 display: flex; 559 - gap: 4px; 603 + flex-direction: row; 604 + column-gap: 8px; 605 + row-gap: 4px; 560 606 align-items: center; 561 - border-inline-start: 1px solid var(--outline-color); 562 - padding-inline-start: 8px; 607 + flex-wrap: wrap; 563 608 } 564 609 565 - #compose-container .expires-in { 566 - flex-grow: 1; 567 - border-inline-start: 1px solid var(--outline-color); 568 - padding-inline-start: 8px; 610 + #compose-container .multiple-choices { 569 611 display: flex; 570 612 gap: 4px; 571 - flex-wrap: wrap; 572 613 align-items: center; 573 - justify-content: flex-end; 614 + padding: 8px; 615 + background-color: var(--bg-blur-color); 616 + border-radius: 8px; 617 + width: fit-content; 574 618 } 575 619 576 - #compose-container .remove-poll-button { 577 - width: 100%; 578 - color: var(--red-color); 620 + #compose-container .expires-in { 621 + display: flex; 622 + gap: 8px; 623 + flex-wrap: wrap; 624 + align-items: center; 625 + padding: 3px; 626 + padding-inline-start: 8px; 627 + background-color: var(--bg-blur-color); 628 + border-radius: 8px; 629 + width: fit-content; 579 630 } 580 631 581 632 #compose-container { 582 633 .scheduled-at { 583 - background-color: var(--bg-faded-color); 584 - border-radius: 8px; 585 - margin: 8px 0 0; 586 - justify-content: flex-end; 587 - text-align: end; 634 + background-color: var(--bg-faded-blur-color); 635 + padding: 8px; 636 + 637 + @media (min-width: 40em) { 638 + border-radius: 8px; 639 + } 588 640 589 641 input[type='datetime-local'] { 590 642 max-width: 80vw; ··· 636 688 } 637 689 638 690 #compose-container button[type='submit'] { 639 - border-radius: 8px; 691 + border-radius: 9px; 692 + align-self: stretch; 640 693 641 - @media (min-width: 480px) { 694 + /* @media (min-width: 480px) { 642 695 padding-inline: 24px; 643 696 font-size: 125%; 644 - } 697 + } */ 645 698 } 646 699 647 700 @keyframes breathe { ··· 932 985 } 933 986 } 934 987 988 + .compose-cw-container { 989 + display: flex; 990 + 991 + &.collapsed { 992 + display: none; 993 + } 994 + 995 + &:not(.collapsed) { 996 + .spoiler-text-field-container { 997 + flex: 1; 998 + min-width: 0; 999 + border-bottom: 2px dashed var(--divider-color); 1000 + 1001 + .spoiler-text-field { 1002 + width: 100%; 1003 + padding-inline-end: calc(24px + 8px); 1004 + border-color: transparent; 1005 + border-radius: 0; 1006 + 1007 + &:hover { 1008 + border-color: var(--divider-color); 1009 + border-style: solid; 1010 + } 1011 + 1012 + @media (max-width: 40em) { 1013 + outline-offset: -2px; 1014 + } 1015 + 1016 + @media (min-width: 40em) { 1017 + border-start-start-radius: 4px; 1018 + border-start-end-radius: 4px; 1019 + } 1020 + } 1021 + 1022 + + button { 1023 + position: absolute; 1024 + inset-inline-end: 8px; 1025 + 1026 + @media (min-width: 40em) { 1027 + inset-inline-end: 16px; 1028 + } 1029 + } 1030 + } 1031 + 1032 + + .compose-field-container .compose-field { 1033 + border-start-start-radius: 0 !important; 1034 + border-start-end-radius: 0 !important; 1035 + } 1036 + } 1037 + } 1038 + 935 1039 .compose-field-container { 936 1040 display: grid !important; 937 1041 938 - @media (width < 480px) { 939 - margin-inline: calc(-1 * var(--form-spacing-inline)); 940 - width: 100vw !important; 941 - max-width: 100vw; 942 - 1042 + @media (max-width: 40em) { 943 1043 .compose-field { 944 - border-radius: 0; 945 1044 outline-offset: -2px; 946 1045 } 947 1046 } ··· 1067 1166 1068 1167 .add-button { 1069 1168 transform-origin: var(--forward) center; 1070 - background-color: var(--bg-blur-color) !important; 1169 + background-color: var(--bg-color) !important; 1071 1170 animation: jump-scare 0.2s ease-in-out both; 1072 1171 :dir(rtl) & { 1073 1172 animation-name: jump-scare-rtl;
+293 -218
src/components/compose.jsx
··· 2 2 3 3 import { msg, plural } from '@lingui/core/macro'; 4 4 import { Trans, useLingui } from '@lingui/react/macro'; 5 - import { MenuItem } from '@szhsin/react-menu'; 5 + import { MenuDivider, MenuItem } from '@szhsin/react-menu'; 6 6 import { deepEqual } from 'fast-equals'; 7 7 import { useEffect, useMemo, useRef, useState } from 'preact/hooks'; 8 8 import { useHotkeys } from 'react-hotkeys-hook'; ··· 31 31 import useCloseWatcher from '../utils/useCloseWatcher'; 32 32 import useInterval from '../utils/useInterval'; 33 33 import visibilityIconsMap from '../utils/visibility-icons-map'; 34 + import visibilityText from '../utils/visibility-text'; 34 35 35 36 import AccountBlock from './account-block'; 36 37 // import Avatar from './avatar'; ··· 102 103 customEmoji: msg`Add custom emoji`, 103 104 gif: msg`Add GIF`, 104 105 poll: msg`Add poll`, 106 + sensitive: msg`Add content warning`, 105 107 scheduledPost: msg`Schedule post`, 106 108 }; 107 109 110 + const DEFAULT_SCHEDULED_AT = Math.max(10 * 60 * 1000, MIN_SCHEDULED_AT); // 10 mins 111 + 108 112 function Compose({ 109 113 onClose, 110 114 replyToStatus, ··· 160 164 161 165 const [visibility, setVisibility] = useState('public'); 162 166 const [sensitive, setSensitive] = useState(false); 167 + const [sensitiveMedia, setSensitiveMedia] = useState(false); 163 168 const [language, setLanguage] = useState( 164 169 store.session.get('currentLanguage') || DEFAULT_LANG, 165 170 ); ··· 218 223 targetElement.dispatchEvent(new Event('input')); 219 224 }; 220 225 226 + const lastFocusedFieldRef = useRef(null); 221 227 const lastFocusedEmojiFieldRef = useRef(null); 228 + const focusLastFocusedField = () => { 229 + setTimeout(() => { 230 + if (!lastFocusedFieldRef.current) return; 231 + lastFocusedFieldRef.current.focus(); 232 + }, 0); 233 + }; 222 234 const composeContainerRef = useRef(null); 223 235 useEffect(() => { 224 236 const handleFocus = (e) => { 237 + // Toggle focused if in or out if any fields are focused 238 + composeContainerRef.current.classList.toggle( 239 + 'focused', 240 + e.type === 'focusin', 241 + ); 242 + 225 243 const target = e.target; 226 244 if (target.hasAttribute('data-allow-custom-emoji')) { 227 245 lastFocusedEmojiFieldRef.current = target; 228 246 } 247 + const isFormElement = ['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA'].includes( 248 + target.tagName, 249 + ); 250 + if (isFormElement) { 251 + lastFocusedFieldRef.current = target; 252 + } 229 253 }; 230 254 231 255 const composeContainer = composeContainerRef.current; 232 256 if (composeContainer) { 233 257 composeContainer.addEventListener('focusin', handleFocus); 258 + composeContainer.addEventListener('focusout', handleFocus); 234 259 } 235 260 236 261 return () => { 237 262 if (composeContainer) { 238 263 composeContainer.removeEventListener('focusin', handleFocus); 264 + composeContainer.removeEventListener('focusout', handleFocus); 239 265 } 240 266 }; 241 267 }, []); ··· 328 354 visibility, 329 355 language, 330 356 sensitive, 357 + sensitiveMedia, 331 358 poll, 332 359 mediaAttachments, 333 360 scheduledAt, ··· 347 374 prefs['posting:default:language']?.toLowerCase() || 348 375 DEFAULT_LANG, 349 376 ); 377 + if (sensitiveMedia !== null) setSensitiveMedia(sensitiveMedia); 350 378 if (sensitive !== null) setSensitive(sensitive); 351 379 if (composablePoll) setPoll(composablePoll); 352 380 if (mediaAttachments) setMediaAttachments(mediaAttachments); ··· 538 566 visibility, 539 567 language, 540 568 sensitive, 569 + sensitiveMedia, 541 570 poll, 542 571 mediaAttachments, 543 572 scheduledAt, ··· 707 736 states.composerState.minimized = true; 708 737 }; 709 738 710 - const gifPickerDisabled = 739 + const mediaButtonDisabled = 711 740 uiState === 'loading' || 712 741 (maxMediaAttachments !== undefined && 713 742 mediaAttachments.length >= maxMediaAttachments) || 714 743 !!poll; 715 744 745 + const cwButtonDisabled = uiState === 'loading' || !!sensitive; 746 + const onCWButtonClick = () => { 747 + setSensitive(true); 748 + setTimeout(() => { 749 + spoilerTextRef.current?.focus(); 750 + }, 0); 751 + }; 752 + 716 753 // If maxOptions is not defined or defined and is greater than 1, show poll button 717 754 const showPollButton = maxOptions == null || maxOptions > 1; 718 755 const pollButtonDisabled = ··· 723 760 expiresIn: 24 * 60 * 60, // 1 day 724 761 multiple: false, 725 762 }); 763 + // Focus first choice field 764 + setTimeout(() => { 765 + composeContainerRef.current 766 + ?.querySelector('.poll-choice input[type="text"]') 767 + ?.focus(); 768 + }, 0); 726 769 }; 727 770 771 + const highlightLanguageField = 772 + language !== prevLanguage.current || 773 + (autoDetectedLanguages?.length && 774 + !autoDetectedLanguages.includes(language)); 775 + const highlightVisibilityField = visibility !== 'public'; 776 + 728 777 const addSubToolbarRef = useRef(); 729 778 const [showAddButton, setShowAddButton] = useState(false); 779 + const BUTTON_WIDTH = 42; // roughly one button width 730 780 useResizeObserver({ 731 781 ref: addSubToolbarRef, 732 782 box: 'border-box', ··· 734 784 // If scrollable, it's truncated 735 785 const { scrollWidth } = addSubToolbarRef.current; 736 786 const truncated = scrollWidth > width; 737 - const overTruncated = width < 84; // roughly two buttons width 787 + const overTruncated = width < BUTTON_WIDTH * 4; 738 788 setShowAddButton(overTruncated || truncated); 739 789 addSubToolbarRef.current.hidden = overTruncated; 740 790 }, ··· 743 793 const showScheduledAt = !editStatus; 744 794 const scheduledAtButtonDisabled = uiState === 'loading' || !!scheduledAt; 745 795 const onScheduledAtClick = () => { 746 - const date = new Date(Date.now() + MIN_SCHEDULED_AT); 796 + const date = new Date(Date.now() + DEFAULT_SCHEDULED_AT); 747 797 setScheduledAt(date); 748 798 }; 749 799 750 800 return ( 751 801 <div id="compose-container-outer" ref={composeContainerRef}> 752 - <div id="compose-container" class={standalone ? 'standalone' : ''}> 802 + <div 803 + id="compose-container" 804 + tabIndex={-1} 805 + class={standalone ? 'standalone' : ''} 806 + > 753 807 <div class="compose-top"> 754 808 {currentAccountInfo?.avatarStatic && ( 755 809 // <Avatar ··· 823 877 </button>{' '} 824 878 <button 825 879 type="button" 826 - class="light close-button" 880 + class="plain4 close-button" 827 881 disabled={uiState === 'loading'} 828 882 onClick={() => { 829 883 if (confirmClose()) { ··· 888 942 visibility, 889 943 language, 890 944 sensitive, 945 + sensitiveMedia, 891 946 poll, 892 947 mediaAttachments, 893 948 scheduledAt, ··· 954 1009 pointerEvents: uiState === 'loading' ? 'none' : 'auto', 955 1010 opacity: uiState === 'loading' ? 0.5 : 1, 956 1011 }} 1012 + onClick={() => { 1013 + lastFocusedFieldRef.current?.focus?.(); 1014 + }} 957 1015 onKeyDown={(e) => { 958 1016 if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { 959 1017 formRef.current.dispatchEvent( ··· 967 1025 const formData = new FormData(e.target); 968 1026 const entries = Object.fromEntries(formData.entries()); 969 1027 console.log('ENTRIES', entries); 970 - let { status, visibility, sensitive, spoilerText, scheduledAt } = 971 - entries; 1028 + let { 1029 + status, 1030 + visibility, 1031 + sensitive, 1032 + sensitiveMedia, 1033 + spoilerText, 1034 + scheduledAt, 1035 + } = entries; 972 1036 973 1037 // Pre-cleanup 974 - sensitive = sensitive === 'on'; // checkboxes return "on" if checked 1038 + // checkboxes return "on" if checked 1039 + sensitive = sensitive === 'on'; 1040 + sensitiveMedia = sensitiveMedia === 'on'; 975 1041 976 1042 // Convert datetime-local input value to RFC3339 Date string value 977 1043 scheduledAt = scheduledAt ··· 1085 1151 // spoilerText, 1086 1152 spoiler_text: spoilerText, 1087 1153 language, 1088 - sensitive, 1154 + sensitive: sensitive || sensitiveMedia, 1089 1155 poll, 1090 1156 // mediaIds: mediaAttachments.map((attachment) => attachment.id), 1091 1157 media_ids: mediaAttachments.map( ··· 1156 1222 })(); 1157 1223 }} 1158 1224 > 1159 - <div class="toolbar stretch"> 1160 - <TextExpander 1161 - keys=":" 1162 - class="spoiler-text-field-container" 1225 + <div> 1226 + <div class={`compose-cw-container ${sensitive ? '' : 'collapsed'}`}> 1227 + <TextExpander 1228 + keys=":" 1229 + class="spoiler-text-field-container" 1230 + onTrigger={(action) => { 1231 + if (action?.name === 'custom-emojis') { 1232 + setShowEmoji2Picker({ 1233 + targetElement: spoilerTextRef, 1234 + defaultSearchTerm: action?.defaultSearchTerm || null, 1235 + }); 1236 + } 1237 + }} 1238 + > 1239 + <input 1240 + ref={spoilerTextRef} 1241 + type="text" 1242 + name="spoilerText" 1243 + placeholder={t`Content warning`} 1244 + data-allow-custom-emoji="true" 1245 + disabled={uiState === 'loading'} 1246 + class="spoiler-text-field" 1247 + lang={language} 1248 + spellCheck="true" 1249 + autocomplete="off" 1250 + dir="auto" 1251 + onInput={() => { 1252 + updateCharCount(); 1253 + }} 1254 + /> 1255 + </TextExpander> 1256 + <button 1257 + type="button" 1258 + class="close-button plain4 small" 1259 + onClick={() => { 1260 + setSensitive(false); 1261 + textareaRef.current.focus(); 1262 + }} 1263 + > 1264 + <Icon icon="x" alt={t`Cancel`} /> 1265 + </button> 1266 + </div> 1267 + <Textarea 1268 + ref={textareaRef} 1269 + data-allow-custom-emoji="true" 1270 + placeholder={ 1271 + replyToStatus 1272 + ? t`Post your reply` 1273 + : editStatus 1274 + ? t`Edit your post` 1275 + : !!poll 1276 + ? t`Ask a question` 1277 + : t`What are you doing?` 1278 + } 1279 + required={mediaAttachments?.length === 0} 1280 + disabled={uiState === 'loading'} 1281 + lang={language} 1282 + onInput={() => { 1283 + updateCharCount(); 1284 + }} 1285 + maxCharacters={maxCharacters} 1163 1286 onTrigger={(action) => { 1164 1287 if (action?.name === 'custom-emojis') { 1165 1288 setShowEmoji2Picker({ 1166 - targetElement: spoilerTextRef, 1289 + targetElement: lastFocusedEmojiFieldRef, 1167 1290 defaultSearchTerm: action?.defaultSearchTerm || null, 1168 1291 }); 1292 + } else if (action?.name === 'mention') { 1293 + setShowMentionPicker({ 1294 + defaultSearchTerm: action?.defaultSearchTerm || null, 1295 + }); 1296 + } else if ( 1297 + action?.name === 'auto-detect-language' && 1298 + action?.languages 1299 + ) { 1300 + setAutoDetectedLanguages(action.languages); 1169 1301 } 1170 1302 }} 1171 - > 1172 - <input 1173 - ref={spoilerTextRef} 1174 - type="text" 1175 - name="spoilerText" 1176 - placeholder={t`Content warning`} 1177 - data-allow-custom-emoji="true" 1178 - disabled={uiState === 'loading'} 1179 - class="spoiler-text-field" 1180 - lang={language} 1181 - spellCheck="true" 1182 - autocomplete="off" 1183 - dir="auto" 1184 - style={{ 1185 - opacity: sensitive ? 1 : 0, 1186 - pointerEvents: sensitive ? 'auto' : 'none', 1187 - }} 1188 - onInput={() => { 1189 - updateCharCount(); 1190 - }} 1191 - /> 1192 - </TextExpander> 1193 - <label 1194 - class={`toolbar-button ${sensitive ? 'highlight' : ''}`} 1195 - title={t`Content warning or sensitive media`} 1196 - > 1197 - <input 1198 - name="sensitive" 1199 - type="checkbox" 1200 - checked={sensitive} 1201 - disabled={uiState === 'loading'} 1202 - onChange={(e) => { 1203 - const sensitive = e.target.checked; 1204 - setSensitive(sensitive); 1205 - if (sensitive) { 1206 - spoilerTextRef.current?.focus(); 1207 - } else { 1208 - textareaRef.current?.focus(); 1209 - } 1210 - }} 1211 - /> 1212 - <Icon icon={`eye-${sensitive ? 'close' : 'open'}`} /> 1213 - </label>{' '} 1214 - <label 1215 - class={`toolbar-button ${ 1216 - visibility !== 'public' && !sensitive ? 'show-field' : '' 1217 - } ${visibility !== 'public' ? 'highlight' : ''}`} 1218 - title={visibility} 1219 - > 1220 - <Icon icon={visibilityIconsMap[visibility]} alt={visibility} /> 1221 - <select 1222 - name="visibility" 1223 - value={visibility} 1224 - onChange={(e) => { 1225 - setVisibility(e.target.value); 1226 - }} 1227 - disabled={uiState === 'loading' || !!editStatus} 1228 - dir="auto" 1229 - > 1230 - <option value="public"> 1231 - <Trans>Public</Trans> 1232 - </option> 1233 - {(supports('@pleroma/local-visibility-post') || 1234 - supports('@akkoma/local-visibility-post')) && ( 1235 - <option value="local"> 1236 - <Trans>Local</Trans> 1237 - </option> 1238 - )} 1239 - <option value="unlisted"> 1240 - <Trans>Unlisted</Trans> 1241 - </option> 1242 - <option value="private"> 1243 - <Trans>Followers only</Trans> 1244 - </option> 1245 - <option value="direct"> 1246 - <Trans>Private mention</Trans> 1247 - </option> 1248 - </select> 1249 - </label>{' '} 1303 + /> 1250 1304 </div> 1251 - <Textarea 1252 - ref={textareaRef} 1253 - data-allow-custom-emoji="true" 1254 - placeholder={ 1255 - replyToStatus 1256 - ? t`Post your reply` 1257 - : editStatus 1258 - ? t`Edit your post` 1259 - : t`What are you doing?` 1260 - } 1261 - required={mediaAttachments?.length === 0} 1262 - disabled={uiState === 'loading'} 1263 - lang={language} 1264 - onInput={() => { 1265 - updateCharCount(); 1266 - }} 1267 - maxCharacters={maxCharacters} 1268 - onTrigger={(action) => { 1269 - if (action?.name === 'custom-emojis') { 1270 - setShowEmoji2Picker({ 1271 - targetElement: lastFocusedEmojiFieldRef, 1272 - defaultSearchTerm: action?.defaultSearchTerm || null, 1273 - }); 1274 - } else if (action?.name === 'mention') { 1275 - setShowMentionPicker({ 1276 - defaultSearchTerm: action?.defaultSearchTerm || null, 1277 - }); 1278 - } else if ( 1279 - action?.name === 'auto-detect-language' && 1280 - action?.languages 1281 - ) { 1282 - setAutoDetectedLanguages(action.languages); 1283 - } 1284 - }} 1285 - /> 1286 1305 {mediaAttachments?.length > 0 && ( 1287 1306 <div class="media-attachments"> 1288 1307 {mediaAttachments.map((attachment, i) => { ··· 1315 1334 })} 1316 1335 <label class="media-sensitive"> 1317 1336 <input 1318 - name="sensitive" 1337 + name="sensitiveMedia" 1319 1338 type="checkbox" 1320 - checked={sensitive} 1339 + checked={sensitiveMedia} 1321 1340 disabled={uiState === 'loading'} 1322 1341 onChange={(e) => { 1323 - const sensitive = e.target.checked; 1324 - setSensitive(sensitive); 1342 + const sensitiveMedia = e.target.checked; 1343 + setSensitiveMedia(sensitiveMedia); 1325 1344 }} 1326 1345 />{' '} 1327 1346 <span> 1328 1347 <Trans>Mark media as sensitive</Trans> 1329 1348 </span>{' '} 1330 - <Icon icon={`eye-${sensitive ? 'close' : 'open'}`} /> 1349 + <Icon icon={`eye-${sensitiveMedia ? 'close' : 'open'}`} /> 1331 1350 </label> 1332 1351 </div> 1333 1352 )} ··· 1346 1365 setPoll(newPoll); 1347 1366 } else { 1348 1367 setPoll(null); 1368 + focusLastFocusedField(); 1349 1369 } 1350 1370 }} 1351 1371 /> 1352 1372 )} 1353 1373 {scheduledAt && ( 1354 1374 <div class="toolbar scheduled-at"> 1375 + <span> 1376 + <label> 1377 + <Trans> 1378 + Posting on{' '} 1379 + <ScheduledAtField 1380 + scheduledAt={scheduledAt} 1381 + setScheduledAt={setScheduledAt} 1382 + /> 1383 + </Trans> 1384 + </label>{' '} 1385 + <small class="tag insignificant"> 1386 + {getLocalTimezoneName()} 1387 + </small> 1388 + </span> 1355 1389 <button 1356 1390 type="button" 1357 - class="plain4 small" 1391 + class="plain4 close-button small" 1358 1392 onClick={() => { 1359 1393 setScheduledAt(null); 1394 + focusLastFocusedField(); 1360 1395 }} 1361 1396 > 1362 - <Icon icon="x" /> 1397 + <Icon icon="x" alt={t`Cancel`} /> 1363 1398 </button> 1364 - <label> 1365 - <Trans> 1366 - Posting on{' '} 1367 - <ScheduledAtField 1368 - scheduledAt={scheduledAt} 1369 - setScheduledAt={setScheduledAt} 1370 - /> 1371 - </Trans> 1372 - <br /> 1373 - <small>{getLocalTimezoneName()}</small> 1374 - </label> 1375 1399 </div> 1376 1400 )} 1377 1401 <div class="toolbar compose-footer"> ··· 1398 1422 )} 1399 1423 > 1400 1424 {supportsCameraCapture && ( 1401 - <MenuItem className="compose-menu-add-media"> 1425 + <MenuItem 1426 + disabled={mediaButtonDisabled} 1427 + className="compose-menu-add-media" 1428 + > 1402 1429 <label class="compose-menu-add-media-field"> 1403 1430 <CameraCaptureInput 1404 1431 hidden 1405 1432 supportedMimeTypes={supportedImagesVideosTypes} 1406 - disabled={ 1407 - uiState === 'loading' || 1408 - mediaAttachments.length >= maxMediaAttachments || 1409 - !!poll 1410 - } 1433 + disabled={mediaButtonDisabled} 1411 1434 setMediaAttachments={setMediaAttachments} 1412 1435 /> 1413 1436 </label> 1414 1437 <Icon icon="camera" /> <span>{_(ADD_LABELS.camera)}</span> 1415 1438 </MenuItem> 1416 1439 )} 1417 - <MenuItem className="compose-menu-add-media"> 1440 + <MenuItem 1441 + disabled={mediaButtonDisabled} 1442 + className="compose-menu-add-media" 1443 + > 1418 1444 <label class="compose-menu-add-media-field"> 1419 1445 <FilePickerInput 1420 1446 hidden 1421 1447 supportedMimeTypes={supportedMimeTypes} 1422 1448 maxMediaAttachments={maxMediaAttachments} 1423 1449 mediaAttachments={mediaAttachments} 1424 - disabled={ 1425 - uiState === 'loading' || 1426 - mediaAttachments.length >= maxMediaAttachments || 1427 - !!poll 1428 - } 1450 + disabled={mediaButtonDisabled} 1429 1451 setMediaAttachments={setMediaAttachments} 1430 1452 /> 1431 1453 </label> 1432 1454 <Icon icon="media" /> <span>{_(ADD_LABELS.media)}</span> 1433 1455 </MenuItem> 1434 1456 <MenuItem 1457 + disabled={cwButtonDisabled} 1458 + onClick={onCWButtonClick} 1459 + > 1460 + <Icon icon={`eye-${sensitive ? 'close' : 'open'}`} />{' '} 1461 + <span>{_(ADD_LABELS.sensitive)}</span> 1462 + </MenuItem> 1463 + {showPollButton && ( 1464 + <MenuItem 1465 + disabled={pollButtonDisabled} 1466 + onClick={onPollButtonClick} 1467 + > 1468 + <Icon icon="poll" /> <span>{_(ADD_LABELS.poll)}</span> 1469 + </MenuItem> 1470 + )} 1471 + <MenuDivider /> 1472 + <MenuItem 1435 1473 onClick={() => { 1436 1474 setShowEmoji2Picker({ 1437 1475 targetElement: lastFocusedEmojiFieldRef, ··· 1443 1481 </MenuItem> 1444 1482 {!!states.settings.composerGIFPicker && ( 1445 1483 <MenuItem 1446 - disabled={gifPickerDisabled} 1484 + disabled={mediaButtonDisabled} 1447 1485 onClick={() => { 1448 1486 setShowGIFPicker(true); 1449 1487 }} ··· 1452 1490 <span>{_(ADD_LABELS.gif)}</span> 1453 1491 </MenuItem> 1454 1492 )} 1455 - {showPollButton && ( 1456 - <MenuItem 1457 - disabled={pollButtonDisabled} 1458 - onClick={onPollButtonClick} 1459 - > 1460 - <Icon icon="poll" /> <span>{_(ADD_LABELS.poll)}</span> 1461 - </MenuItem> 1462 - )} 1463 1493 {showScheduledAt && ( 1464 - <MenuItem 1465 - disabled={scheduledAtButtonDisabled} 1466 - onClick={onScheduledAtClick} 1467 - > 1468 - <Icon icon="schedule" />{' '} 1469 - <span>{_(ADD_LABELS.scheduledPost)}</span> 1470 - </MenuItem> 1494 + <> 1495 + <MenuDivider /> 1496 + <MenuItem 1497 + disabled={scheduledAtButtonDisabled} 1498 + onClick={onScheduledAtClick} 1499 + > 1500 + <Icon icon="schedule" />{' '} 1501 + <span>{_(ADD_LABELS.scheduledPost)}</span> 1502 + </MenuItem> 1503 + </> 1471 1504 )} 1472 1505 </Menu2> 1473 1506 )} ··· 1477 1510 <CameraCaptureInput 1478 1511 supportedMimeTypes={supportedImagesVideosTypes} 1479 1512 mediaAttachments={mediaAttachments} 1480 - disabled={ 1481 - uiState === 'loading' || 1482 - mediaAttachments.length >= maxMediaAttachments || 1483 - !!poll 1484 - } 1513 + disabled={mediaButtonDisabled} 1485 1514 setMediaAttachments={setMediaAttachments} 1486 1515 /> 1487 1516 <Icon icon="camera" alt={_(ADD_LABELS.camera)} /> ··· 1492 1521 supportedMimeTypes={supportedMimeTypes} 1493 1522 maxMediaAttachments={maxMediaAttachments} 1494 1523 mediaAttachments={mediaAttachments} 1495 - disabled={ 1496 - uiState === 'loading' || 1497 - mediaAttachments.length >= maxMediaAttachments || 1498 - !!poll 1499 - } 1524 + disabled={mediaButtonDisabled} 1500 1525 setMediaAttachments={setMediaAttachments} 1501 1526 /> 1502 1527 <Icon icon="media" alt={_(ADD_LABELS.media)} /> 1503 1528 </label> 1529 + <button 1530 + type="button" 1531 + class="toolbar-button" 1532 + disabled={cwButtonDisabled} 1533 + onClick={onCWButtonClick} 1534 + > 1535 + <Icon 1536 + icon={`eye-${sensitive ? 'close' : 'open'}`} 1537 + alt={_(ADD_LABELS.sensitive)} 1538 + /> 1539 + </button> 1540 + {showPollButton && ( 1541 + <button 1542 + type="button" 1543 + class="toolbar-button" 1544 + disabled={pollButtonDisabled} 1545 + onClick={onPollButtonClick} 1546 + > 1547 + <Icon icon="poll" alt={_(ADD_LABELS.poll)} /> 1548 + </button> 1549 + )} 1550 + <div class="toolbar-divider" /> 1504 1551 {/* <button 1505 - type="button" 1506 - class="toolbar-button" 1507 - disabled={uiState === 'loading'} 1508 - onClick={() => { 1509 - setShowMentionPicker(true); 1510 - }} 1511 - > 1512 - <Icon icon="at" /> 1513 - </button> */} 1552 + type="button" 1553 + class="toolbar-button" 1554 + disabled={uiState === 'loading'} 1555 + onClick={() => { 1556 + setShowMentionPicker(true); 1557 + }} 1558 + > 1559 + <Icon icon="at" /> 1560 + </button> */} 1514 1561 <button 1515 1562 type="button" 1516 1563 class="toolbar-button" ··· 1527 1574 <button 1528 1575 type="button" 1529 1576 class="toolbar-button gif-picker-button" 1530 - disabled={gifPickerDisabled} 1577 + disabled={mediaButtonDisabled} 1531 1578 onClick={() => { 1532 1579 setShowGIFPicker(true); 1533 1580 }} ··· 1538 1585 /> 1539 1586 </button> 1540 1587 )} 1541 - {showPollButton && ( 1588 + {showScheduledAt && ( 1542 1589 <> 1590 + <div class="toolbar-divider" /> 1543 1591 <button 1544 1592 type="button" 1545 - class="toolbar-button" 1546 - disabled={pollButtonDisabled} 1547 - onClick={onPollButtonClick} 1593 + class={`toolbar-button ${scheduledAt ? 'highlight' : ''}`} 1594 + disabled={scheduledAtButtonDisabled} 1595 + onClick={onScheduledAtClick} 1548 1596 > 1549 - <Icon icon="poll" alt={_(ADD_LABELS.poll)} /> 1597 + <Icon icon="schedule" alt={_(ADD_LABELS.scheduledPost)} /> 1550 1598 </button> 1551 1599 </> 1552 1600 )} 1553 - {showScheduledAt && ( 1554 - <button 1555 - type="button" 1556 - class={`toolbar-button ${scheduledAt ? 'highlight' : ''}`} 1557 - disabled={scheduledAtButtonDisabled} 1558 - onClick={onScheduledAtClick} 1559 - > 1560 - <Icon icon="schedule" alt={_(ADD_LABELS.scheduledPost)} /> 1561 - </button> 1562 - )} 1563 1601 </span> 1564 1602 </span> 1565 - {/* <div class="spacer" /> */} 1566 1603 {uiState === 'loading' ? ( 1567 1604 <Loader abrupt /> 1568 1605 ) : ( ··· 1573 1610 )} 1574 1611 <label 1575 1612 class={`toolbar-button ${ 1576 - language !== prevLanguage.current || 1577 - ( 1578 - autoDetectedLanguages?.length && 1579 - !autoDetectedLanguages.includes(language) 1580 - ) 1581 - ? 'highlight' 1582 - : '' 1613 + highlightLanguageField ? 'highlight' : '' 1583 1614 }`} 1584 1615 > 1585 1616 <span class="icon-text"> ··· 1623 1654 })} 1624 1655 </select> 1625 1656 </label>{' '} 1657 + <label 1658 + class={`toolbar-button ${highlightVisibilityField ? 'highlight' : ''}`} 1659 + title={_(visibilityText[visibility])} 1660 + > 1661 + {visibility === 'public' || visibility === 'direct' ? ( 1662 + <Icon 1663 + icon={visibilityIconsMap[visibility]} 1664 + alt={_(visibilityText[visibility])} 1665 + /> 1666 + ) : ( 1667 + <span class="icon-text">{_(visibilityText[visibility])}</span> 1668 + )} 1669 + <select 1670 + name="visibility" 1671 + value={visibility} 1672 + onChange={(e) => { 1673 + setVisibility(e.target.value); 1674 + }} 1675 + disabled={uiState === 'loading' || !!editStatus} 1676 + dir="auto" 1677 + > 1678 + <option value="public"> 1679 + <Trans>Public</Trans> 1680 + </option> 1681 + {(supports('@pleroma/local-visibility-post') || 1682 + supports('@akkoma/local-visibility-post')) && ( 1683 + <option value="local"> 1684 + <Trans>Local</Trans> 1685 + </option> 1686 + )} 1687 + <option value="unlisted"> 1688 + <Trans>Quiet public</Trans> 1689 + </option> 1690 + <option value="private"> 1691 + <Trans>Followers</Trans> 1692 + </option> 1693 + <option value="direct"> 1694 + <Trans>Private mention</Trans> 1695 + </option> 1696 + </select> 1697 + </label>{' '} 1626 1698 <button type="submit" disabled={uiState === 'loading'}> 1627 1699 {scheduledAt 1628 1700 ? t`Schedule` ··· 1642 1714 <Modal 1643 1715 onClose={() => { 1644 1716 setShowMentionPicker(false); 1717 + focusLastFocusedField(); 1645 1718 }} 1646 1719 > 1647 1720 <MentionModal ··· 1667 1740 <Modal 1668 1741 onClose={() => { 1669 1742 setShowEmoji2Picker(false); 1743 + focusLastFocusedField(); 1670 1744 }} 1671 1745 > 1672 1746 <CustomEmojisModal ··· 1690 1764 <Modal 1691 1765 onClose={() => { 1692 1766 setShowGIFPicker(false); 1767 + focusLastFocusedField(); 1693 1768 }} 1694 1769 > 1695 1770 <GIFPickerModal
+1 -8
src/components/status.jsx
··· 44 44 import supports from '../utils/supports'; 45 45 import useTruncated from '../utils/useTruncated'; 46 46 import visibilityIconsMap from '../utils/visibility-icons-map'; 47 + import visibilityText from '../utils/visibility-text'; 47 48 48 49 import Avatar from './avatar'; 49 50 import CustomEmoji from './custom-emoji'; ··· 82 83 return masto.v1.accounts.$select(id).fetch(); 83 84 } 84 85 const memFetchAccount = pmem(throttle(fetchAccount)); 85 - 86 - const visibilityText = { 87 - public: msg`Public`, 88 - local: msg`Local`, 89 - unlisted: msg`Unlisted`, 90 - private: msg`Followers only`, 91 - direct: msg`Private mention`, 92 - }; 93 86 94 87 const isIOS = 95 88 window.ontouchstart !== undefined &&
+1 -1
src/index.css
··· 100 100 --outline-hover-color: rgba(128, 128, 128, 0.7); 101 101 --divider-color: rgba(0, 0, 0, 0.1); 102 102 --backdrop-color: rgba(0, 0, 0, 0.1); 103 - --backdrop-solid-color: color-mix(in srgb, var(--bg-color) 90%, #000 10%); 103 + --backdrop-solid-color: var(--bg-faded-color); 104 104 --backdrop-theme-color: #e5e5e5; 105 105 --backdrop-darker-color: rgba(0, 0, 0, 0.25); 106 106 --img-bg-color: rgba(128, 128, 128, 0.2);
+207 -203
src/locales/en.po
··· 34 34 35 35 #: src/components/account-block.jsx:190 36 36 #: src/components/account-info.jsx:675 37 - #: src/components/status.jsx:519 37 + #: src/components/status.jsx:512 38 38 msgid "Group" 39 39 msgstr "" 40 40 ··· 91 91 92 92 #: src/components/account-info.jsx:407 93 93 #: src/components/account-info.jsx:740 94 + #: src/components/compose.jsx:1691 95 + #: src/pages/settings.jsx:324 96 + #: src/utils/visibility-text.jsx:7 94 97 msgid "Followers" 95 98 msgstr "Followers" 96 99 ··· 112 115 #: src/components/media-attachment.jsx:380 113 116 #: src/components/media-modal.jsx:363 114 117 #: src/components/related-actions.jsx:238 115 - #: src/components/status.jsx:1771 116 - #: src/components/status.jsx:1788 117 - #: src/components/status.jsx:1919 118 - #: src/components/status.jsx:2543 119 - #: src/components/status.jsx:2546 118 + #: src/components/status.jsx:1764 119 + #: src/components/status.jsx:1781 120 + #: src/components/status.jsx:1912 121 + #: src/components/status.jsx:2536 122 + #: src/components/status.jsx:2539 120 123 #: src/pages/account-statuses.jsx:539 121 124 #: src/pages/accounts.jsx:118 122 125 #: src/pages/hashtag.jsx:203 ··· 221 224 msgstr "" 222 225 223 226 #: src/components/account-info.jsx:943 224 - #: src/components/status.jsx:2328 227 + #: src/components/status.jsx:2321 225 228 #: src/pages/catchup.jsx:71 226 229 #: src/pages/catchup.jsx:1448 227 230 #: src/pages/catchup.jsx:2061 ··· 248 251 249 252 #: src/components/account-sheet.jsx:38 250 253 #: src/components/add-remove-lists-sheet.jsx:45 251 - #: src/components/compose.jsx:834 254 + #: src/components/compose.jsx:888 252 255 #: src/components/custom-emojis-modal.jsx:234 253 256 #: src/components/drafts.jsx:57 254 257 #: src/components/edit-profile-sheet.jsx:87 ··· 268 271 #: src/components/shortcuts-settings.jsx:230 269 272 #: src/components/shortcuts-settings.jsx:583 270 273 #: src/components/shortcuts-settings.jsx:783 271 - #: src/components/status.jsx:2742 272 - #: src/components/status.jsx:2954 274 + #: src/components/status.jsx:2735 275 + #: src/components/status.jsx:2947 273 276 #: src/components/translated-bio-sheet.jsx:21 274 277 #: src/pages/accounts.jsx:45 275 278 #: src/pages/catchup.jsx:1584 ··· 358 361 msgid "Choice {0}" 359 362 msgstr "Choice {0}" 360 363 361 - #: src/components/compose-poll.jsx:65 364 + #: src/components/compose-poll.jsx:64 362 365 #: src/components/media-attachment.jsx:300 363 366 #: src/components/shortcuts-settings.jsx:726 364 367 #: src/pages/catchup.jsx:1081 ··· 366 369 msgid "Remove" 367 370 msgstr "" 368 371 369 - #: src/components/compose-poll.jsx:93 370 - msgid "Multiple choices" 371 - msgstr "Multiple choices" 372 + #: src/components/compose-poll.jsx:80 373 + #: src/components/compose.jsx:1420 374 + #: src/components/mention-modal.jsx:220 375 + #: src/components/shortcuts-settings.jsx:715 376 + #: src/pages/list.jsx:388 377 + msgid "Add" 378 + msgstr "" 372 379 373 380 #: src/components/compose-poll.jsx:96 381 + msgid "Multiple choice" 382 + msgstr "Multiple choice" 383 + 384 + #: src/components/compose-poll.jsx:99 374 385 msgid "Duration" 375 386 msgstr "" 376 387 377 - #: src/components/compose-poll.jsx:127 388 + #: src/components/compose-poll.jsx:129 378 389 msgid "Remove poll" 379 390 msgstr "" 380 391 381 - #: src/components/compose.jsx:100 392 + #: src/components/compose.jsx:101 382 393 msgid "Take photo or video" 383 394 msgstr "Take photo or video" 384 395 385 - #: src/components/compose.jsx:101 396 + #: src/components/compose.jsx:102 386 397 msgid "Add media" 387 398 msgstr "Add media" 388 399 389 - #: src/components/compose.jsx:102 400 + #: src/components/compose.jsx:103 390 401 msgid "Add custom emoji" 391 402 msgstr "" 392 403 393 - #: src/components/compose.jsx:103 404 + #: src/components/compose.jsx:104 394 405 msgid "Add GIF" 395 406 msgstr "Add GIF" 396 407 397 - #: src/components/compose.jsx:104 408 + #: src/components/compose.jsx:105 398 409 msgid "Add poll" 399 410 msgstr "" 400 411 401 - #: src/components/compose.jsx:105 412 + #: src/components/compose.jsx:106 413 + msgid "Add content warning" 414 + msgstr "Add content warning" 415 + 416 + #: src/components/compose.jsx:107 402 417 msgid "Schedule post" 403 418 msgstr "Schedule post" 404 419 405 - #: src/components/compose.jsx:359 420 + #: src/components/compose.jsx:387 406 421 msgid "You have unsaved changes. Discard this post?" 407 422 msgstr "You have unsaved changes. Discard this post?" 408 423 409 424 #. placeholder {0}: unsupportedFiles.length 410 425 #. placeholder {1}: unsupportedFiles[0].name 411 426 #. placeholder {2}: lf.format( unsupportedFiles.map((f) => f.name), ) 412 - #: src/components/compose.jsx:597 427 + #: src/components/compose.jsx:626 413 428 msgid "{0, plural, one {File {1} is not supported.} other {Files {2} are not supported.}}" 414 429 msgstr "{0, plural, one {File {1} is not supported.} other {Files {2} are not supported.}}" 415 430 416 - #: src/components/compose.jsx:607 417 - #: src/components/compose.jsx:625 418 - #: src/components/compose.jsx:1701 431 + #: src/components/compose.jsx:636 432 + #: src/components/compose.jsx:654 433 + #: src/components/compose.jsx:1776 419 434 #: src/components/file-picker-input.jsx:38 420 435 msgid "{maxMediaAttachments, plural, one {You can only attach up to 1 file.} other {You can only attach up to # files.}}" 421 436 msgstr "" 422 437 423 - #: src/components/compose.jsx:815 438 + #: src/components/compose.jsx:869 424 439 msgid "Pop out" 425 440 msgstr "Pop out" 426 441 427 - #: src/components/compose.jsx:822 442 + #: src/components/compose.jsx:876 428 443 msgid "Minimize" 429 444 msgstr "Minimize" 430 445 431 - #: src/components/compose.jsx:858 446 + #: src/components/compose.jsx:912 432 447 msgid "Looks like you closed the parent window." 433 448 msgstr "Looks like you closed the parent window." 434 449 435 - #: src/components/compose.jsx:865 450 + #: src/components/compose.jsx:919 436 451 msgid "Looks like you already have a compose field open in the parent window and currently publishing. Please wait for it to be done and try again later." 437 452 msgstr "Looks like you already have a compose field open in the parent window and currently publishing. Please wait for it to be done and try again later." 438 453 439 - #: src/components/compose.jsx:870 454 + #: src/components/compose.jsx:924 440 455 msgid "Looks like you already have a compose field open in the parent window. Popping in this window will discard the changes you made in the parent window. Continue?" 441 456 msgstr "Looks like you already have a compose field open in the parent window. Popping in this window will discard the changes you made in the parent window. Continue?" 442 457 443 - #: src/components/compose.jsx:913 458 + #: src/components/compose.jsx:968 444 459 msgid "Pop in" 445 460 msgstr "Pop in" 446 461 447 462 #. placeholder {0}: replyToStatus.account.acct || replyToStatus.account.username 448 463 #. placeholder {1}: rtf.format(-replyToStatusMonthsAgo, 'month') 449 - #: src/components/compose.jsx:923 464 + #: src/components/compose.jsx:978 450 465 msgid "Replying to @{0}’s post (<0>{1}</0>)" 451 466 msgstr "" 452 467 453 468 #. placeholder {0}: replyToStatus.account.acct || replyToStatus.account.username 454 - #: src/components/compose.jsx:933 469 + #: src/components/compose.jsx:988 455 470 msgid "Replying to @{0}’s post" 456 471 msgstr "" 457 472 458 - #: src/components/compose.jsx:946 473 + #: src/components/compose.jsx:1001 459 474 msgid "Editing source post" 460 475 msgstr "" 461 476 462 - #: src/components/compose.jsx:999 477 + #: src/components/compose.jsx:1065 463 478 msgid "Poll must have at least 2 options" 464 479 msgstr "Poll must have at least 2 options" 465 480 466 - #: src/components/compose.jsx:1003 481 + #: src/components/compose.jsx:1069 467 482 msgid "Some poll choices are empty" 468 483 msgstr "Some poll choices are empty" 469 484 470 - #: src/components/compose.jsx:1016 485 + #: src/components/compose.jsx:1082 471 486 msgid "Some media have no descriptions. Continue?" 472 487 msgstr "Some media have no descriptions. Continue?" 473 488 474 - #: src/components/compose.jsx:1068 489 + #: src/components/compose.jsx:1134 475 490 msgid "Attachment #{i} failed" 476 491 msgstr "Attachment #{i} failed" 477 492 478 - #: src/components/compose.jsx:1176 479 - #: src/components/status.jsx:2103 493 + #: src/components/compose.jsx:1243 494 + #: src/components/status.jsx:2096 480 495 #: src/components/timeline.jsx:1015 481 496 msgid "Content warning" 482 497 msgstr "" 483 498 484 - #: src/components/compose.jsx:1195 485 - msgid "Content warning or sensitive media" 486 - msgstr "Content warning or sensitive media" 487 - 488 - #: src/components/compose.jsx:1231 489 - #: src/components/status.jsx:87 490 - #: src/pages/settings.jsx:318 491 - msgid "Public" 499 + #: src/components/compose.jsx:1264 500 + #: src/components/compose.jsx:1397 501 + #: src/components/edit-profile-sheet.jsx:323 502 + #: src/components/private-note-sheet.jsx:94 503 + msgid "Cancel" 492 504 msgstr "" 493 505 494 - #: src/components/compose.jsx:1236 495 - #: src/components/nav-menu.jsx:349 496 - #: src/components/shortcuts-settings.jsx:165 497 - #: src/components/status.jsx:88 498 - msgid "Local" 499 - msgstr "" 500 - 501 - #: src/components/compose.jsx:1240 502 - #: src/components/status.jsx:89 503 - #: src/pages/settings.jsx:321 504 - msgid "Unlisted" 505 - msgstr "" 506 - 507 - #: src/components/compose.jsx:1243 508 - #: src/components/status.jsx:90 509 - #: src/pages/settings.jsx:324 510 - msgid "Followers only" 511 - msgstr "" 512 - 513 - #: src/components/compose.jsx:1246 514 - #: src/components/status.jsx:91 515 - #: src/components/status.jsx:1983 516 - msgid "Private mention" 517 - msgstr "" 518 - 519 - #: src/components/compose.jsx:1256 506 + #: src/components/compose.jsx:1272 520 507 msgid "Post your reply" 521 508 msgstr "Post your reply" 522 509 523 - #: src/components/compose.jsx:1258 510 + #: src/components/compose.jsx:1274 524 511 msgid "Edit your post" 525 512 msgstr "Edit your post" 526 513 527 - #: src/components/compose.jsx:1259 514 + #: src/components/compose.jsx:1276 515 + msgid "Ask a question" 516 + msgstr "Ask a question" 517 + 518 + #: src/components/compose.jsx:1277 528 519 msgid "What are you doing?" 529 520 msgstr "What are you doing?" 530 521 531 - #: src/components/compose.jsx:1328 522 + #: src/components/compose.jsx:1347 532 523 msgid "Mark media as sensitive" 533 524 msgstr "" 534 525 535 - #: src/components/compose.jsx:1365 526 + #: src/components/compose.jsx:1377 536 527 msgid "Posting on <0/>" 537 528 msgstr "Posting on <0/>" 538 529 539 - #: src/components/compose.jsx:1396 540 - #: src/components/mention-modal.jsx:220 541 - #: src/components/shortcuts-settings.jsx:715 542 - #: src/pages/list.jsx:388 543 - msgid "Add" 530 + #: src/components/compose.jsx:1679 531 + #: src/pages/settings.jsx:318 532 + #: src/utils/visibility-text.jsx:4 533 + msgid "Public" 534 + msgstr "" 535 + 536 + #: src/components/compose.jsx:1684 537 + #: src/components/nav-menu.jsx:349 538 + #: src/components/shortcuts-settings.jsx:165 539 + #: src/utils/visibility-text.jsx:5 540 + msgid "Local" 541 + msgstr "" 542 + 543 + #: src/components/compose.jsx:1688 544 + #: src/pages/settings.jsx:321 545 + #: src/utils/visibility-text.jsx:6 546 + msgid "Quiet public" 547 + msgstr "Quiet public" 548 + 549 + #: src/components/compose.jsx:1694 550 + #: src/components/status.jsx:1976 551 + #: src/utils/visibility-text.jsx:8 552 + msgid "Private mention" 544 553 msgstr "" 545 554 546 - #: src/components/compose.jsx:1628 555 + #: src/components/compose.jsx:1700 547 556 msgid "Schedule" 548 557 msgstr "Schedule" 549 558 550 - #: src/components/compose.jsx:1630 559 + #: src/components/compose.jsx:1702 551 560 #: src/components/keyboard-shortcuts-help.jsx:155 552 - #: src/components/status.jsx:965 553 - #: src/components/status.jsx:1751 554 - #: src/components/status.jsx:1752 555 - #: src/components/status.jsx:2447 561 + #: src/components/status.jsx:958 562 + #: src/components/status.jsx:1744 563 + #: src/components/status.jsx:1745 564 + #: src/components/status.jsx:2440 556 565 msgid "Reply" 557 566 msgstr "" 558 567 559 - #: src/components/compose.jsx:1632 568 + #: src/components/compose.jsx:1704 560 569 msgid "Update" 561 570 msgstr "Update" 562 571 563 - #: src/components/compose.jsx:1633 572 + #: src/components/compose.jsx:1705 564 573 msgctxt "Submit button in composer" 565 574 msgid "Post" 566 575 msgstr "Post" 567 576 568 - #: src/components/compose.jsx:1713 577 + #: src/components/compose.jsx:1788 569 578 msgid "Downloading GIF…" 570 579 msgstr "Downloading GIF…" 571 580 572 - #: src/components/compose.jsx:1741 581 + #: src/components/compose.jsx:1816 573 582 msgid "Failed to download GIF" 574 583 msgstr "Failed to download GIF" 575 584 ··· 617 626 618 627 #: src/components/drafts.jsx:126 619 628 #: src/components/list-add-edit.jsx:188 620 - #: src/components/status.jsx:1368 629 + #: src/components/status.jsx:1361 621 630 #: src/pages/filters.jsx:603 622 631 #: src/pages/scheduled-posts.jsx:368 623 632 msgid "Delete…" ··· 684 693 685 694 #: src/components/edit-profile-sheet.jsx:293 686 695 msgid "Content" 687 - msgstr "" 688 - 689 - #: src/components/edit-profile-sheet.jsx:323 690 - #: src/components/private-note-sheet.jsx:94 691 - msgid "Cancel" 692 696 msgstr "" 693 697 694 698 #: src/components/edit-profile-sheet.jsx:326 ··· 914 918 msgstr "" 915 919 916 920 #: src/components/keyboard-shortcuts-help.jsx:176 917 - #: src/components/status.jsx:973 918 - #: src/components/status.jsx:2474 919 - #: src/components/status.jsx:2497 920 - #: src/components/status.jsx:2498 921 + #: src/components/status.jsx:966 922 + #: src/components/status.jsx:2467 923 + #: src/components/status.jsx:2490 924 + #: src/components/status.jsx:2491 921 925 msgid "Boost" 922 926 msgstr "" 923 927 ··· 926 930 msgstr "" 927 931 928 932 #: src/components/keyboard-shortcuts-help.jsx:184 929 - #: src/components/status.jsx:1036 930 - #: src/components/status.jsx:2522 931 - #: src/components/status.jsx:2523 933 + #: src/components/status.jsx:1029 934 + #: src/components/status.jsx:2515 935 + #: src/components/status.jsx:2516 932 936 msgid "Bookmark" 933 937 msgstr "" 934 938 ··· 1011 1015 msgstr "" 1012 1016 1013 1017 #: src/components/media-alt-modal.jsx:67 1014 - #: src/components/status.jsx:1079 1015 - #: src/components/status.jsx:1088 1018 + #: src/components/status.jsx:1072 1019 + #: src/components/status.jsx:1081 1016 1020 #: src/components/translation-block.jsx:239 1017 1021 msgid "Translate" 1018 1022 msgstr "" 1019 1023 1020 1024 #: src/components/media-alt-modal.jsx:78 1021 - #: src/components/status.jsx:1107 1025 + #: src/components/status.jsx:1100 1022 1026 msgid "Speak" 1023 1027 msgstr "" 1024 1028 ··· 1145 1149 1146 1150 #: src/components/media-post.jsx:133 1147 1151 #: src/components/status-compact.jsx:70 1148 - #: src/components/status.jsx:2880 1149 - #: src/components/status.jsx:2958 1152 + #: src/components/status.jsx:2873 1153 + #: src/components/status.jsx:2951 1150 1154 #: src/components/timeline.jsx:1004 1151 1155 #: src/pages/catchup.jsx:75 1152 1156 #: src/pages/catchup.jsx:1880 ··· 1467 1471 msgstr "" 1468 1472 1469 1473 #: src/components/notification.jsx:451 1470 - #: src/components/status.jsx:1050 1471 - #: src/components/status.jsx:1060 1474 + #: src/components/status.jsx:1043 1475 + #: src/components/status.jsx:1053 1472 1476 msgid "Boosted/Liked by…" 1473 1477 msgstr "" 1474 1478 ··· 1494 1498 msgstr "View #Wrapstodon" 1495 1499 1496 1500 #: src/components/notification.jsx:801 1497 - #: src/components/status.jsx:260 1501 + #: src/components/status.jsx:253 1498 1502 msgid "Read more →" 1499 1503 msgstr "" 1500 1504 ··· 1558 1562 msgstr "Ending" 1559 1563 1560 1564 #: src/components/post-embed-modal.jsx:201 1561 - #: src/components/status.jsx:1237 1565 + #: src/components/status.jsx:1230 1562 1566 msgid "Embed post" 1563 1567 msgstr "" 1564 1568 ··· 1577 1581 #: src/components/post-embed-modal.jsx:232 1578 1582 #: src/components/related-actions.jsx:501 1579 1583 #: src/components/shortcuts-settings.jsx:1059 1580 - #: src/components/status.jsx:1202 1584 + #: src/components/status.jsx:1195 1581 1585 msgid "Copy" 1582 1586 msgstr "" 1583 1587 ··· 1756 1760 msgstr "Show featured profiles" 1757 1761 1758 1762 #: src/components/related-actions.jsx:492 1759 - #: src/components/status.jsx:1193 1763 + #: src/components/status.jsx:1186 1760 1764 msgid "Link copied" 1761 1765 msgstr "" 1762 1766 1763 1767 #: src/components/related-actions.jsx:495 1764 - #: src/components/status.jsx:1196 1768 + #: src/components/status.jsx:1189 1765 1769 msgid "Unable to copy link" 1766 1770 msgstr "" 1767 1771 1768 1772 #: src/components/related-actions.jsx:516 1769 1773 #: src/components/shortcuts-settings.jsx:1077 1770 - #: src/components/status.jsx:1218 1774 + #: src/components/status.jsx:1211 1771 1775 msgid "Sharing doesn't seem to work." 1772 1776 msgstr "" 1773 1777 1774 1778 #: src/components/related-actions.jsx:522 1775 - #: src/components/status.jsx:1224 1779 + #: src/components/status.jsx:1217 1776 1780 msgid "Share…" 1777 1781 msgstr "" 1778 1782 ··· 2110 2114 msgstr "" 2111 2115 2112 2116 #: src/components/shortcuts-settings.jsx:379 2113 - #: src/components/status.jsx:1330 2117 + #: src/components/status.jsx:1323 2114 2118 #: src/pages/list.jsx:195 2115 2119 msgid "Edit" 2116 2120 msgstr "" ··· 2309 2313 msgid "Import/export settings from/to instance server (Very experimental)" 2310 2314 msgstr "" 2311 2315 2312 - #: src/components/status.jsx:543 2316 + #: src/components/status.jsx:536 2313 2317 msgid "<0/> <1>boosted</1>" 2314 2318 msgstr "<0/> <1>boosted</1>" 2315 2319 2316 - #: src/components/status.jsx:646 2320 + #: src/components/status.jsx:639 2317 2321 msgid "Sorry, your current logged-in instance can't interact with this post from another instance." 2318 2322 msgstr "" 2319 2323 2320 2324 #. placeholder {0}: username || acct 2321 - #: src/components/status.jsx:800 2325 + #: src/components/status.jsx:793 2322 2326 msgid "Unliked @{0}'s post" 2323 2327 msgstr "" 2324 2328 2325 2329 #. placeholder {0}: username || acct 2326 - #: src/components/status.jsx:801 2330 + #: src/components/status.jsx:794 2327 2331 msgid "Liked @{0}'s post" 2328 2332 msgstr "Liked @{0}'s post" 2329 2333 2330 2334 #. placeholder {0}: username || acct 2331 - #: src/components/status.jsx:840 2335 + #: src/components/status.jsx:833 2332 2336 msgid "Unbookmarked @{0}'s post" 2333 2337 msgstr "Unbookmarked @{0}'s post" 2334 2338 2335 2339 #. placeholder {0}: username || acct 2336 - #: src/components/status.jsx:841 2340 + #: src/components/status.jsx:834 2337 2341 msgid "Bookmarked @{0}'s post" 2338 2342 msgstr "Bookmarked @{0}'s post" 2339 2343 2340 - #: src/components/status.jsx:942 2344 + #: src/components/status.jsx:935 2341 2345 msgid "Some media have no descriptions." 2342 2346 msgstr "" 2343 2347 2344 2348 #. placeholder {0}: rtf.format(-statusMonthsAgo, 'month') 2345 - #: src/components/status.jsx:949 2349 + #: src/components/status.jsx:942 2346 2350 msgid "Old post (<0>{0}</0>)" 2347 2351 msgstr "" 2348 2352 2349 - #: src/components/status.jsx:973 2350 - #: src/components/status.jsx:1013 2351 - #: src/components/status.jsx:2474 2352 - #: src/components/status.jsx:2497 2353 + #: src/components/status.jsx:966 2354 + #: src/components/status.jsx:1006 2355 + #: src/components/status.jsx:2467 2356 + #: src/components/status.jsx:2490 2353 2357 msgid "Unboost" 2354 2358 msgstr "" 2355 2359 2356 - #: src/components/status.jsx:989 2357 - #: src/components/status.jsx:2489 2360 + #: src/components/status.jsx:982 2361 + #: src/components/status.jsx:2482 2358 2362 msgid "Quote" 2359 2363 msgstr "" 2360 2364 2361 2365 #. placeholder {0}: username || acct 2362 - #: src/components/status.jsx:1001 2363 - #: src/components/status.jsx:1467 2366 + #: src/components/status.jsx:994 2367 + #: src/components/status.jsx:1460 2364 2368 msgid "Unboosted @{0}'s post" 2365 2369 msgstr "Unboosted @{0}'s post" 2366 2370 2367 2371 #. placeholder {0}: username || acct 2368 - #: src/components/status.jsx:1002 2369 - #: src/components/status.jsx:1468 2372 + #: src/components/status.jsx:995 2373 + #: src/components/status.jsx:1461 2370 2374 msgid "Boosted @{0}'s post" 2371 2375 msgstr "Boosted @{0}'s post" 2372 2376 2373 - #: src/components/status.jsx:1014 2377 + #: src/components/status.jsx:1007 2374 2378 msgid "Boost…" 2375 2379 msgstr "" 2376 2380 2377 - #: src/components/status.jsx:1026 2378 - #: src/components/status.jsx:1761 2379 - #: src/components/status.jsx:2510 2381 + #: src/components/status.jsx:1019 2382 + #: src/components/status.jsx:1754 2383 + #: src/components/status.jsx:2503 2380 2384 msgid "Unlike" 2381 2385 msgstr "" 2382 2386 2383 - #: src/components/status.jsx:1027 2384 - #: src/components/status.jsx:1761 2385 - #: src/components/status.jsx:1762 2386 - #: src/components/status.jsx:2510 2387 - #: src/components/status.jsx:2511 2387 + #: src/components/status.jsx:1020 2388 + #: src/components/status.jsx:1754 2389 + #: src/components/status.jsx:1755 2390 + #: src/components/status.jsx:2503 2391 + #: src/components/status.jsx:2504 2388 2392 msgid "Like" 2389 2393 msgstr "" 2390 2394 2391 - #: src/components/status.jsx:1036 2392 - #: src/components/status.jsx:2522 2395 + #: src/components/status.jsx:1029 2396 + #: src/components/status.jsx:2515 2393 2397 msgid "Unbookmark" 2394 2398 msgstr "" 2395 2399 2396 - #: src/components/status.jsx:1119 2400 + #: src/components/status.jsx:1112 2397 2401 msgid "Post text copied" 2398 2402 msgstr "Post text copied" 2399 2403 2400 - #: src/components/status.jsx:1122 2404 + #: src/components/status.jsx:1115 2401 2405 msgid "Unable to copy post text" 2402 2406 msgstr "Unable to copy post text" 2403 2407 2404 - #: src/components/status.jsx:1128 2408 + #: src/components/status.jsx:1121 2405 2409 msgid "Copy post text" 2406 2410 msgstr "Copy post text" 2407 2411 2408 2412 #. placeholder {0}: username || acct 2409 - #: src/components/status.jsx:1146 2413 + #: src/components/status.jsx:1139 2410 2414 msgid "View post by <0>@{0}</0>" 2411 2415 msgstr "" 2412 2416 2413 - #: src/components/status.jsx:1167 2417 + #: src/components/status.jsx:1160 2414 2418 msgid "Show Edit History" 2415 2419 msgstr "" 2416 2420 2417 - #: src/components/status.jsx:1170 2421 + #: src/components/status.jsx:1163 2418 2422 msgid "Edited: {editedDateText}" 2419 2423 msgstr "" 2420 2424 2421 - #: src/components/status.jsx:1251 2425 + #: src/components/status.jsx:1244 2422 2426 msgid "Conversation unmuted" 2423 2427 msgstr "" 2424 2428 2425 - #: src/components/status.jsx:1251 2429 + #: src/components/status.jsx:1244 2426 2430 msgid "Conversation muted" 2427 2431 msgstr "" 2428 2432 2429 - #: src/components/status.jsx:1257 2433 + #: src/components/status.jsx:1250 2430 2434 msgid "Unable to unmute conversation" 2431 2435 msgstr "" 2432 2436 2433 - #: src/components/status.jsx:1258 2437 + #: src/components/status.jsx:1251 2434 2438 msgid "Unable to mute conversation" 2435 2439 msgstr "" 2436 2440 2437 - #: src/components/status.jsx:1267 2441 + #: src/components/status.jsx:1260 2438 2442 msgid "Unmute conversation" 2439 2443 msgstr "" 2440 2444 2441 - #: src/components/status.jsx:1274 2445 + #: src/components/status.jsx:1267 2442 2446 msgid "Mute conversation" 2443 2447 msgstr "" 2444 2448 2445 - #: src/components/status.jsx:1290 2449 + #: src/components/status.jsx:1283 2446 2450 msgid "Post unpinned from profile" 2447 2451 msgstr "" 2448 2452 2449 - #: src/components/status.jsx:1291 2453 + #: src/components/status.jsx:1284 2450 2454 msgid "Post pinned to profile" 2451 2455 msgstr "" 2452 2456 2453 - #: src/components/status.jsx:1296 2457 + #: src/components/status.jsx:1289 2454 2458 msgid "Unable to unpin post" 2455 2459 msgstr "" 2456 2460 2457 - #: src/components/status.jsx:1296 2461 + #: src/components/status.jsx:1289 2458 2462 msgid "Unable to pin post" 2459 2463 msgstr "" 2460 2464 2461 - #: src/components/status.jsx:1305 2465 + #: src/components/status.jsx:1298 2462 2466 msgid "Unpin from profile" 2463 2467 msgstr "" 2464 2468 2465 - #: src/components/status.jsx:1312 2469 + #: src/components/status.jsx:1305 2466 2470 msgid "Pin to profile" 2467 2471 msgstr "" 2468 2472 2469 - #: src/components/status.jsx:1341 2473 + #: src/components/status.jsx:1334 2470 2474 msgid "Delete this post?" 2471 2475 msgstr "" 2472 2476 2473 - #: src/components/status.jsx:1357 2477 + #: src/components/status.jsx:1350 2474 2478 msgid "Post deleted" 2475 2479 msgstr "" 2476 2480 2477 - #: src/components/status.jsx:1360 2481 + #: src/components/status.jsx:1353 2478 2482 msgid "Unable to delete post" 2479 2483 msgstr "" 2480 2484 2481 - #: src/components/status.jsx:1388 2485 + #: src/components/status.jsx:1381 2482 2486 msgid "Report post…" 2483 2487 msgstr "" 2484 2488 2485 - #: src/components/status.jsx:1762 2486 - #: src/components/status.jsx:1798 2487 - #: src/components/status.jsx:2511 2489 + #: src/components/status.jsx:1755 2490 + #: src/components/status.jsx:1791 2491 + #: src/components/status.jsx:2504 2488 2492 msgid "Liked" 2489 2493 msgstr "" 2490 2494 2491 - #: src/components/status.jsx:1795 2492 - #: src/components/status.jsx:2498 2495 + #: src/components/status.jsx:1788 2496 + #: src/components/status.jsx:2491 2493 2497 msgid "Boosted" 2494 2498 msgstr "" 2495 2499 2496 - #: src/components/status.jsx:1805 2497 - #: src/components/status.jsx:2523 2500 + #: src/components/status.jsx:1798 2501 + #: src/components/status.jsx:2516 2498 2502 msgid "Bookmarked" 2499 2503 msgstr "" 2500 2504 2501 - #: src/components/status.jsx:1809 2505 + #: src/components/status.jsx:1802 2502 2506 msgid "Pinned" 2503 2507 msgstr "" 2504 2508 2505 - #: src/components/status.jsx:1861 2506 - #: src/components/status.jsx:2336 2509 + #: src/components/status.jsx:1854 2510 + #: src/components/status.jsx:2329 2507 2511 msgid "Deleted" 2508 2512 msgstr "" 2509 2513 2510 - #: src/components/status.jsx:1902 2514 + #: src/components/status.jsx:1895 2511 2515 msgid "{repliesCount, plural, one {# reply} other {# replies}}" 2512 2516 msgstr "" 2513 2517 2514 - #: src/components/status.jsx:2066 2515 - #: src/components/status.jsx:2128 2516 - #: src/components/status.jsx:2232 2518 + #: src/components/status.jsx:2059 2519 + #: src/components/status.jsx:2121 2520 + #: src/components/status.jsx:2225 2517 2521 msgid "Show less" 2518 2522 msgstr "" 2519 2523 2520 - #: src/components/status.jsx:2066 2521 - #: src/components/status.jsx:2128 2524 + #: src/components/status.jsx:2059 2525 + #: src/components/status.jsx:2121 2522 2526 msgid "Show content" 2523 2527 msgstr "" 2524 2528 2525 2529 #. placeholder {0}: filterInfo.titlesStr 2526 2530 #. placeholder {0}: filterInfo?.titlesStr 2527 - #: src/components/status.jsx:2228 2531 + #: src/components/status.jsx:2221 2528 2532 #: src/pages/catchup.jsx:1879 2529 2533 msgid "Filtered: {0}" 2530 2534 msgstr "Filtered: {0}" 2531 2535 2532 - #: src/components/status.jsx:2232 2536 + #: src/components/status.jsx:2225 2533 2537 msgid "Show media" 2534 2538 msgstr "" 2535 2539 2536 - #: src/components/status.jsx:2371 2540 + #: src/components/status.jsx:2364 2537 2541 msgid "Edited" 2538 2542 msgstr "" 2539 2543 2540 - #: src/components/status.jsx:2448 2544 + #: src/components/status.jsx:2441 2541 2545 msgid "Comments" 2542 2546 msgstr "" 2543 2547 2544 - #: src/components/status.jsx:2633 2548 + #: src/components/status.jsx:2626 2545 2549 msgid "Post hidden by your filters" 2546 2550 msgstr "Post hidden by your filters" 2547 2551 2548 - #: src/components/status.jsx:2634 2552 + #: src/components/status.jsx:2627 2549 2553 msgid "Post pending" 2550 2554 msgstr "Post pending" 2551 2555 2552 - #: src/components/status.jsx:2635 2553 - #: src/components/status.jsx:2636 2554 - #: src/components/status.jsx:2637 2555 - #: src/components/status.jsx:2638 2556 + #: src/components/status.jsx:2628 2557 + #: src/components/status.jsx:2629 2558 + #: src/components/status.jsx:2630 2559 + #: src/components/status.jsx:2631 2556 2560 msgid "Post unavailable" 2557 2561 msgstr "Post unavailable" 2558 2562 2559 - #: src/components/status.jsx:2747 2563 + #: src/components/status.jsx:2740 2560 2564 msgid "Edit History" 2561 2565 msgstr "" 2562 2566 2563 - #: src/components/status.jsx:2751 2567 + #: src/components/status.jsx:2744 2564 2568 msgid "Failed to load history" 2565 2569 msgstr "" 2566 2570 2567 - #: src/components/status.jsx:2756 2571 + #: src/components/status.jsx:2749 2568 2572 #: src/pages/annual-report.jsx:45 2569 2573 msgid "Loading…" 2570 2574 msgstr "" 2571 2575 2572 2576 #. [Name] [Visibility icon] boosted 2573 - #: src/components/status.jsx:2888 2577 + #: src/components/status.jsx:2881 2574 2578 msgid "<0/> <1/> boosted" 2575 2579 msgstr "<0/> <1/> boosted" 2576 2580
+1 -1
src/pages/sandbox.jsx
··· 873 873 checked={toggleState.visibility === 'unlisted'} 874 874 onChange={() => updateToggles({ visibility: 'unlisted' })} 875 875 /> 876 - <span>Unlisted</span> 876 + <span>Quiet public</span> 877 877 </label> 878 878 </li> 879 879 <li>
+2 -2
src/pages/settings.jsx
··· 318 318 <Trans>Public</Trans> 319 319 </option> 320 320 <option value="unlisted"> 321 - <Trans>Unlisted</Trans> 321 + <Trans>Quiet public</Trans> 322 322 </option> 323 323 <option value="private"> 324 - <Trans>Followers only</Trans> 324 + <Trans>Followers</Trans> 325 325 </option> 326 326 </select> 327 327 </div>
+11
src/utils/visibility-text.jsx
··· 1 + import { msg } from '@lingui/core/macro'; 2 + 3 + const visibilityText = { 4 + public: msg`Public`, 5 + local: msg`Local`, 6 + unlisted: msg`Quiet public`, 7 + private: msg`Followers`, 8 + direct: msg`Private mention`, 9 + }; 10 + 11 + export default visibilityText;