From 717c21a2e37593de8221fc78dd2aa4f23bda8e43 Mon Sep 17 00:00:00 2001 From: Chewbacca Date: Tue, 6 Dec 2022 13:50:03 -0500 Subject: [PATCH 01/18] Switch X for Check --- app/soapbox/features/feed-filtering/feed-carousel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/soapbox/features/feed-filtering/feed-carousel.tsx b/app/soapbox/features/feed-filtering/feed-carousel.tsx index 621c48374..804643443 100644 --- a/app/soapbox/features/feed-filtering/feed-carousel.tsx +++ b/app/soapbox/features/feed-filtering/feed-carousel.tsx @@ -37,7 +37,7 @@ const CarouselItem = ({ avatar }: { avatar: any }) => {
{isSelected && (
- +
)} From 994d58294a6cfa0ba03905b9e15d89819002d30f Mon Sep 17 00:00:00 2001 From: Chewbacca Date: Tue, 6 Dec 2022 16:00:56 -0500 Subject: [PATCH 02/18] Support 'seen' property in the Feed Carousel --- .../__tests__/feed-carousel.test.tsx | 35 +++++++++++++++-- .../features/feed-filtering/feed-carousel.tsx | 39 ++++++++++++++++--- .../queries/__tests__/carousels.test.ts | 2 +- .../queries/__tests__/suggestions.test.ts | 2 +- app/soapbox/queries/carousels.ts | 26 ++++++++++--- 5 files changed, 88 insertions(+), 16 deletions(-) diff --git a/app/soapbox/features/feed-filtering/__tests__/feed-carousel.test.tsx b/app/soapbox/features/feed-filtering/__tests__/feed-carousel.test.tsx index 9cc88a6c3..f4c4690a9 100644 --- a/app/soapbox/features/feed-filtering/__tests__/feed-carousel.test.tsx +++ b/app/soapbox/features/feed-filtering/__tests__/feed-carousel.test.tsx @@ -61,11 +61,15 @@ describe('', () => { __stub((mock) => { mock.onGet('/api/v1/truth/carousels/avatars') .reply(200, [ - { account_id: '1', acct: 'a', account_avatar: 'https://example.com/some.jpg' }, - { account_id: '2', acct: 'b', account_avatar: 'https://example.com/some.jpg' }, - { account_id: '3', acct: 'c', account_avatar: 'https://example.com/some.jpg' }, - { account_id: '4', acct: 'd', account_avatar: 'https://example.com/some.jpg' }, + { account_id: '1', acct: 'a', account_avatar: 'https://example.com/some.jpg', seen: false }, + { account_id: '2', acct: 'b', account_avatar: 'https://example.com/some.jpg', seen: false }, + { account_id: '3', acct: 'c', account_avatar: 'https://example.com/some.jpg', seen: false }, + { account_id: '4', acct: 'd', account_avatar: 'https://example.com/some.jpg', seen: false }, ]); + + mock.onGet('/api/v1/accounts/1/statuses').reply(200, [], { + link: '; rel=\'prev\'', + }); }); }); @@ -74,6 +78,29 @@ describe('', () => { await waitFor(() => { expect(screen.queryAllByTestId('feed-carousel')).toHaveLength(1); + expect(screen.queryAllByTestId('carousel-item')).toHaveLength(4); + }); + }); + + it('should handle the "seen" state', async() => { + render(, undefined, store); + + // Unseen + await waitFor(() => { + expect(screen.queryAllByTestId('carousel-item')).toHaveLength(4); + }); + expect(screen.getAllByTestId('carousel-item-avatar')[0]).toHaveClass('ring-accent-500'); + + // Selected + await userEvent.click(screen.getAllByTestId('carousel-item-avatar')[0]); + await waitFor(() => { + expect(screen.getAllByTestId('carousel-item-avatar')[0]).toHaveClass('ring-primary-600'); + }); + + // Marked as seen, not selected + await userEvent.click(screen.getAllByTestId('carousel-item-avatar')[0]); + await waitFor(() => { + expect(screen.getAllByTestId('carousel-item-avatar')[0]).toHaveClass('ring-transparent'); }); }); }); diff --git a/app/soapbox/features/feed-filtering/feed-carousel.tsx b/app/soapbox/features/feed-filtering/feed-carousel.tsx index 804643443..b30ecba0a 100644 --- a/app/soapbox/features/feed-filtering/feed-carousel.tsx +++ b/app/soapbox/features/feed-filtering/feed-carousel.tsx @@ -4,15 +4,17 @@ import { FormattedMessage } from 'react-intl'; import { replaceHomeTimeline } from 'soapbox/actions/timelines'; import { useAppDispatch, useAppSelector, useDimensions } from 'soapbox/hooks'; -import useCarouselAvatars from 'soapbox/queries/carousels'; +import { Avatar, useCarouselAvatars, useMarkAsSeen } from 'soapbox/queries/carousels'; import { Card, HStack, Icon, Stack, Text } from '../../components/ui'; import PlaceholderAvatar from '../placeholder/components/placeholder-avatar'; -const CarouselItem = ({ avatar }: { avatar: any }) => { +const CarouselItem = ({ avatar, seen, onViewed }: { avatar: Avatar, seen: boolean, onViewed: (account_id: string) => void }) => { const dispatch = useAppDispatch(); - const selectedAccountId = useAppSelector(state => state.timelines.get('home')?.feedAccountId); + const markAsSeen = useMarkAsSeen(); + + const selectedAccountId = useAppSelector(state => state.timelines.getIn(['home', 'feedAccountId']) as string); const isSelected = avatar.account_id === selectedAccountId; const [isFetching, setLoading] = useState(false); @@ -27,12 +29,20 @@ const CarouselItem = ({ avatar }: { avatar: any }) => { if (isSelected) { dispatch(replaceHomeTimeline(null, { maxId: null }, () => setLoading(false))); } else { + onViewed(avatar.account_id); + markAsSeen.mutate(avatar.account_id); dispatch(replaceHomeTimeline(avatar.account_id, { maxId: null }, () => setLoading(false))); } }; return ( -
+
{isSelected && ( @@ -45,10 +55,12 @@ const CarouselItem = ({ avatar }: { avatar: any }) => { src={avatar.account_avatar} className={classNames({ 'w-14 h-14 min-w-[56px] rounded-full ring-2 ring-offset-4 dark:ring-offset-primary-900': true, - 'ring-transparent': !isSelected, + 'ring-transparent': !isSelected && seen, 'ring-primary-600': isSelected, + 'ring-accent-500': !seen && !isSelected, })} alt={avatar.acct} + data-testid='carousel-item-avatar' />
@@ -63,6 +75,7 @@ const FeedCarousel = () => { const [cardRef, setCardRef, { width }] = useDimensions(); + const [seenAccountIds, setSeenAccountIds] = useState([]); const [pageSize, setPageSize] = useState(0); const [currentPage, setCurrentPage] = useState(1); @@ -75,6 +88,20 @@ const FeedCarousel = () => { const handleNextPage = () => setCurrentPage((prevPage) => prevPage + 1); const handlePrevPage = () => setCurrentPage((prevPage) => prevPage - 1); + const markAsSeen = (account_id: string) => { + setSeenAccountIds((prev) => [...prev, account_id]); + }; + + useEffect(() => { + if (avatars.length > 0) { + setSeenAccountIds( + avatars + .filter((avatar) => avatar.seen) + .map((avatar) => avatar.account_id), + ); + } + }, [avatars]); + useEffect(() => { if (width) { setPageSize(Math.round(width / widthPerAvatar)); @@ -130,6 +157,8 @@ const FeedCarousel = () => { )) )} diff --git a/app/soapbox/queries/__tests__/carousels.test.ts b/app/soapbox/queries/__tests__/carousels.test.ts index 9ee5fa4c2..eb9501638 100644 --- a/app/soapbox/queries/__tests__/carousels.test.ts +++ b/app/soapbox/queries/__tests__/carousels.test.ts @@ -1,7 +1,7 @@ import { __stub } from 'soapbox/api'; import { renderHook, waitFor } from 'soapbox/jest/test-helpers'; -import useCarouselAvatars from '../carousels'; +import { useCarouselAvatars } from '../carousels'; describe('useCarouselAvatars', () => { describe('with a successful query', () => { diff --git a/app/soapbox/queries/__tests__/suggestions.test.ts b/app/soapbox/queries/__tests__/suggestions.test.ts index aa352abe9..cfd8cbb8a 100644 --- a/app/soapbox/queries/__tests__/suggestions.test.ts +++ b/app/soapbox/queries/__tests__/suggestions.test.ts @@ -3,7 +3,7 @@ import { renderHook, waitFor } from 'soapbox/jest/test-helpers'; import { useOnboardingSuggestions } from '../suggestions'; -describe('useCarouselAvatars', () => { +describe('useOnboardingSuggestions', () => { describe('with a successful query', () => { beforeEach(() => { __stub((mock) => { diff --git a/app/soapbox/queries/carousels.ts b/app/soapbox/queries/carousels.ts index 7d295183e..c908b352a 100644 --- a/app/soapbox/queries/carousels.ts +++ b/app/soapbox/queries/carousels.ts @@ -1,14 +1,19 @@ -import { useQuery } from '@tanstack/react-query'; +import { useMutation, useQuery } from '@tanstack/react-query'; import { useApi } from 'soapbox/hooks'; -type Avatar = { +export type Avatar = { account_id: string account_avatar: string - username: string + acct: string + seen: boolean } -export default function useCarouselAvatars() { +const CarouselKeys = { + avatars: ['carouselAvatars'] as const, +}; + +function useCarouselAvatars() { const api = useApi(); const getCarouselAvatars = async() => { @@ -16,8 +21,9 @@ export default function useCarouselAvatars() { return data; }; - const result = useQuery(['carouselAvatars'], getCarouselAvatars, { + const result = useQuery(CarouselKeys.avatars, getCarouselAvatars, { placeholderData: [], + keepPreviousData: true, }); const avatars = result.data; @@ -27,3 +33,13 @@ export default function useCarouselAvatars() { data: avatars || [], }; } + +function useMarkAsSeen() { + const api = useApi(); + + return useMutation((account_id: string) => api.post('/api/v1/truth/carousels/avatars/seen', { + account_id, + })); +} + +export { useCarouselAvatars, useMarkAsSeen }; \ No newline at end of file From 6c00d4e0e18817df5ae95b10327e0116b575251c Mon Sep 17 00:00:00 2001 From: tassoman Date: Sat, 10 Dec 2022 18:55:04 +0100 Subject: [PATCH 03/18] file fields inverted position --- app/soapbox/features/edit-profile/index.tsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/app/soapbox/features/edit-profile/index.tsx b/app/soapbox/features/edit-profile/index.tsx index 97fdd7e50..3f0cdcf27 100644 --- a/app/soapbox/features/edit-profile/index.tsx +++ b/app/soapbox/features/edit-profile/index.tsx @@ -373,19 +373,18 @@ const EditProfile: React.FC = () => {
- } - hintText={} - > - - - } hintText={} > + } + hintText={} + > + +
From f0a057d68597f53b0f19a527a631f78c9730ed20 Mon Sep 17 00:00:00 2001 From: Chewbacca Date: Tue, 13 Dec 2022 11:05:11 -0500 Subject: [PATCH 04/18] Reduce size of timestamp --- app/soapbox/features/chats/components/chat-message-list.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/soapbox/features/chats/components/chat-message-list.tsx b/app/soapbox/features/chats/components/chat-message-list.tsx index 983a0d6f2..4b5098a7d 100644 --- a/app/soapbox/features/chats/components/chat-message-list.tsx +++ b/app/soapbox/features/chats/components/chat-message-list.tsx @@ -208,7 +208,7 @@ const ChatMessageList: React.FC = ({ chat }) => { return emojify(formatted, emojiMap.toJS()); }; - const renderDivider = (key: React.Key, text: string) => ; + const renderDivider = (key: React.Key, text: string) => ; const handleCopyText = (chatMessage: ChatMessageEntity) => { if (navigator.clipboard) { From 8aa171fede60a5f97462529918b2115fdcebb9bd Mon Sep 17 00:00:00 2001 From: Chewbacca Date: Tue, 13 Dec 2022 13:35:05 -0500 Subject: [PATCH 05/18] Re-order chat-list after sending messaging --- app/soapbox/queries/chats.ts | 2 ++ app/soapbox/utils/chats.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/soapbox/queries/chats.ts b/app/soapbox/queries/chats.ts index 9a119f5f1..4c377abb7 100644 --- a/app/soapbox/queries/chats.ts +++ b/app/soapbox/queries/chats.ts @@ -8,6 +8,7 @@ import { ChatWidgetScreens, useChatContext } from 'soapbox/contexts/chat-context import { useStatContext } from 'soapbox/contexts/stat-context'; import { useApi, useAppDispatch, useAppSelector, useFeatures, useOwnAccount } from 'soapbox/hooks'; import { normalizeChatMessage } from 'soapbox/normalizers'; +import { reOrderChatListItems } from 'soapbox/utils/chats'; import { flattenPages, PaginatedResult, updatePageItem } from 'soapbox/utils/queries'; import { queryClient } from './client'; @@ -280,6 +281,7 @@ const useChatActions = (chatId: string) => { onSuccess: (response, variables) => { const nextChat = { ...chat, last_message: response.data }; updatePageItem(ChatKeys.chatSearch(), nextChat, (o, n) => o.id === n.id); + reOrderChatListItems(); queryClient.invalidateQueries(ChatKeys.chatMessages(variables.chatId)); }, diff --git a/app/soapbox/utils/chats.ts b/app/soapbox/utils/chats.ts index 53676c898..e740e6b8e 100644 --- a/app/soapbox/utils/chats.ts +++ b/app/soapbox/utils/chats.ts @@ -81,4 +81,4 @@ const getUnreadChatsCount = (): number => { return sumBy(chats, chat => chat.unread); }; -export { updateChatListItem, getUnreadChatsCount }; \ No newline at end of file +export { updateChatListItem, getUnreadChatsCount, reOrderChatListItems }; \ No newline at end of file From b3a0f785d829c851b12266077ba0108f66645073 Mon Sep 17 00:00:00 2001 From: Chewbacca Date: Tue, 13 Dec 2022 14:42:18 -0500 Subject: [PATCH 06/18] Fix sorting of Chat List --- app/soapbox/utils/chats.ts | 7 +++++-- app/soapbox/utils/queries.ts | 23 +++++++++++++++++++---- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/app/soapbox/utils/chats.ts b/app/soapbox/utils/chats.ts index e740e6b8e..b7f0eb4ec 100644 --- a/app/soapbox/utils/chats.ts +++ b/app/soapbox/utils/chats.ts @@ -26,8 +26,11 @@ const updateChatInChatSearchQuery = (newChat: ChatPayload) => { */ const reOrderChatListItems = () => { sortQueryData(ChatKeys.chatSearch(), (chatA, chatB) => { - return compareDate(chatA.last_message?.created_at as string, chatB.last_message?.created_at as string); - }); + return compareDate( + chatA.last_message?.created_at as string, + chatB.last_message?.created_at as string, + ); + }, 'default'); }; /** diff --git a/app/soapbox/utils/queries.ts b/app/soapbox/utils/queries.ts index 263d2407b..e9d44210c 100644 --- a/app/soapbox/utils/queries.ts +++ b/app/soapbox/utils/queries.ts @@ -25,10 +25,21 @@ const deduplicateById = (entities: T[]): T[] => { return Array.from(map.values()); }; +export type SortOrder = 'reverse' | 'default' + /** Flatten paginated results into a single array. */ -const flattenPages = (queryData: InfiniteData> | undefined) => { +const flattenPages = ( + queryData: InfiniteData> | undefined, + order: SortOrder = 'reverse', +) => { const data = queryData?.pages.reduce( - (prev: T[], curr) => [...prev, ...curr.result], + (prev: T[], curr) => { + if (order === 'reverse') { + return [...curr.result, ...prev]; + } else { + return [...prev, ...curr.result]; + } + }, [], ); @@ -90,11 +101,15 @@ const paginateQueryData = (array: T[] | undefined) => { }, []); }; -const sortQueryData = (queryKey: QueryKey, comparator: (a: T, b: T) => number) => { +const sortQueryData = ( + queryKey: QueryKey, + comparator: (a: T, b: T) => number, + order: SortOrder = 'reverse', +) => { queryClient.setQueryData>>(queryKey, (prevResult) => { if (prevResult) { const nextResult = { ...prevResult }; - const flattenedQueryData = flattenPages(nextResult); + const flattenedQueryData = flattenPages(nextResult, order); const sortedQueryData = flattenedQueryData?.sort(comparator); const paginatedPages = paginateQueryData(sortedQueryData); const newPages = paginatedPages.map((page: T, idx: number) => ({ From 5de1c3a61d7909fd7bd4f3bb2de8df4a5750d56d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 13 Dec 2022 15:17:58 -0600 Subject: [PATCH 07/18] Revert flattenPages order changes --- app/soapbox/utils/chats.ts | 2 +- app/soapbox/utils/queries.ts | 23 ++++------------------- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/app/soapbox/utils/chats.ts b/app/soapbox/utils/chats.ts index b7f0eb4ec..71a416562 100644 --- a/app/soapbox/utils/chats.ts +++ b/app/soapbox/utils/chats.ts @@ -30,7 +30,7 @@ const reOrderChatListItems = () => { chatA.last_message?.created_at as string, chatB.last_message?.created_at as string, ); - }, 'default'); + }); }; /** diff --git a/app/soapbox/utils/queries.ts b/app/soapbox/utils/queries.ts index e9d44210c..263d2407b 100644 --- a/app/soapbox/utils/queries.ts +++ b/app/soapbox/utils/queries.ts @@ -25,21 +25,10 @@ const deduplicateById = (entities: T[]): T[] => { return Array.from(map.values()); }; -export type SortOrder = 'reverse' | 'default' - /** Flatten paginated results into a single array. */ -const flattenPages = ( - queryData: InfiniteData> | undefined, - order: SortOrder = 'reverse', -) => { +const flattenPages = (queryData: InfiniteData> | undefined) => { const data = queryData?.pages.reduce( - (prev: T[], curr) => { - if (order === 'reverse') { - return [...curr.result, ...prev]; - } else { - return [...prev, ...curr.result]; - } - }, + (prev: T[], curr) => [...prev, ...curr.result], [], ); @@ -101,15 +90,11 @@ const paginateQueryData = (array: T[] | undefined) => { }, []); }; -const sortQueryData = ( - queryKey: QueryKey, - comparator: (a: T, b: T) => number, - order: SortOrder = 'reverse', -) => { +const sortQueryData = (queryKey: QueryKey, comparator: (a: T, b: T) => number) => { queryClient.setQueryData>>(queryKey, (prevResult) => { if (prevResult) { const nextResult = { ...prevResult }; - const flattenedQueryData = flattenPages(nextResult, order); + const flattenedQueryData = flattenPages(nextResult); const sortedQueryData = flattenedQueryData?.sort(comparator); const paginatedPages = paginateQueryData(sortedQueryData); const newPages = paginatedPages.map((page: T, idx: number) => ({ From 2adf8f18861e213694be0c42c251461b5c18ef88 Mon Sep 17 00:00:00 2001 From: Tassoman Date: Wed, 14 Dec 2022 03:44:40 +0000 Subject: [PATCH 08/18] ita mobile patch --- app/soapbox/locales/it.json | 56 ++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/app/soapbox/locales/it.json b/app/soapbox/locales/it.json index 0f107119e..d96db99ab 100644 --- a/app/soapbox/locales/it.json +++ b/app/soapbox/locales/it.json @@ -13,7 +13,7 @@ "account.deactivated": "Disattivato", "account.direct": "Scrivi direttamente a @{name}", "account.domain_blocked": "Istanza nascosta", - "account.edit_profile": "Modifica profilo", + "account.edit_profile": "Modifica", "account.endorse": "Promuovi sul tuo profilo", "account.endorse.success": "Stai promuovendo @{acct} dal tuo profilo", "account.familiar_followers": "Seguito da {accounts}", @@ -31,7 +31,7 @@ "account.link_verified_on": "Link verificato il {date}", "account.locked_info": "Il livello di riservatezza è «chiuso». L'autore esamina manualmente ogni richiesta di follow.", "account.login": "Accedi", - "account.media": "Media caricati", + "account.media": "Media", "account.member_since": "Insieme a noi da {date}", "account.mention": "Menziona questo profilo", "account.mute": "Silenzia @{name}", @@ -146,7 +146,7 @@ "alert.unexpected.links.status": "Stato", "alert.unexpected.links.support": "Assistenza", "alert.unexpected.message": "Si è verificato un errore.", - "alert.unexpected.return_home": "Torna alla pagina iniziale", + "alert.unexpected.return_home": "Torna alla Home", "alert.unexpected.title": "Oops!", "aliases.account.add": "Crea un alias", "aliases.account_label": "Vecchio indirizzo:", @@ -252,7 +252,7 @@ "column.follow_requests": "Richieste di amicizia", "column.followers": "Followers", "column.following": "Following", - "column.home": "Pagina iniziale", + "column.home": "Home", "column.import_data": "Importazione dati", "column.info": "Informazioni server", "column.lists": "Liste", @@ -277,8 +277,8 @@ "column.soapbox_config": "Soapbox config", "column.test": "Test timeline", "column_back_button.label": "Indietro", - "column_forbidden.body": "You do not have permission to access this page.", - "column_forbidden.title": "Forbidden", + "column_forbidden.body": "Non hai il permesso di accedere a questa pagina", + "column_forbidden.title": "Accesso negato", "common.cancel": "Annulla", "common.error": "Something isn't right. Try reloading the page.", "compare_history_modal.header": "Edit history", @@ -376,7 +376,7 @@ "confirmations.unfollow.message": "Confermi che non vuoi più seguire {name}?", "crypto_donate.explanation_box.message": "{siteTitle} accetta donazioni in cripto valuta. Puoi spedire la tua donazione ad uno di questi indirizzi. Grazie per la solidarietà", "crypto_donate.explanation_box.title": "Spedire donazioni in cripto valuta", - "crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}", + "crypto_donate_panel.actions.view": "Guarda {count} wallet", "crypto_donate_panel.heading": "Donazioni cripto", "crypto_donate_panel.intro.message": "{siteTitle} accetta donazioni in cripto valuta. Grazie per la tua solidarietà!", "datepicker.day": "Giorno", @@ -411,13 +411,13 @@ "edit_email.header": "Change Email", "edit_email.placeholder": "me@example.com", "edit_federation.followers_only": "Pubblica soltanto alle persone Follower", - "edit_federation.force_nsfw": "Force attachments to be marked sensitive", - "edit_federation.media_removal": "Strip media", - "edit_federation.reject": "Reject all activities", + "edit_federation.force_nsfw": "Obbliga la protezione degli allegati (NSFW)", + "edit_federation.media_removal": "Rimuovi i media", + "edit_federation.reject": "Rifiuta tutte le attività", "edit_federation.save": "Salva", - "edit_federation.success": "{host} federation was updated", + "edit_federation.success": "Modalità di federazione di {host}, aggiornata", "edit_federation.unlisted": "Forza le pubblicazioni non in elenco", - "edit_password.header": "Change Password", + "edit_password.header": "Modifica la password", "edit_profile.error": "Impossibile salvare le modifiche", "edit_profile.fields.accepts_email_list_label": "Autorizzo gli amministratori al trattamento dei dati per l'invio di comunicazioni ", "edit_profile.fields.avatar_label": "Emblema o immagine", @@ -569,7 +569,7 @@ "hashtag.column_header.tag_mode.all": "e {additional}", "hashtag.column_header.tag_mode.any": "o {additional}", "hashtag.column_header.tag_mode.none": "senza {additional}", - "header.home.label": "Pagina iniziale", + "header.home.label": "Home", "header.login.forgot_password": "Password dimenticata?", "header.login.label": "Accedi", "header.login.password.label": "Password", @@ -609,14 +609,14 @@ "keyboard_shortcuts.favourite": "per segnare come preferita", "keyboard_shortcuts.favourites": "per aprire l'elenco di pubblicazioni preferite", "keyboard_shortcuts.heading": "Tasti di scelta rapida", - "keyboard_shortcuts.home": "per aprire la pagina iniziale", + "keyboard_shortcuts.home": "per aprire la Home", "keyboard_shortcuts.hotkey": "Tasto di scelta rapida", "keyboard_shortcuts.legend": "per mostrare questa spiegazione", "keyboard_shortcuts.mention": "per menzionare l'autore", "keyboard_shortcuts.muted": "per aprire l'elenco delle persone silenziate", "keyboard_shortcuts.my_profile": "per aprire il tuo profilo", "keyboard_shortcuts.notifications": "per aprire la colonna delle notifiche", - "keyboard_shortcuts.open_media": "to open media", + "keyboard_shortcuts.open_media": "per aprire i media", "keyboard_shortcuts.pinned": "per aprire l'elenco pubblicazioni fissate in cima", "keyboard_shortcuts.profile": "per aprire il profilo dell'autore", "keyboard_shortcuts.react": "per reagire", @@ -624,7 +624,7 @@ "keyboard_shortcuts.requests": "per aprire l'elenco delle richieste di seguirti", "keyboard_shortcuts.search": "per spostare il focus sulla ricerca", "keyboard_shortcuts.toggle_hidden": "per mostrare/nascondere il testo dei CW", - "keyboard_shortcuts.toggle_sensitivity": "mostrare/nascondere media", + "keyboard_shortcuts.toggle_sensitivity": "mostrare/nascondere i media", "keyboard_shortcuts.toot": "per iniziare a scrivere un toot completamente nuovo", "keyboard_shortcuts.unfocus": "per uscire dall'area di composizione o dalla ricerca", "keyboard_shortcuts.up": "per spostarsi in alto nella lista", @@ -663,7 +663,7 @@ "login_external.errors.network_fail": "Connection failed. Is a browser extension blocking it?", "login_form.header": "Accedi", "media_panel.empty_message": "Non ha caricato niente", - "media_panel.title": "Media caricati", + "media_panel.title": "Media", "mfa.confirm.success_message": "Autenticazione a due fattori, attivata!", "mfa.disable.success_message": "Autenticazione a due fattori, disattivata!", "mfa.disabled": "Disattivo", @@ -713,7 +713,7 @@ "navigation.dashboard": "Cruscotto", "navigation.developers": "Sviluppatori", "navigation.direct_messages": "Messaggi diretti", - "navigation.home": "Pagina iniziale", + "navigation.home": "Home", "navigation.notifications": "Notifiche", "navigation.search": "Cerca", "navigation_bar.account_aliases": "Account aliases", @@ -760,7 +760,7 @@ "notifications.filter.polls": "Risultati del sondaggio", "notifications.filter.statuses": "Updates from people you follow", "notifications.group": "{count} notifiche", - "notifications.queue_label": "Click to see {count} new {count, plural, one {notification} other {notifications}}", + "notifications.queue_label": "Ci sono {count} {count, plural, one {notifica} other {notifiche}}", "oauth_consumer.tooltip": "Sign in with {provider}", "oauth_consumers.title": "Other ways to sign in", "onboarding.avatar.subtitle": "Scegline una accattivante, o divertente!", @@ -826,14 +826,14 @@ "preferences.fields.expand_spoilers_label": "Espandi automaticamente le pubblicazioni segnate «con avvertimento» (CW)", "preferences.fields.language_label": "Lingua", "preferences.fields.media_display_label": "Visualizzazione dei media", - "preferences.fields.missing_description_modal_label": "Show confirmation dialog before sending a post without media descriptions", + "preferences.fields.missing_description_modal_label": "Richiedi conferma per pubblicare allegati senza descrizione", "preferences.fields.privacy_label": "Livello di privacy predefinito", "preferences.fields.reduce_motion_label": "Reduce motion in animations", - "preferences.fields.system_font_label": "Use system's default font", + "preferences.fields.system_font_label": "Sfrutta i caratteri del sistema operativo", "preferences.fields.theme": "Tema grafico", - "preferences.fields.underline_links_label": "Always underline links in posts", - "preferences.fields.unfollow_modal_label": "Show confirmation dialog before unfollowing someone", - "preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.", + "preferences.fields.underline_links_label": "Link sempre sottolineati", + "preferences.fields.unfollow_modal_label": "Richiedi conferma per smettere di seguire", + "preferences.hints.demetricator": "Diminuisci l'ansia, nascondendo tutti i conteggi", "preferences.hints.feed": "Nella timeline personale", "preferences.notifications.advanced": "Show all notification categories", "preferences.options.content_type_markdown": "Markdown", @@ -850,13 +850,13 @@ "privacy.public.short": "Pubblico", "privacy.unlisted.long": "Pubblico ma non visibile nella timeline pubblica", "privacy.unlisted.short": "Non elencato", - "profile_dropdown.add_account": "Cambia profilo (esistente)", + "profile_dropdown.add_account": "Aggiungi profilo (esistente)", "profile_dropdown.logout": "Disconnetti @{acct}", - "profile_dropdown.switch_account": "Switch accounts", + "profile_dropdown.switch_account": "Cambia profilo", "profile_dropdown.theme": "Tema", "profile_fields_panel.title": "Altre informazioni", "reactions.all": "Tutte", - "regeneration_indicator.label": "Attendere prego …", + "regeneration_indicator.label": "Attendere prego…", "regeneration_indicator.sublabel": "Stiamo preparando il tuo home feed!", "register_invite.lead": "Completa questo modulo per creare il tuo profilo ed essere dei nostri!", "register_invite.title": "Hai ricevuto un invito su {siteTitle}, iscriviti!", @@ -1142,7 +1142,7 @@ "tabs_bar.chats": "Chat", "tabs_bar.dashboard": "Cruscotto", "tabs_bar.fediverse": "Timeline Federata", - "tabs_bar.home": "Pagina iniziale", + "tabs_bar.home": "Home", "tabs_bar.local": "Timeline Locale", "tabs_bar.more": "Altro", "tabs_bar.notifications": "Notifiche", From 4204b48a40fa98aa61f2245761caedbc4955df86 Mon Sep 17 00:00:00 2001 From: Chewbacca Date: Wed, 14 Dec 2022 08:42:14 -0500 Subject: [PATCH 09/18] Remove mocks --- app/soapbox/queries/__tests__/chats.test.ts | 2 - app/soapbox/utils/__mocks__/queries.ts | 55 --------------------- 2 files changed, 57 deletions(-) delete mode 100644 app/soapbox/utils/__mocks__/queries.ts diff --git a/app/soapbox/queries/__tests__/chats.test.ts b/app/soapbox/queries/__tests__/chats.test.ts index 7640fd511..3d72b9c0f 100644 --- a/app/soapbox/queries/__tests__/chats.test.ts +++ b/app/soapbox/queries/__tests__/chats.test.ts @@ -11,8 +11,6 @@ import { flattenPages } from 'soapbox/utils/queries'; import { IAccount } from '../accounts'; import { ChatKeys, IChat, IChatMessage, isLastMessage, useChat, useChatActions, useChatMessages, useChats } from '../chats'; -jest.mock('soapbox/utils/queries'); - const chat: IChat = { accepted: true, account: { diff --git a/app/soapbox/utils/__mocks__/queries.ts b/app/soapbox/utils/__mocks__/queries.ts deleted file mode 100644 index efc7447a3..000000000 --- a/app/soapbox/utils/__mocks__/queries.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { InfiniteData, QueryKey, UseInfiniteQueryResult } from '@tanstack/react-query'; - -import { queryClient } from 'soapbox/jest/test-helpers'; - -import { PaginatedResult } from '../queries'; - -const flattenPages = (queryData: UseInfiniteQueryResult>['data']) => { - return queryData?.pages.reduce( - (prev: T[], curr) => [...curr.result, ...prev], - [], - ); -}; - -const updatePageItem = (queryKey: QueryKey, newItem: T, isItem: (item: T, newItem: T) => boolean) => { - queryClient.setQueriesData>>(queryKey, (data) => { - if (data) { - const pages = data.pages.map(page => { - const result = page.result.map(item => isItem(item, newItem) ? newItem : item); - return { ...page, result }; - }); - return { ...data, pages }; - } - }); -}; - -/** Insert the new item at the beginning of the first page. */ -const appendPageItem = (queryKey: QueryKey, newItem: T) => { - queryClient.setQueryData>>(queryKey, (data) => { - if (data) { - const pages = [...data.pages]; - pages[0] = { ...pages[0], result: [...pages[0].result, newItem] }; - return { ...data, pages }; - } - }); -}; - -/** Remove an item inside if found. */ -const removePageItem = (queryKey: QueryKey, itemToRemove: T, isItem: (item: T, newItem: T) => boolean) => { - queryClient.setQueriesData>>(queryKey, (data) => { - if (data) { - const pages = data.pages.map(page => { - const result = page.result.filter(item => !isItem(item, itemToRemove)); - return { ...page, result }; - }); - return { ...data, pages }; - } - }); -}; - -export { - flattenPages, - updatePageItem, - appendPageItem, - removePageItem, -}; \ No newline at end of file From 463504346ca1f702cfa43830f4e24eca5e216d22 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 14 Dec 2022 10:12:24 -0600 Subject: [PATCH 10/18] Add Inter 600, fix ChatPaneHeader font weight --- .../chats/components/chat-widget/chat-pane-header.tsx | 8 +++----- app/styles/application.scss | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/soapbox/features/chats/components/chat-widget/chat-pane-header.tsx b/app/soapbox/features/chats/components/chat-widget/chat-pane-header.tsx index 5b2e17299..20967d171 100644 --- a/app/soapbox/features/chats/components/chat-widget/chat-pane-header.tsx +++ b/app/soapbox/features/chats/components/chat-widget/chat-pane-header.tsx @@ -37,11 +37,9 @@ const ChatPaneHeader = (props: IChatPaneHeader) => { data-testid='title' {...buttonProps} > - {typeof title === 'string' ? ( - - {title} - - ) : (title)} + + {title} + {(typeof unreadCount !== 'undefined' && unreadCount > 0) && ( diff --git a/app/styles/application.scss b/app/styles/application.scss index 2582b0b63..ff2bf20de 100644 --- a/app/styles/application.scss +++ b/app/styles/application.scss @@ -4,6 +4,7 @@ @import '~@fontsource/inter/300.css'; @import '~@fontsource/inter/400.css'; @import '~@fontsource/inter/500.css'; +@import '~@fontsource/inter/600.css'; @import '~@fontsource/inter/700.css'; @import '~@fontsource/inter/900.css'; From eef7fdb78e068b22dc05c28c252fc5ade3e47e37 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 14 Dec 2022 11:01:09 -0600 Subject: [PATCH 11/18] Markup: use whitespace-pre-wrap (fix multiline bios on Mastodon) --- app/soapbox/components/markup.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/soapbox/components/markup.css b/app/soapbox/components/markup.css index 3e6bf1c3d..f451e3ba1 100644 --- a/app/soapbox/components/markup.css +++ b/app/soapbox/components/markup.css @@ -1,3 +1,7 @@ +[data-markup] { + @apply whitespace-pre-wrap; +} + [data-markup] p { @apply mb-4 whitespace-pre-wrap; } From c54b50c56271ccb4f1eb4039cee69ffc7a43e125 Mon Sep 17 00:00:00 2001 From: Chewbacca Date: Wed, 14 Dec 2022 12:26:44 -0500 Subject: [PATCH 12/18] Add spacing above textarea --- app/soapbox/pages/home_page.tsx | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/app/soapbox/pages/home_page.tsx b/app/soapbox/pages/home_page.tsx index 2d73673d7..9e5e1cb5d 100644 --- a/app/soapbox/pages/home_page.tsx +++ b/app/soapbox/pages/home_page.tsx @@ -16,8 +16,7 @@ import { } from 'soapbox/features/ui/util/async-components'; import { useAppSelector, useOwnAccount, useFeatures, useSoapboxConfig } from 'soapbox/hooks'; -import Avatar from '../components/avatar'; -import { Card, CardBody, Layout } from '../components/ui'; +import { Avatar, Card, CardBody, Layout } from '../components/ui'; import ComposeForm from '../features/compose/components/compose-form'; import BundleContainer from '../features/ui/containers/bundle_container'; // import GroupSidebarPanel from '../features/groups/sidebar_panel'; @@ -35,6 +34,7 @@ const HomePage: React.FC = ({ children }) => { const cryptoLimit = soapboxConfig.cryptoDonatePanel.get('limit', 0); const acct = account ? account.acct : ''; + const avatar = account ? account.avatar : ''; return ( <> @@ -44,15 +44,17 @@ const HomePage: React.FC = ({ children }) => {
- + - +
+ +
From 15b9e0e355556b4825e6c7454e4aea50aa2cc23c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 14 Dec 2022 12:04:59 -0600 Subject: [PATCH 13/18] HomePage: shift the ComposeForm with translate-y instead of pt --- app/soapbox/pages/home-page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/soapbox/pages/home-page.tsx b/app/soapbox/pages/home-page.tsx index 6bbd1bf03..94dbc83b7 100644 --- a/app/soapbox/pages/home-page.tsx +++ b/app/soapbox/pages/home-page.tsx @@ -46,7 +46,7 @@ const HomePage: React.FC = ({ children }) => { -
+
Date: Wed, 14 Dec 2022 12:54:42 -0600 Subject: [PATCH 14/18] features.feedUserFiltering --> features.carousel --- .../feed-filtering/__tests__/feed-carousel.test.tsx | 4 ++-- app/soapbox/pages/home-page.tsx | 2 +- app/soapbox/utils/features.ts | 10 +++++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/soapbox/features/feed-filtering/__tests__/feed-carousel.test.tsx b/app/soapbox/features/feed-filtering/__tests__/feed-carousel.test.tsx index f4c4690a9..514e23c4c 100644 --- a/app/soapbox/features/feed-filtering/__tests__/feed-carousel.test.tsx +++ b/app/soapbox/features/feed-filtering/__tests__/feed-carousel.test.tsx @@ -21,7 +21,7 @@ jest.mock('../../../hooks/useDimensions', () => ({ describe('', () => { let store: any; - describe('with "feedUserFiltering" disabled', () => { + describe('with "carousel" disabled', () => { beforeEach(() => { store = { instance: { @@ -42,7 +42,7 @@ describe('', () => { }); }); - describe('with "feedUserFiltering" enabled', () => { + describe('with "carousel" enabled', () => { beforeEach(() => { store = { instance: { diff --git a/app/soapbox/pages/home-page.tsx b/app/soapbox/pages/home-page.tsx index 94dbc83b7..c65d9b86c 100644 --- a/app/soapbox/pages/home-page.tsx +++ b/app/soapbox/pages/home-page.tsx @@ -59,7 +59,7 @@ const HomePage: React.FC = ({ children }) => { )} - {features.feedUserFiltering && } + {features.carousel && } {children} diff --git a/app/soapbox/utils/features.ts b/app/soapbox/utils/features.ts index f57458f98..9fc226b4e 100644 --- a/app/soapbox/utils/features.ts +++ b/app/soapbox/utils/features.ts @@ -205,6 +205,13 @@ const getInstanceFeatures = (instance: Instance) => { v.software === PLEROMA, ]), + /** + * Whether to show the Feed Carousel for suggested Statuses. + * @see GET /api/v1/truth/carousels/avatars + * @see GET /api/v1/truth/carousels/suggestions + */ + carousel: v.software === TRUTHSOCIAL, + /** * Ability to accept a chat. * POST /api/v1/pleroma/chats/:id/accept @@ -371,9 +378,6 @@ const getInstanceFeatures = (instance: Instance) => { /** Whether the instance federates. */ federating: federation.get('enabled', true) === true, // Assume true unless explicitly false - /** Whether or not to show the Feed Carousel for suggested Statuses */ - feedUserFiltering: v.software === TRUTHSOCIAL, - /** * Can edit and manage timeline filters (aka "muted words"). * @see {@link https://docs.joinmastodon.org/methods/accounts/filters/} From 8f3c0a1071d2ab13b2a40b496365278cf9990140 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 14 Dec 2022 13:06:41 -0600 Subject: [PATCH 15/18] Carousel: make `seen` optional, only hit the API if features.carouselSeen is supported --- app/soapbox/queries/carousels.ts | 15 ++++++++++----- app/soapbox/utils/features.ts | 6 ++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/app/soapbox/queries/carousels.ts b/app/soapbox/queries/carousels.ts index c908b352a..d6e05798a 100644 --- a/app/soapbox/queries/carousels.ts +++ b/app/soapbox/queries/carousels.ts @@ -1,12 +1,12 @@ import { useMutation, useQuery } from '@tanstack/react-query'; -import { useApi } from 'soapbox/hooks'; +import { useApi, useFeatures } from 'soapbox/hooks'; export type Avatar = { account_id: string account_avatar: string acct: string - seen: boolean + seen?: boolean } const CarouselKeys = { @@ -36,10 +36,15 @@ function useCarouselAvatars() { function useMarkAsSeen() { const api = useApi(); + const features = useFeatures(); - return useMutation((account_id: string) => api.post('/api/v1/truth/carousels/avatars/seen', { - account_id, - })); + return useMutation(async (accountId: string) => { + if (features.carouselSeen) { + await void api.post('/api/v1/truth/carousels/avatars/seen', { + account_id: accountId, + }); + } + }); } export { useCarouselAvatars, useMarkAsSeen }; \ No newline at end of file diff --git a/app/soapbox/utils/features.ts b/app/soapbox/utils/features.ts index 9fc226b4e..1927bde2e 100644 --- a/app/soapbox/utils/features.ts +++ b/app/soapbox/utils/features.ts @@ -212,6 +212,12 @@ const getInstanceFeatures = (instance: Instance) => { */ carousel: v.software === TRUTHSOCIAL, + /** + * Ability to mark a carousel avatar as "seen." + * @see POST /api/v1/truth/carousels/avatars/seen + */ + carouselSeen: v.software === TRUTHSOCIAL, + /** * Ability to accept a chat. * POST /api/v1/pleroma/chats/:id/accept From e2b6a7af844ba1e07e6bccd5747d3d9d219ef7f9 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 14 Dec 2022 13:23:11 -0600 Subject: [PATCH 16/18] Carousel: don't set unseen unless it's explicitly `false` (backwards-compat) --- app/soapbox/features/feed-filtering/feed-carousel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/soapbox/features/feed-filtering/feed-carousel.tsx b/app/soapbox/features/feed-filtering/feed-carousel.tsx index b30ecba0a..481ae138e 100644 --- a/app/soapbox/features/feed-filtering/feed-carousel.tsx +++ b/app/soapbox/features/feed-filtering/feed-carousel.tsx @@ -96,7 +96,7 @@ const FeedCarousel = () => { if (avatars.length > 0) { setSeenAccountIds( avatars - .filter((avatar) => avatar.seen) + .filter((avatar) => avatar.seen !== false) .map((avatar) => avatar.account_id), ); } From b5f829cdb374312a9794d3ae12d915e189ec4a1d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 14 Dec 2022 13:28:41 -0600 Subject: [PATCH 17/18] Carousel: add mock for seen API --- .../features/feed-filtering/__tests__/feed-carousel.test.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/soapbox/features/feed-filtering/__tests__/feed-carousel.test.tsx b/app/soapbox/features/feed-filtering/__tests__/feed-carousel.test.tsx index 514e23c4c..dc34e73e0 100644 --- a/app/soapbox/features/feed-filtering/__tests__/feed-carousel.test.tsx +++ b/app/soapbox/features/feed-filtering/__tests__/feed-carousel.test.tsx @@ -70,6 +70,8 @@ describe('', () => { mock.onGet('/api/v1/accounts/1/statuses').reply(200, [], { link: '; rel=\'prev\'', }); + + mock.onPost('/api/v1/truth/carousels/avatars/seen').reply(200); }); }); From 5499696915b6025c8d4cd0711f3c70289e4c8662 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 14 Dec 2022 13:53:59 -0600 Subject: [PATCH 18/18] EditProfile: move profile preview (avatar/banner) to the top --- app/soapbox/features/edit-profile/index.tsx | 39 +++++++++++---------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/app/soapbox/features/edit-profile/index.tsx b/app/soapbox/features/edit-profile/index.tsx index 3f0cdcf27..1e3c0ed7d 100644 --- a/app/soapbox/features/edit-profile/index.tsx +++ b/app/soapbox/features/edit-profile/index.tsx @@ -310,6 +310,26 @@ const EditProfile: React.FC = () => { return (
+
+ + +
+ } + hintText={} + > + + + + } + hintText={} + > + + +
+
+ } > @@ -369,25 +389,6 @@ const EditProfile: React.FC = () => { /> -
- - -
- } - hintText={} - > - - - } - hintText={} - > - - -
-
- {features.followRequests && (