diff --git a/package.json b/package.json
index 01a87c0..2e5a7a4 100644
--- a/package.json
+++ b/package.json
@@ -143,7 +143,8 @@
"Element",
"Image",
"NotificationEvent",
- "NodeList"
+ "NodeList",
+ "DOMParser"
],
"ignore": [
"dist",
diff --git a/routes/_a11y/getAccessibleLabelForStatus.js b/routes/_a11y/getAccessibleLabelForStatus.js
new file mode 100644
index 0000000..9b5beb6
--- /dev/null
+++ b/routes/_a11y/getAccessibleLabelForStatus.js
@@ -0,0 +1,49 @@
+import { getAccountAccessibleName } from './getAccountAccessibleName'
+import { htmlToPlainText } from '../_utils/htmlToPlainText'
+import { POST_PRIVACY_OPTIONS } from '../_static/statuses'
+
+function notificationText (notification, omitEmojiInDisplayNames) {
+ if (!notification) {
+ return
+ }
+ let notificationAccountDisplayName = getAccountAccessibleName(notification.account, omitEmojiInDisplayNames)
+ if (notification.type === 'reblog') {
+ return `${notificationAccountDisplayName} boosted your status`
+ } else if (notification.type === 'favourite') {
+ return `${notificationAccountDisplayName} favorited your status`
+ }
+}
+
+function privacyText (visibility) {
+ for (let option of POST_PRIVACY_OPTIONS) {
+ if (option.key === visibility) {
+ return option.label
+ }
+ }
+}
+
+function reblogText (reblog, account, omitEmojiInDisplayNames) {
+ if (!reblog) {
+ return
+ }
+ let accountDisplayName = getAccountAccessibleName(account, omitEmojiInDisplayNames)
+ return `Boosted by ${accountDisplayName}`
+}
+
+export function getAccessibleLabelForStatus (originalAccount, account, content,
+ timeagoFormattedDate, spoilerText, showContent,
+ reblog, notification, visibility, omitEmojiInDisplayNames) {
+ let originalAccountDisplayName = getAccountAccessibleName(originalAccount, omitEmojiInDisplayNames)
+
+ let values = [
+ notificationText(notification, omitEmojiInDisplayNames),
+ originalAccountDisplayName,
+ (showContent || !spoilerText) ? htmlToPlainText(content) : `Content warning: ${spoilerText}`,
+ timeagoFormattedDate,
+ `@${originalAccount.acct}`,
+ privacyText(visibility),
+ reblogText(reblog, account, omitEmojiInDisplayNames)
+ ].filter(Boolean)
+
+ return values.join(', ')
+}
diff --git a/routes/_a11y/getAccountAccessibleName.js b/routes/_a11y/getAccountAccessibleName.js
new file mode 100644
index 0000000..e5df001
--- /dev/null
+++ b/routes/_a11y/getAccountAccessibleName.js
@@ -0,0 +1,10 @@
+import { removeEmoji } from '../_utils/removeEmoji'
+
+export function getAccountAccessibleName (account, omitEmojiInDisplayNames) {
+ let emojis = account.emojis
+ let displayName = account.display_name || account.username
+ if (omitEmojiInDisplayNames) {
+ displayName = removeEmoji(displayName, emojis) || displayName
+ }
+ return displayName
+}
diff --git a/routes/_components/status/Notification.html b/routes/_components/status/Notification.html
index 8510ca8..6a26137 100644
--- a/routes/_components/status/Notification.html
+++ b/routes/_components/status/Notification.html
@@ -6,7 +6,9 @@
+ aria-setsize={length}
+ aria-label={ariaLabel}
+ >
@@ -30,6 +32,7 @@
import Status from './Status.html'
import StatusHeader from './StatusHeader.html'
import { store } from '../../_store/store'
+ import { getAccountAccessibleName } from '../../_a11y/getAccountAccessibleName'
export default {
components: {
@@ -45,7 +48,10 @@
statusId: ({ status }) => status && status.id,
uuid: ({ $currentInstance, timelineType, timelineValue, notificationId, statusId }) => {
return `${$currentInstance}/${timelineType}/${timelineValue}/${notificationId}/${statusId || ''}`
- }
+ },
+ ariaLabel: ({ status, account, $omitEmojiInDisplayNames }) => (
+ !status && `${getAccountAccessibleName(account, $omitEmojiInDisplayNames)} followed you, @${account.acct}`
+ )
}
}
\ No newline at end of file
diff --git a/routes/_components/status/Status.html b/routes/_components/status/Status.html
index da6fa1e..4403ebd 100644
--- a/routes/_components/status/Status.html
+++ b/routes/_components/status/Status.html
@@ -110,7 +110,9 @@
import { classname } from '../../_utils/classname'
import { checkDomAncestors } from '../../_utils/checkDomAncestors'
import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
- import { removeEmoji } from '../../_utils/removeEmoji'
+ import { getAccountAccessibleName } from '../../_a11y/getAccountAccessibleName'
+ import { getAccessibleLabelForStatus } from '../../_a11y/getAccessibleLabelForStatus'
+ import { formatTimeagoDate } from '../../_intl/formatTimeagoDate'
const INPUT_TAGS = new Set(['a', 'button', 'input', 'textarea'])
const isUserInputElement = node => INPUT_TAGS.has(node.localName)
@@ -211,16 +213,17 @@
),
originalAccountEmojis: ({ originalAccount }) => (originalAccount.emojis || []),
originalAccountDisplayName: ({ originalAccount }) => (originalAccount.display_name || originalAccount.username),
- originalAccountAccessibleName: ({ originalAccountDisplayName, originalAccountEmojis, $omitEmojiInDisplayNames }) => {
- if ($omitEmojiInDisplayNames) {
- return removeEmoji(originalAccountDisplayName, originalAccountEmojis) || originalAccountDisplayName
- }
- return originalAccountDisplayName
+ originalAccountAccessibleName: ({ originalAccount, $omitEmojiInDisplayNames }) => {
+ return getAccountAccessibleName(originalAccount, $omitEmojiInDisplayNames)
},
- ariaLabel: ({ originalAccountAccessibleName, originalStatus, visibility, isStatusInOwnThread }) => (
- (visibility === 'direct' ? 'Direct message' : 'Status') +
- ` by ${originalAccountAccessibleName}` +
- (isStatusInOwnThread ? ' (focused)' : '')
+ createdAtDate: ({ originalStatus }) => originalStatus.created_at,
+ timeagoFormattedDate: ({ createdAtDate }) => formatTimeagoDate(createdAtDate),
+ reblog: ({ status }) => status.reblog,
+ ariaLabel: ({ originalAccount, account, content, timeagoFormattedDate, spoilerText,
+ showContent, reblog, notification, visibility, $omitEmojiInDisplayNames }) => (
+ getAccessibleLabelForStatus(originalAccount, account, content,
+ timeagoFormattedDate, spoilerText, showContent,
+ reblog, notification, visibility, $omitEmojiInDisplayNames)
),
showHeader: ({ notification, status, timelineType }) => (
(notification && (notification.type === 'reblog' || notification.type === 'favourite')) ||
@@ -238,7 +241,8 @@
params: ({ notification, notificationId, status, statusId, timelineType,
account, accountId, uuid, isStatusInNotification, isStatusInOwnThread,
originalAccount, originalAccountId, spoilerShown, visibility, replyShown,
- replyVisibility, spoilerText, originalStatus, originalStatusId, inReplyToId }) => ({
+ replyVisibility, spoilerText, originalStatus, originalStatusId, inReplyToId,
+ createdAtDate, timeagoFormattedDate }) => ({
notification,
notificationId,
status,
@@ -258,7 +262,9 @@
spoilerText,
originalStatus,
originalStatusId,
- inReplyToId
+ inReplyToId,
+ createdAtDate,
+ timeagoFormattedDate
})
}
}
diff --git a/routes/_components/status/StatusRelativeDate.html b/routes/_components/status/StatusRelativeDate.html
index cea2160..1eb5af9 100644
--- a/routes/_components/status/StatusRelativeDate.html
+++ b/routes/_components/status/StatusRelativeDate.html
@@ -2,9 +2,9 @@
href="/statuses/{originalStatusId}"
focus-key={focusKey}
>
-