@meith/web 0.21.1 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/app/(board)/notifications/preferences/page.tsx +15 -1
  2. package/app/(board)/unsubscribe/page.tsx +5 -3
  3. package/app/admin/api/marketplace/screenshot/route.ts +0 -8
  4. package/app/admin/users/mail/page.tsx +1 -1
  5. package/app/layout.tsx +1 -3
  6. package/app/manifest.ts +1 -3
  7. package/bin/forum-web.mjs +1 -182
  8. package/bin/forum-web.test.ts +0 -22
  9. package/next.config.mjs +14 -29
  10. package/package.json +47 -47
  11. package/proxy.ts +0 -14
  12. package/src/components/account/notification-forms.tsx +45 -0
  13. package/src/components/admin/admin-nav.tsx +0 -4
  14. package/src/components/admin/marketplace-listing.tsx +0 -9
  15. package/src/components/auth/passkey-client.ts +0 -5
  16. package/src/components/auth/register-form.tsx +18 -0
  17. package/src/components/content/composer-ids.ts +0 -1
  18. package/src/components/content/markdown-editor.tsx +0 -7
  19. package/src/components/install/install-form.tsx +3 -1
  20. package/src/components/shell/demo-banner.tsx +0 -8
  21. package/src/config.ts +0 -11
  22. package/src/server/admin-actions.ts +0 -5
  23. package/src/server/antispam.ts +27 -14
  24. package/src/server/auth-actions.ts +12 -1
  25. package/src/server/auth-config.ts +0 -11
  26. package/src/server/auth-events.ts +0 -5
  27. package/src/server/brand-assets.ts +2 -6
  28. package/src/server/container.ts +0 -8
  29. package/src/server/cookies.ts +0 -24
  30. package/src/server/demo-uploads.ts +0 -11
  31. package/src/server/demo.ts +0 -21
  32. package/src/server/federation-actions.ts +0 -6
  33. package/src/server/federation.ts +0 -7
  34. package/src/server/feed-routes.ts +0 -5
  35. package/src/server/i18n.ts +1 -3
  36. package/src/server/install.ts +13 -2
  37. package/src/server/location-header.ts +0 -30
  38. package/src/server/mail-send.ts +0 -6
  39. package/src/server/marketplace-actions.ts +0 -6
  40. package/src/server/marketplace-admin.ts +0 -33
  41. package/src/server/navigation.ts +0 -5
  42. package/src/server/notification-actions.ts +25 -0
  43. package/src/server/onboarding.ts +0 -5
  44. package/src/server/plugin-admin-actions.ts +0 -11
  45. package/src/server/plugin-admin.ts +0 -3
  46. package/src/server/plugin-host.ts +15 -6
  47. package/src/server/plugin-pages.ts +3 -68
  48. package/src/server/plugin-routes.ts +1 -1
  49. package/src/server/plugin-runtime.ts +78 -0
  50. package/src/server/presence.ts +3 -34
  51. package/src/server/relations.ts +1 -3
  52. package/src/server/reply-core.ts +1 -0
  53. package/src/server/request-fingerprint.ts +0 -1
  54. package/src/server/search-page.ts +0 -4
  55. package/src/server/session-actions.ts +0 -1
  56. package/src/server/settings.ts +0 -11
  57. package/src/server/subscription-actions.ts +12 -4
  58. package/src/server/syndication.ts +0 -4
  59. package/src/server/theme-admin.ts +2 -6
  60. package/src/server/thread-core.ts +6 -1
  61. package/src/server/two-factor-actions.ts +0 -5
  62. package/src/server/two-factor.ts +0 -6
  63. package/src/server/upgrade-notice.ts +1 -1
  64. package/src/view/account-copy.ts +5 -0
  65. package/src/view/auth-copy.ts +2 -0
  66. package/src/view/editor-toolbar.ts +0 -1
  67. package/src/view/install-copy.ts +3 -0
  68. package/src/view/navigation.ts +0 -4
  69. package/src/view/notification-menu.ts +0 -2
  70. package/src/view/notifications.ts +1 -0
  71. package/src/view/pager.ts +0 -6
  72. package/src/view/panel-nav.ts +0 -1
  73. package/src/view/security-activity.ts +0 -5
  74. package/src/view/subscriptions.ts +1 -0
  75. package/src/view/two-factor.ts +0 -5
  76. package/src/view/usercp.ts +1 -3
