about summary refs log tree commit diff
path: root/src/lib/strings.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/strings.ts')
-rw-r--r--src/lib/strings.ts151
1 files changed, 151 insertions, 0 deletions
diff --git a/src/lib/strings.ts b/src/lib/strings.ts
new file mode 100644
index 000000000..8f2c3a3ea
--- /dev/null
+++ b/src/lib/strings.ts
@@ -0,0 +1,151 @@
+import {AtUri} from '../third-party/uri'
+import {Entity} from '../third-party/api/src/client/types/app/bsky/feed/post'
+import {PROD_SERVICE} from '../state'
+
+export const MAX_DISPLAY_NAME = 64
+export const MAX_DESCRIPTION = 256
+
+export function pluralize(n: number, base: string, plural?: string): string {
+  if (n === 1) {
+    return base
+  }
+  if (plural) {
+    return plural
+  }
+  return base + 's'
+}
+
+export function makeRecordUri(
+  didOrName: string,
+  collection: string,
+  rkey: string,
+) {
+  const urip = new AtUri(`at://host/`)
+  urip.host = didOrName
+  urip.collection = collection
+  urip.rkey = rkey
+  return urip.toString()
+}
+
+const MINUTE = 60
+const HOUR = MINUTE * 60
+const DAY = HOUR * 24
+const MONTH = DAY * 30
+const YEAR = DAY * 365
+export function ago(date: number | string | Date): string {
+  let ts: number
+  if (typeof date === 'string') {
+    ts = Number(new Date(date))
+  } else if (date instanceof Date) {
+    ts = Number(date)
+  } else {
+    ts = date
+  }
+  const diffSeconds = Math.floor((Date.now() - ts) / 1e3)
+  if (diffSeconds < MINUTE) {
+    return `${diffSeconds}s`
+  } else if (diffSeconds < HOUR) {
+    return `${Math.floor(diffSeconds / MINUTE)}m`
+  } else if (diffSeconds < DAY) {
+    return `${Math.floor(diffSeconds / HOUR)}h`
+  } else if (diffSeconds < MONTH) {
+    return `${Math.floor(diffSeconds / DAY)}d`
+  } else if (diffSeconds < YEAR) {
+    return `${Math.floor(diffSeconds / MONTH)}mo`
+  } else {
+    return new Date(ts).toLocaleDateString()
+  }
+}
+
+export function extractEntities(
+  text: string,
+  knownHandles?: Set<string>,
+): Entity[] | undefined {
+  let match
+  let ents: Entity[] = []
+  {
+    // mentions
+    const re = /(^|\s)(@)([a-zA-Z0-9\.-]+)(\b)/dg
+    while ((match = re.exec(text))) {
+      if (knownHandles && !knownHandles.has(match[3])) {
+        continue // not a known handle
+      }
+      ents.push({
+        type: 'mention',
+        value: match[3],
+        index: {
+          start: match.indices[2][0], // skip the (^|\s) but include the '@'
+          end: match.indices[3][1],
+        },
+      })
+    }
+  }
+  {
+    // links
+    const re = /(^|\s)(https?:\/\/[\S]+)(\b)/dg
+    while ((match = re.exec(text))) {
+      ents.push({
+        type: 'link',
+        value: match[2],
+        index: {
+          start: match.indices[1][0], // skip the (^|\s) but include the '@'
+          end: match.indices[2][1],
+        },
+      })
+    }
+  }
+  return ents.length > 0 ? ents : undefined
+}
+
+export function makeValidHandle(str: string): string {
+  if (str.length > 20) {
+    str = str.slice(0, 20)
+  }
+  str = str.toLowerCase()
+  return str.replace(/^[^a-z]+/g, '').replace(/[^a-z0-9-]/g, '')
+}
+
+export function createFullHandle(name: string, domain: string): string {
+  name = name.replace(/[\.]+$/, '')
+  domain = domain.replace(/^[\.]+/, '')
+  return `${name}.${domain}`
+}
+
+export function enforceLen(str: string, len: number): string {
+  str = str || ''
+  if (str.length > len) {
+    return str.slice(0, len)
+  }
+  return str
+}
+
+export function cleanError(str: string): string {
+  if (str.includes('Network request failed')) {
+    return 'Unable to connect. Please check your internet connection and try again.'
+  }
+  if (str.startsWith('Error: ')) {
+    return str.slice('Error: '.length)
+  }
+  return str
+}
+
+export function toNiceDomain(url: string): string {
+  try {
+    const urlp = new URL(url)
+    if (`https://${urlp.host}` === PROD_SERVICE) {
+      return 'Bluesky Social'
+    }
+    return urlp.host
+  } catch (e) {
+    return url
+  }
+}
+
+export function toShareUrl(url: string) {
+  if (!url.startsWith('https')) {
+    const urlp = new URL('https://bsky.app')
+    urlp.pathname = url
+    url = urlp.toString()
+  }
+  return url
+}