about summary refs log tree commit diff
path: root/src/lib
diff options
context:
space:
mode:
authorLW <git@llllvvuu.dev>2023-05-16 11:13:05 -0700
committerGitHub <noreply@github.com>2023-05-16 13:13:05 -0500
commit50c1841a06d0502428f70bb0bb225cca70f82c20 (patch)
tree47b12090bb18e04e599a45cae7648135c772ab28 /src/lib
parenta5838694bd7debecc1d66ce8f8e4492350ea289f (diff)
downloadvoidsky-50c1841a06d0502428f70bb0bb225cca70f82c20.tar.zst
feat: Update HTML `title` on web #626 #599 (#655)
For any `Screen` that shows on desktop, `title` is "(1) ... - Bluesky"
where "(1)" is the unread notification count.

The titles are unlocalized and the string "Bluesky" is hardcoded,
following the pattern of the rest of the app.

Display names and post content are loaded into the title as effects.

Tested:
* all screens
* screen changes / component mounts/unmounts
* long posts with links and images
* display name set/unset
* spamming myself with notifications, clearing notifications
* /profile/did:... links
* lint (only my changed files), jest, e2e.

New utilities: `useUnreadCountLabel`, `bskyTitle`,
`combinedDisplayName`, `useSetTitle`.

resolves: #626 #599
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/hooks/useSetTitle.ts16
-rw-r--r--src/lib/hooks/useUnreadCountLabel.ts19
-rw-r--r--src/lib/strings/display-names.ts15
-rw-r--r--src/lib/strings/headings.ts4
4 files changed, 54 insertions, 0 deletions
diff --git a/src/lib/hooks/useSetTitle.ts b/src/lib/hooks/useSetTitle.ts
new file mode 100644
index 000000000..85ba44d29
--- /dev/null
+++ b/src/lib/hooks/useSetTitle.ts
@@ -0,0 +1,16 @@
+import {useEffect} from 'react'
+import {useNavigation} from '@react-navigation/native'
+
+import {NavigationProp} from 'lib/routes/types'
+import {bskyTitle} from 'lib/strings/headings'
+import {useUnreadCountLabel} from './useUnreadCountLabel'
+
+export function useSetTitle(title?: string) {
+  const navigation = useNavigation<NavigationProp>()
+  const unreadCountLabel = useUnreadCountLabel()
+  useEffect(() => {
+    if (title) {
+      navigation.setOptions({title: bskyTitle(title, unreadCountLabel)})
+    }
+  }, [title, navigation, unreadCountLabel])
+}
diff --git a/src/lib/hooks/useUnreadCountLabel.ts b/src/lib/hooks/useUnreadCountLabel.ts
new file mode 100644
index 000000000..e2bf77885
--- /dev/null
+++ b/src/lib/hooks/useUnreadCountLabel.ts
@@ -0,0 +1,19 @@
+import {useEffect, useReducer} from 'react'
+import {DeviceEventEmitter} from 'react-native'
+import {useStores} from 'state/index'
+
+export function useUnreadCountLabel() {
+  // HACK: We don't have anything like Redux selectors,
+  // and we don't want to use <RootStoreContext.Consumer />
+  // to react to the whole store
+  const [, forceUpdate] = useReducer(x => x + 1, 0)
+  useEffect(() => {
+    const subscription = DeviceEventEmitter.addListener(
+      'unread-notifications',
+      forceUpdate,
+    )
+    return () => subscription?.remove()
+  }, [forceUpdate])
+
+  return useStores().me.notifications.unreadCountLabel
+}
diff --git a/src/lib/strings/display-names.ts b/src/lib/strings/display-names.ts
index 555151b55..b98153732 100644
--- a/src/lib/strings/display-names.ts
+++ b/src/lib/strings/display-names.ts
@@ -10,3 +10,18 @@ export function sanitizeDisplayName(str: string): string {
   }
   return ''
 }
+
+export function combinedDisplayName({
+  handle,
+  displayName,
+}: {
+  handle?: string
+  displayName?: string
+}): string {
+  if (!handle) {
+    return ''
+  }
+  return displayName
+    ? `${sanitizeDisplayName(displayName)} (@${handle})`
+    : `@${handle}`
+}
diff --git a/src/lib/strings/headings.ts b/src/lib/strings/headings.ts
new file mode 100644
index 000000000..a88a69645
--- /dev/null
+++ b/src/lib/strings/headings.ts
@@ -0,0 +1,4 @@
+export function bskyTitle(page: string, unreadCountLabel?: string) {
+  const unreadPrefix = unreadCountLabel ? `(${unreadCountLabel}) ` : ''
+  return `${unreadPrefix}${page} - Bluesky`
+}