@@ -32,11 +32,6 @@ export function twoFactorService(): TwoFactorService | null {
32
32
  })
33
33
  }
34
34
 
35
- /**
36
- * Offered when the board says so and the board has a key to seal a secret
37
- * with. Without AUTH_SECRET there is nowhere safe to put the secret, so the
38
- * surface is hidden rather than half-built.
39
- */
40
35
  export async function twoFactorOffered(): Promise<boolean> {
41
36
  if (getContainer().dataSource !== 'postgres') return false
42
37
  if (twoFactorService() === null) return false
@@ -49,7 +44,6 @@ export async function twoFactorRequiredForStaff(): Promise<boolean> {
49
44
  return (await getSettingsUncached()).get('security.two_factor_required_for_staff')
50
45
  }
51
46
 
52
- /** Whether this member must hold a second factor, and may not drop it. */
53
47
  export async function secondFactorPosture(userId: number | null): Promise<SecondFactorPosture> {
54
48
  const offered = await twoFactorOffered()
55
49
  if (!offered || userId === null) return { offered, required: false }
@@ -6,7 +6,7 @@ import { planUpgrade, type UpgradeState, upgradeNotice } from '@meith/upgrade'
6
6
 
7
7
  import { activeDefinitions } from './plugin-host'
8
8
 
9
- export const CODE_VERSION = '0.21.1'
9
+ export const CODE_VERSION = '0.22.0'
10
10
 
11
11
  export async function pendingUpgradeNotice(): Promise<string | null> {
12
12
  if (env.DATA_SOURCE !== 'postgres') return null
@@ -297,6 +297,11 @@ export function notificationFormsCopy(
297
297
  'accountForm.notifications.push',
298
298
  'accountForm.notifications.blurb',
299
299
  'accountForm.notifications.submit',
300
+ 'accountForm.announcements.legend',
301
+ 'accountForm.announcements.title',
302
+ 'accountForm.announcements.description',
303
+ 'accountForm.announcements.blurb',
304
+ 'accountForm.announcements.submit',
300
305
  'accountForm.push.heading',
301
306
  'accountForm.push.subscribe',
302
307
  'accountForm.push.unsubscribe',
@@ -34,6 +34,8 @@ export function registerFormCopy(
34
34
  'authForm.register.password',
35
35
  'authForm.register.honeypot',
36
36
  'authForm.register.challengeHint',
37
+ 'authForm.register.announcements',
38
+ 'authForm.register.announcementsHint',
37
39
  'authForm.register.submit',
38
40
  ],
39
41
  t,
@@ -62,7 +62,6 @@ function button(spec: ToolSpec, t: Translator): EditorToolbarButtonModel {
62
62
 
63
63
  export interface EditorToolbarInput {
64
64
  readonly textareaId?: string
65
- /** Whether this composer takes attachments. Defaults to `false`. */
66
65
  readonly attachments?: boolean
67
66
  readonly t?: Translator
68
67
  }
@@ -61,6 +61,9 @@ export function installFormCopy(
61
61
  'install.steps.failed',
62
62
  'install.steps.notRun',
63
63
  'install.outcome.oneAnswer',
64
+ 'install.schema.missing',
65
+ 'install.raced.inFlight',
66
+ 'install.raced.sealed',
64
67
  'install.outcome.passwords',
65
68
  'install.field.boardName',
66
69
  'install.field.boardUrl',
@@ -67,10 +67,6 @@ export function defaultNavigationItems(): readonly NavigationItemRow[] {
67
67
  }))
68
68
  }
69
69
 
70
- /**
71
- * What a plugin called an item, for the rows the board holds on its behalf.
72
- * Keyed by the same `plugin.<plugin>.<item>` the row carries.
73
- */
74
70
  export interface NavigationName {
75
71
  readonly label: string
76
72
  readonly labelKey: string | null
@@ -13,14 +13,12 @@ export type NotificationMenuTabKind = 'notifications' | 'messages' | 'mod'
13
13
 
14
14
  export interface NotificationMenuRow {
15
15
  readonly key: string
16
- /** The id a mark-seen form submits, or `null` for a row that is acted on elsewhere. */
17
16
  readonly seenId: number | null
18
17
  readonly subject: string
19
18
  readonly meta: string | null
20
19
  readonly href: string | null
21
20
  readonly at: TimeModel
22
21
  readonly isUnread: boolean
23
- /** A short category label shown before the subject, or `null`. */
24
22
  readonly tag: string | null
25
23
  }
26
24
 
@@ -77,6 +77,7 @@ export function notificationNotice(
77
77
  },
78
78
  t: Translator = untranslated(),
79
79
  ): string | null {
80
+ if (query.saved === 'announcements') return t.t('notice.announcementsSaved')
80
81
  if (query.saved !== undefined) return t.t('notice.preferencesSaved')
81
82
  if (query.read === 'all') return t.t('notice.allRead')
82
83
  if (query.read === 'one') return t.t('notice.markedRead')
package/src/view/pager.ts CHANGED
@@ -15,12 +15,6 @@ export type Cursor = Readonly<Record<string, string>>
15
15
  export interface PagerInput {
16
16
  readonly path: string
17
17
  readonly params: QueryParams
18
- /**
19
- * The query parameters the cursor owns. They are rewritten on every step and
20
- * never carried forward from the current address, which is what keeps a
21
- * two-part cursor — a rank and an id — from being half of one page and half
22
- * of the next.
23
- */
24
18
  readonly cursorParams: readonly string[]
25
19
  readonly nextCursor: Cursor | null
26
20
  readonly pageSize?: number
@@ -12,7 +12,6 @@ import { untranslated } from './time'
12
12
  export interface PanelSubsection {
13
13
  readonly href: string
14
14
  readonly titleKey?: string
15
- /** Set instead of `titleKey` where the name is data, such as a plugin's. */
16
15
  readonly titleText?: string
17
16
  readonly record?: boolean
18
17
  }
@@ -30,11 +30,6 @@ export function authEventLabel(kind: string, t: Translator = untranslated()): st
30
30
  return key === undefined ? kind.replace(/_/g, ' ') : t.t(key)
31
31
  }
32
32
 
33
- /**
34
- * Enough of a user-agent string for a member to tell one of their own devices
35
- * from another, and no more: the full string is a fingerprint, and this is a
36
- * page whose job is recognition, not identification.
37
- */
38
33
  export function describeDevice(userAgent: string | null, t: Translator = untranslated()): string {
39
34
  const unknown = t.t('device.unknown')
40
35
  if (userAgent === null || userAgent.trim() === '') return unknown
@@ -70,6 +70,7 @@ export function unsubscribeNotice(
70
70
  done: string | undefined,
71
71
  t: Translator = untranslated(),
72
72
  ): string | null {
73
+ if (done === 'announcements') return t.t('subscription.announcementsOff')
73
74
  if (done === 'email') return t.t('subscription.emailsOff')
74
75
  if (done === 'one') return t.t('subscription.doneOne')
75
76
  return null
@@ -1,6 +1 @@
1
- /**
2
- * The form-state key the fresh recovery codes travel back under. It lives here
3
- * rather than beside the action because a `'use server'` module may export
4
- * nothing but async functions, and the client component needs to read it.
5
- */
6
1
  export const RECOVERY_CODES_FIELD = 'recoveryCodes'
@@ -59,9 +59,7 @@ export function availableTimezones(): readonly string[] {
59
59
  if (typeof supported === 'function') {
60
60
  try {
61
61
  return supported('timeZone')
62
- } catch {
63
- /* ignore */
64
- }
62
+ } catch {}
65
63
  }
66
64
 
67
65
  return FALLBACK_TIMEZONES