diff --git a/site/app/components/EpicCard.tsx b/site/app/components/EpicCard.tsx deleted file mode 100644 index 4749eca..0000000 --- a/site/app/components/EpicCard.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { Link } from 'react-router'; -import type { Project } from '../types'; -import { accentStyle } from './kit/basics'; -import ProjectTile from './ProjectTile'; -import Status from './Status'; -import TagList from './TagList'; - -interface Props { - epic: Project; - projects: Project[]; - context?: string; - accent?: string; -} - -export default function EpicCard({ epic, projects, context, accent }: Props) { - return ( -
-
-
-

- epic · {projects.length} {projects.length === 1 ? 'project' : 'projects'} -

-

- - {epic.name} - -

-
- -
- -

{epic.summary}

- - {context && ( -

- {context} -

- )} - - - - {projects.length > 0 && ( -
- {projects.map((project) => ( - - ))} -
- )} -
- ); -} diff --git a/site/app/components/ProjectCard.tsx b/site/app/components/ProjectCard.tsx deleted file mode 100644 index ae3578f..0000000 --- a/site/app/components/ProjectCard.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { Link } from 'react-router'; -import AuthorLink from './AuthorLink'; -import Status from './Status'; -import TagList from './TagList'; -import type { Project } from '../types'; - -interface Props { - project: Project; - context?: string; -} - -export default function ProjectCard({ project, context }: Props) { - return ( -
-
-

- - {project.name} - -

- -
- -

{project.summary}

- - {context && ( -

- {context} -

- )} - - - -
- - {project.authors.map((author, index) => ( - - {index > 0 && ·} - - - ))} - - - {project.url && ( - - visit ↗ - - )} - - source ↗ - - -
-
- ); -} diff --git a/site/app/components/kit/Palette.tsx b/site/app/components/kit/Palette.tsx index b44bbf2..8484878 100644 --- a/site/app/components/kit/Palette.tsx +++ b/site/app/components/kit/Palette.tsx @@ -73,6 +73,7 @@ export function Palette() { { setQuery(event.target.value); diff --git a/site/app/components/kit/live.tsx b/site/app/components/kit/live.tsx index 439d14a..e13eff6 100644 --- a/site/app/components/kit/live.tsx +++ b/site/app/components/kit/live.tsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useRef, useState, type CSSProperties } from 'react'; import { Link } from 'react-router'; import { useFolded } from '../../folds'; -import { clock, day, describe } from '../../format'; +import { clock, day, describe, plural } from '../../format'; import { useRoot } from '../../hub'; import { useConnection, @@ -73,7 +73,7 @@ export function Views({ kind, id }: { kind: 'post' | 'project'; id: string }) { const shown = useCountUp(count); return count > 0 ? ( - {shown} {count === 1 ? 'view' : 'views'} + {shown} {plural(count, 'view')} ) : null; } diff --git a/site/app/components/kit/map.tsx b/site/app/components/kit/map.tsx index 6a13dd8..e07f5a8 100644 --- a/site/app/components/kit/map.tsx +++ b/site/app/components/kit/map.tsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { plural } from '../../format'; import { useLiveMessages } from '../../live'; import type { Country } from '../../types'; import Status from '../Status'; @@ -40,8 +41,8 @@ export function VisitorMap({ initial }: { initial: Country[] }) {
- {tabs} {tabs === 1 ? 'tab' : 'tabs'} from {countries.length}{' '} - {countries.length === 1 ? 'country' : 'countries'} + {tabs} {plural(tabs, 'tab')} from {countries.length}{' '} + {plural(countries.length, 'country', 'countries')} last 10 minutes
diff --git a/site/app/components/kit/members.tsx b/site/app/components/kit/members.tsx index 81f4c16..ba6e0a2 100644 --- a/site/app/components/kit/members.tsx +++ b/site/app/components/kit/members.tsx @@ -1,7 +1,8 @@ import type { CSSProperties } from 'react'; import { Link } from 'react-router'; +import { plural } from '../../format'; import { useOnline } from '../../live'; -import { useSpotlight } from '../../motion/motion'; +import { useMotion, useSpotlight } from '../../motion/motion'; import type { Member } from '../../types'; import { accentStyle, Avatar } from './basics'; @@ -22,7 +23,7 @@ function MemberRow({ {member.name} {member.headline ?? `@${member.github}`} - + {projects} @@ -61,8 +62,9 @@ export function Orbit({ label: string; }) { const online = useOnline(); + const spinning = useMotion('orbit'); return ( -
+
{members.map((member, index) => (
- {project.deploy - ? `sha-${shortTag(project.deploy.tag)} · ${day(project.deploy.at)}` - : 'no deploy recorded yet'} + {project.deploy && `sha-${shortTag(project.deploy.tag)} · ${day(project.deploy.at)}`} {project.week.map((state, index) => ( - {project.url ? new URL(project.url).host : `${project.slug} · source only`} + {project.url + ? new URL(project.url).host + : `${project.slug} · ${STATUS_LABEL[project.status]}`}
); diff --git a/site/app/format.ts b/site/app/format.ts index b33fc20..807405e 100644 --- a/site/app/format.ts +++ b/site/app/format.ts @@ -9,6 +9,10 @@ export function day(iso: string) { return `${MONTHS[date.getUTCMonth()]} ${date.getUTCDate()}`; } +export function plural(count: number, noun: string, many = `${noun}s`) { + return count === 1 ? noun : many; +} + export function shortTag(tag: unknown) { return typeof tag === 'string' ? tag.replace(/^sha-/, '').slice(0, 7) : ''; } diff --git a/site/app/pages/member-page.tsx b/site/app/pages/member-page.tsx index 494298d..e7f20f7 100644 --- a/site/app/pages/member-page.tsx +++ b/site/app/pages/member-page.tsx @@ -7,6 +7,7 @@ import { ChangelogPanel, RunningPanel } from '../components/kit/panels'; import { SubscribePanel } from '../components/kit/subscribe'; import { Page } from '../components/Shell'; import type { Hub } from '../context'; +import { plural } from '../format'; import { useHub, useRoot } from '../hub'; import { createModel } from '../model'; import type { Member } from '../types'; @@ -38,6 +39,8 @@ export function MemberHero({ member, actions }: { member: Member; actions?: Reac const hub = useHub(); const live = hub.projectsOwnedBy(member).filter((project) => project.status === 'live').length; const posts = hub.postsBy(member).length; + const projects = hub.projectsForMember(member).length + hub.epicsForMember(member).length; + const releases = hub.releasesOwnedBy(member).length; return ( } @@ -48,15 +51,12 @@ export function MemberHero({ member, actions }: { member: Member; actions?: Reac actions={actions} facts={ <> - + {live > 0 && } - + {posts > 0 && ( - + )} {member.joined && } diff --git a/site/app/pages/project-page.tsx b/site/app/pages/project-page.tsx index 481038f..31b97c1 100644 --- a/site/app/pages/project-page.tsx +++ b/site/app/pages/project-page.tsx @@ -14,7 +14,7 @@ import { Page } from '../components/Shell'; import Status from '../components/Status'; import TagList from '../components/TagList'; import type { Hub } from '../context'; -import { day, shortTag } from '../format'; +import { day, plural, shortTag } from '../format'; import { useHub, useRoot } from '../hub'; import { useView, useViews } from '../live'; import { changelogPath } from '../model'; @@ -148,9 +148,9 @@ export function ProjectPage({ data: loaderData }: { data: ReturnType {releases[0] && } - + {releases[0] && } - {views > 0 && } + {views > 0 && } } > @@ -183,7 +183,7 @@ export function ProjectPage({ data: loaderData }: { data: ReturnType - {writing.length} {writing.length === 1 ? 'post' : 'posts'} + {writing.length} {plural(writing.length, 'post')} } > diff --git a/site/app/routes/admin.tsx b/site/app/routes/admin.tsx index 4478d82..1258ca7 100644 --- a/site/app/routes/admin.tsx +++ b/site/app/routes/admin.tsx @@ -6,6 +6,7 @@ import { Confirm, Segmented, Sortable, Switch } from '../components/kit/controls import { Fact, PageHeader } from '../components/kit/page'; import { Page } from '../components/Shell'; import { hubContext, type Hub } from '../context'; +import { plural } from '../format'; import { useRoot } from '../hub'; import { useSpotlight } from '../motion/motion'; import { @@ -668,9 +669,15 @@ export default function AdminPage({ loaderData }: Route.ComponentProps) { facts={ <> - - - + + + } > diff --git a/site/app/routes/author.tsx b/site/app/routes/author.tsx index 0378305..bdaf3cb 100644 --- a/site/app/routes/author.tsx +++ b/site/app/routes/author.tsx @@ -6,6 +6,7 @@ import { ChangelogPanel } from '../components/kit/panels'; import { PostCard } from '../components/kit/posts'; import { Page } from '../components/Shell'; import { hubContext } from '../context'; +import { plural } from '../format'; import { useHub, useRoot } from '../hub'; import { authorFrom, viewerHandle } from '../pages/blog'; import { topicsFor } from '../topics'; @@ -102,7 +103,7 @@ export default function Author({ loaderData }: Route.ComponentProps) { } facts={ <> - + {words > 0 && } {posts[0] && } diff --git a/site/app/routes/blog-mine.tsx b/site/app/routes/blog-mine.tsx index 4352318..2573052 100644 --- a/site/app/routes/blog-mine.tsx +++ b/site/app/routes/blog-mine.tsx @@ -6,7 +6,7 @@ import { Confirm, Segmented } from '../components/kit/controls'; import { Fact, PageHeader } from '../components/kit/page'; import { Page } from '../components/Shell'; import { hubContext } from '../context'; -import { day } from '../format'; +import { day, plural } from '../format'; import { useHub, useRoot } from '../hub'; import { checkedForm, EDITOR_PATH, member, viewerHandle } from '../pages/blog'; import { postPath, STATUS_LABEL, type PostStatus } from '../posts'; @@ -106,7 +106,7 @@ export default function BlogMine({ loaderData }: Route.ComponentProps) { } facts={ <> - + diff --git a/site/app/routes/blog.tsx b/site/app/routes/blog.tsx index aa148fa..205ff44 100644 --- a/site/app/routes/blog.tsx +++ b/site/app/routes/blog.tsx @@ -10,6 +10,7 @@ import { PostCard, WritingStrip } from '../components/kit/posts'; import { SubscribePanel } from '../components/kit/subscribe'; import { Page } from '../components/Shell'; import { hubContext } from '../context'; +import { plural } from '../format'; import { useHub, useRoot } from '../hub'; import { viewerHandle } from '../pages/blog'; import { topicsFor } from '../topics'; @@ -132,8 +133,8 @@ export default function Blog({ loaderData }: Route.ComponentProps) { } facts={ <> - - + + {month > 0 && } } diff --git a/site/app/routes/changelog.tsx b/site/app/routes/changelog.tsx index 14dfa46..095e9f2 100644 --- a/site/app/routes/changelog.tsx +++ b/site/app/routes/changelog.tsx @@ -3,7 +3,7 @@ import { Panel } from '../components/kit/basics'; import { Fact, PageHeader } from '../components/kit/page'; import { Page } from '../components/Shell'; import Timeline from '../components/Timeline'; -import { day } from '../format'; +import { day, plural } from '../format'; import { useHub } from '../hub'; import { topicsFor } from '../topics'; @@ -24,9 +24,9 @@ export default function Changelog() { title="Changelog" facts={ <> - - - + + + {releases[0] && } } diff --git a/site/app/routes/epics.tsx b/site/app/routes/epics.tsx index 4c99c2d..0ddca11 100644 --- a/site/app/routes/epics.tsx +++ b/site/app/routes/epics.tsx @@ -8,7 +8,7 @@ import Status from '../components/Status'; import TagList from '../components/TagList'; import { Page } from '../components/Shell'; import { hubContext } from '../context'; -import { day } from '../format'; +import { day, plural } from '../format'; import { useHub } from '../hub'; import { useSpotlight } from '../motion/motion'; import { topicsFor } from '../topics'; @@ -44,8 +44,8 @@ function Epic({ epic, today }: { epic: Project; today: string }) {

{epic.summary}

- - + + {latest && } @@ -108,9 +108,9 @@ export default function Epics({ loaderData }: Route.ComponentProps) { title="Epics" facts={ <> - - - + + + } /> diff --git a/site/app/routes/member-changelog.tsx b/site/app/routes/member-changelog.tsx index 89e5184..20b265c 100644 --- a/site/app/routes/member-changelog.tsx +++ b/site/app/routes/member-changelog.tsx @@ -5,6 +5,7 @@ import { Page } from '../components/Shell'; import { Link } from 'react-router'; import type { Route } from './+types/member-changelog'; import Timeline from '../components/Timeline'; +import { plural } from '../format'; import { found, modelFrom, useHub } from '../hub'; import { changelogPath } from '../model'; import { topicsFor } from '../topics'; @@ -38,8 +39,8 @@ export default function MemberChangelog({ loaderData }: Route.ComponentProps) { actions={} facts={ <> - - + + {releases[0] && } } diff --git a/site/app/routes/members.tsx b/site/app/routes/members.tsx index 18f6938..4dd0523 100644 --- a/site/app/routes/members.tsx +++ b/site/app/routes/members.tsx @@ -6,6 +6,7 @@ import { Fact, loadRail, PageHeader, StandardRail } from '../components/kit/page import { Page } from '../components/Shell'; import TagList from '../components/TagList'; import { hubContext } from '../context'; +import { plural } from '../format'; import { useHub } from '../hub'; import { useOnline } from '../live'; import { useSpotlight, useTilt } from '../motion/motion'; @@ -61,14 +62,14 @@ function Card({ {member.bio &&

{member.bio}

}
- {projects} {projects === 1 ? 'project' : 'projects'} + {projects} {plural(projects, 'project')} - {releases} {releases === 1 ? 'release' : 'releases'} + {releases} {plural(releases, 'release')} {member.followers !== null && ( - {member.followers} followers + {member.followers} {plural(member.followers, 'follower')} )} {member.joined && ( @@ -112,10 +113,13 @@ export default function Members({ loaderData }: Route.ComponentProps) { title="Members" facts={ <> - - - - + + + + } /> diff --git a/site/app/routes/messages.tsx b/site/app/routes/messages.tsx index 0184b05..158cacf 100644 --- a/site/app/routes/messages.tsx +++ b/site/app/routes/messages.tsx @@ -100,13 +100,20 @@ function Leave() { ) : ( - +