diff --git a/packages/frontend/src/components/AccountListSidebar/styles.module.scss b/packages/frontend/src/components/AccountListSidebar/styles.module.scss
index 249c00174d..22d77a81e9 100644
--- a/packages/frontend/src/components/AccountListSidebar/styles.module.scss
+++ b/packages/frontend/src/components/AccountListSidebar/styles.module.scss
@@ -50,6 +50,10 @@
--als-avatar-margin: 5px;
--als-active-indicator-color: white;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+
align-items: center;
display: flex;
flex-direction: column;
@@ -63,6 +67,28 @@
}
}
+ .accountWrapper {
+ margin-bottom: var(--als-avatar-margin);
+ margin-top: var(--als-avatar-margin);
+
+ &.isSticky {
+ position: sticky;
+ bottom: var(--als-avatar-margin);
+ top: var(--als-avatar-margin);
+ // Only needed when this account is scrolled _above_ the visible region.
+ z-index: map-get($z-index, account-list-sidebar-scope-sticky-account);
+
+ .account {
+ .avatar {
+ opacity: 1;
+ .content {
+ box-shadow: 0px 0px 4px 2px #00000040;
+ }
+ }
+ }
+ }
+ }
+
.account {
border: none;
background: none;
@@ -71,8 +97,6 @@
height: var(--als-avatar-size);
- margin-bottom: var(--als-avatar-margin);
- margin-top: var(--als-avatar-margin);
// Adding extra `var(--als-avatar-size) + 2 * var(--als-avatar-margin)`
// to the margins so that
// if there is another `.isSticky` account, then that account does not
@@ -107,21 +131,6 @@
}
}
- &.isSticky {
- position: sticky;
- bottom: var(--als-avatar-margin);
- top: var(--als-avatar-margin);
- // Only needed when this account is scrolled _above_ the visible region.
- z-index: map-get($z-index, account-list-sidebar-scope-sticky-account);
-
- .avatar {
- opacity: 1;
- .content {
- box-shadow: 0px 0px 4px 2px #00000040;
- }
- }
- }
-
&.active,
&:hover,
&.context-menu-active {
diff --git a/packages/frontend/src/components/chat/ChatList.tsx b/packages/frontend/src/components/chat/ChatList.tsx
index 7ed0e8d73a..30c6aedcbb 100644
--- a/packages/frontend/src/components/chat/ChatList.tsx
+++ b/packages/frontend/src/components/chat/ChatList.tsx
@@ -5,6 +5,8 @@ import React, {
useCallback,
ComponentType,
useMemo,
+ HTMLAttributes,
+ useLayoutEffect,
} from 'react'
import {
FixedSizeList as List,
@@ -60,6 +62,7 @@ export function ChatListPart({
height,
itemKey,
setListRef,
+ olElementAttrs,
itemData,
itemHeight,
}: {
@@ -71,6 +74,10 @@ export function ChatListPart({
height: number
itemKey: ListItemKeySelector
setListRef?: (ref: List | null) => void
+ /**
+ * This does _not_ support maps with dynamically added/removed keys.
+ */
+ olElementAttrs?: HTMLAttributes
itemData: ChatListItemData | ContactChatListItemData | MessageChatListItemData
itemHeight: number
}) {
@@ -92,6 +99,26 @@ export function ChatListPart({
// So let's play it safe.
})
+ const olRef = useRef(null)
+ // 'react-window' does not expose API to set attributes on its element,
+ // so we have to `useLayoutEffect`.
+ useLayoutEffect(() => {
+ if (olRef.current == null) {
+ return
+ }
+ if (olElementAttrs == undefined) {
+ return
+ }
+
+ for (const [key, value] of Object.entries(olElementAttrs)) {
+ if (value == undefined) {
+ olRef.current.removeAttribute(key)
+ } else {
+ olRef.current.setAttribute(key, value)
+ }
+ }
+ })
+
return (
(
{({ height }) => (
-
+
{tx('search_in', searchChatInfo.name)}
{messageResultIds.length !== 0 &&
': ' + translate_n('n_messages', messageResultIds.length)}
@@ -378,6 +409,9 @@ export default function ChatList(props: {
classNameOfTargetElements={rovingTabindexItemsClassName}
>
(
<>
{isSearchActive && (
-
+
{translate_n('n_chats', chatListIds.length)}
)}
@@ -419,6 +456,18 @@ export default function ChatList(props: {
>
{isSearchActive && (
<>
-
+
{translate_n('n_contacts', contactIds.length)}
-
+
{translated_messages_label(messageResultIds.length)}
@@ -483,6 +541,9 @@ export default function ChatList(props: {
>
void
disabledContacts?: number[]
onContactContextMenu?: (contact: Type.Contact) => void
+ olElementAttrs?: Omit, 'style'>
}) {
const {
contacts,
@@ -32,9 +33,13 @@ export function ContactList(props: {
onRemoveClick,
disabledContacts,
onContactContextMenu,
+ olElementAttrs,
} = props
return (
-
+
{contacts.map(contact => {
let checked = false
if (showCheckbox && typeof isChecked === 'function') {
diff --git a/packages/frontend/src/components/dialogs/CreateChat/index.tsx b/packages/frontend/src/components/dialogs/CreateChat/index.tsx
index 3f33dceb08..8ce3deed21 100644
--- a/packages/frontend/src/components/dialogs/CreateChat/index.tsx
+++ b/packages/frontend/src/components/dialogs/CreateChat/index.tsx
@@ -565,7 +565,7 @@ export function CreateGroup(props: CreateGroupProps) {
type='group'
/>
-
+
{tx('n_members', groupMembers.length.toString(), {
quantity: groupMembers.length,
})}
@@ -587,6 +587,9 @@ export function CreateGroup(props: CreateGroupProps) {
onRemoveClick={c => {
removeGroupMember(c)
}}
+ olElementAttrs={{
+ 'aria-labelledby': 'create-group-members-title',
+ }}
/>
@@ -693,7 +696,10 @@ function CreateBroadcastList(props: CreateBroadcastListProps) {
/>
{broadcastRecipients.length > 0 && (
-
+
{tx('n_recipients', broadcastRecipients.length.toString(), {
quantity: broadcastRecipients.length,
})}
@@ -716,6 +722,9 @@ function CreateBroadcastList(props: CreateBroadcastListProps) {
onRemoveClick={c => {
removeBroadcastRecipient(c)
}}
+ olElementAttrs={{
+ 'aria-labelledby': 'create-broadcast-list-recipients-title',
+ }}
/>
diff --git a/packages/frontend/src/components/dialogs/SelectChat/index.tsx b/packages/frontend/src/components/dialogs/SelectChat/index.tsx
index e7a245181f..2b30379465 100644
--- a/packages/frontend/src/components/dialogs/SelectChat/index.tsx
+++ b/packages/frontend/src/components/dialogs/SelectChat/index.tsx
@@ -64,6 +64,9 @@ export default function SelectChat(props: Props) {
{({ height }) => (
{isRelatedChatsEnabled && (
<>
- {tx('related_chats')}
+
+ {tx('related_chats')}
+
>
)}
-
+
{!isBroadcast
? tx('n_members', groupMembers.length.toString(), {
quantity: groupMembers.length,
@@ -340,12 +351,20 @@ function ViewGroupInner(
setProfileContact(contact)
}}
onRemoveClick={showRemoveGroupMemberConfirmationDialog}
+ olElementAttrs={{
+ 'aria-labelledby': 'view-group-members-recipients-title',
+ }}
/>
{pastContacts.length > 0 && (
<>
-
{tx('past_members')}
+
+ {tx('past_members')}
+
diff --git a/packages/frontend/src/components/dialogs/ViewProfile/index.tsx b/packages/frontend/src/components/dialogs/ViewProfile/index.tsx
index d54146d6fa..c1facafbe1 100644
--- a/packages/frontend/src/components/dialogs/ViewProfile/index.tsx
+++ b/packages/frontend/src/components/dialogs/ViewProfile/index.tsx
@@ -327,7 +327,9 @@ export function ViewProfileInner({
)}
{!(isDeviceChat || isSelfChat) && (
<>
- {tx('profile_shared_chats')}
+
+ {tx('profile_shared_chats')}
+
{({ height }) => (
loadMissingMessages: () => Promise
}) => {
+ const tx = useTranslationFunction()
+
const {
onScroll,
onScrollEnd,
@@ -859,14 +861,14 @@ export const MessageListInner = React.memo(
if (!loaded) {
return (
)
}
return (
-
+
{messageListItems.length === 0 && }
{activeView.map(messageId => {