Skip to content

Commit 517692b

Browse files
Load /weekly from real logseq/logseq pull requests.
The page lists weeks newest first, defaults to the latest week, and uses a native select to switch weeks. Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
1 parent dfaad58 commit 517692b

4 files changed

Lines changed: 207 additions & 126 deletions

File tree

‎src/pages/Landing/LandingFooterNav.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ export function LandingFooterNav () {
228228

229229
const links = (
230230
<div className="links flex flex-col justify-center">
231-
<p className="flex space-x-4 text-xs text-gray-300/90 pb-1">
231+
<p className="flex flex-wrap gap-x-4 gap-y-1 text-xs text-gray-300/90 pb-1">
232232
<a href="https://logseq.io/p/NX4mc_ggEV" target="_blank" rel="noreferrer">Roadmap</a>
233233
<Link to={"/weekly"}>Weekly</Link>
234234
<Link to={"/privacy-policy"}>Privacy</Link>

‎src/pages/Weekly/README.md‎

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,31 @@
11
# Weekly highlights
22

3-
`weekly.json` is the only file to edit when refreshing [/weekly](https://logseq.com/weekly). The page reads that file. It does not need a UI change.
3+
`weekly.json` is the only file to edit when refreshing [/weekly](https://logseq.com/weekly).
44

5-
Replace the file each week. Keep the list short: about 5 to 10 items, most important first.
5+
Source is merged pull requests in [`logseq/logseq`](https://github.com/logseq/logseq) only. Do not include other Logseq repos, issues, or invented PR numbers.
66

7-
## Schema
7+
## Shape
8+
9+
`weeks` is an array, newest first. Keep about three months at most. The page defaults to the latest week and shows at most 10 items, in the order written.
10+
11+
Each week:
812

913
| Field | Required | Notes |
1014
| --- | --- | --- |
11-
| `_note` | no | Marker for sample data. While it is present, the page shows a short placeholder hint. Remove it when the items are real. |
12-
| `weekStart` | yes | Inclusive start date, `YYYY-MM-DD`. |
13-
| `weekEnd` | yes | Inclusive end date, `YYYY-MM-DD`. |
15+
| `weekStart` | yes | Inclusive `YYYY-MM-DD`. Also the dropdown value. |
16+
| `weekEnd` | yes | Inclusive `YYYY-MM-DD`. |
1417
| `updatedAt` | yes | ISO-8601 datetime for the “Updated …” line. |
15-
| `items` | yes | Ordered array. The page shows the first 10 entries and does not reorder them. |
18+
| `items` | yes | Ordered array, max 10 shown. |
1619

1720
Each item:
1821

1922
| Field | Required | Notes |
2023
| --- | --- | --- |
2124
| `title` | yes | Short. |
2225
| `summary` | no | One sentence. |
23-
| `link` | no | A real PR, issue, or blog URL. Use `#` or omit the field for a placeholder. Do not invent GitHub numbers. |
24-
| `tags` | no | Short labels, for example `Sync` or `Mobile`. |
25-
| `category` | no | Optional single label. Shown with `tags`. |
26-
| `priority` | no | Optional number for whoever prepares the file. Display order is the array order, not this field. |
26+
| `link` | no | One real `logseq/logseq` pull request URL. |
27+
| `tags` | no | Optional. Not shown as badges. |
28+
| `category` | no | Optional. Not shown as badges. |
29+
| `priority` | no | Optional metadata. Display order is array order. |
2730

28-
Fewer than 5 items is fine. The page renders whatever is there. An empty `items` array shows a quiet empty state.
31+
A week with fewer than 5 items still renders as-is.

‎src/pages/Weekly/index.tsx‎

Lines changed: 114 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useSearchParams } from 'react-router-dom'
12
import { ArrowSquareOut } from '@phosphor-icons/react'
23
import { LandingFooterNav } from '../Landing'
34
import weeklyData from './weekly.json'
@@ -13,8 +14,15 @@ type WeeklyItem = {
1314
priority?: number
1415
}
1516

17+
type WeeklyWeek = {
18+
weekStart?: string
19+
weekEnd?: string
20+
updatedAt?: string
21+
items?: WeeklyItem[]
22+
}
23+
1624
type WeeklyFile = {
17-
_note?: string
25+
weeks?: WeeklyWeek[]
1826
weekStart?: string
1927
weekEnd?: string
2028
updatedAt?: string
@@ -61,7 +69,7 @@ function formatWeekRange (startIso?: string, endIso?: string): string {
6169

6270
if (start) return monthDayYear.format(start)
6371
if (end) return monthDayYear.format(end)
64-
return ''
72+
return 'Unknown week'
6573
}
6674

6775
function formatUpdated (iso?: string): string {
@@ -76,107 +84,144 @@ function formatUpdated (iso?: string): string {
7684
}).format(date)
7785
}
7886

87+
function weekKey (week: WeeklyWeek): string {
88+
return (week.weekStart || week.weekEnd || '').slice(0, 10)
89+
}
90+
91+
function weeksFromFile (file: WeeklyFile): WeeklyWeek[] {
92+
const source = Array.isArray(file.weeks)
93+
? file.weeks
94+
: (file.weekStart || file.weekEnd || file.items)
95+
? [file]
96+
: []
97+
98+
return source
99+
.filter(week => week && (week.weekStart || week.weekEnd))
100+
.slice()
101+
.sort((a, b) => weekKey(b).localeCompare(weekKey(a)))
102+
}
103+
79104
function visibleHref (link?: string): string | null {
80105
if (!link) return null
81106
const href = link.trim()
82107
if (!href || href === '#') return null
83108
return href
84109
}
85110

86-
function labelsFor (item: WeeklyItem): string[] {
87-
const labels: string[] = []
88-
if (item.category?.trim()) labels.push(item.category.trim())
89-
for (const tag of item.tags || []) {
90-
const text = tag?.trim()
91-
if (text && !labels.includes(text)) labels.push(text)
92-
}
93-
return labels
111+
function itemsFor (week?: WeeklyWeek): WeeklyItem[] {
112+
if (!week || !Array.isArray(week.items)) return []
113+
return week.items
114+
.filter(item => item && typeof item.title === 'string' && item.title.trim())
115+
.slice(0, MAX_ITEMS)
94116
}
95117

96118
function WeeklyItemRow (props: { item: WeeklyItem }) {
97-
const { item } = props
98-
const href = visibleHref(item.link)
119+
const title = props.item.title?.trim() || ''
120+
const summary = props.item.summary?.trim()
121+
const href = visibleHref(props.item.link)
99122
const external = !!href && /^https?:\/\//i.test(href)
100-
const labels = labelsFor(item)
101-
const title = item.title?.trim() || ''
102-
103-
const titleNode = href ? (
104-
<a
105-
href={href}
106-
className="inline-flex items-center gap-2 hover:text-white"
107-
{...(external ? { target: '_blank', rel: 'noreferrer' } : {})}
108-
>
109-
{title}
110-
{external && (
111-
<>
112-
<ArrowSquareOut size={16} className="opacity-50" aria-hidden="true"/>
113-
<span className="sr-only"> (opens a new window)</span>
114-
</>
123+
124+
const body = (
125+
<>
126+
<span className="block text-lg font-medium leading-snug text-gray-100">
127+
{title}
128+
{external && (
129+
<ArrowSquareOut
130+
size={15}
131+
weight="bold"
132+
className="ml-1.5 inline-block translate-y-px opacity-40"
133+
aria-hidden="true"
134+
/>
135+
)}
136+
</span>
137+
{summary && (
138+
<span className="mt-1.5 block text-base font-normal leading-relaxed text-logseq-50/80">
139+
{summary}
140+
</span>
115141
)}
116-
</a>
117-
) : title
142+
</>
143+
)
144+
145+
if (!href) {
146+
return <li className="border-t border-white/10 py-5">{body}</li>
147+
}
118148

119149
return (
120-
<li className="py-8 first:pt-0">
121-
{labels.length > 0 && (
122-
<p className="text-xs tracking-wide text-logseq-100/80">
123-
{labels.join(' · ')}
124-
</p>
125-
)}
126-
<h2 className="mt-2 text-xl font-medium tracking-wide text-gray-100 sm:text-2xl">
127-
{titleNode}
128-
</h2>
129-
{item.summary?.trim() && (
130-
<p className="mt-2 max-w-xl text-base leading-relaxed text-logseq-50/80">
131-
{item.summary.trim()}
132-
</p>
133-
)}
150+
<li className="border-t border-white/10">
151+
<a
152+
href={href}
153+
className="block py-5 hover:text-white"
154+
{...(external ? { target: '_blank', rel: 'noreferrer' } : {})}
155+
>
156+
{body}
157+
{external && <span className="sr-only"> (opens a new window)</span>}
158+
</a>
134159
</li>
135160
)
136161
}
137162

138163
export function WeeklyPage () {
139-
const items = (Array.isArray(data.items) ? data.items : [])
140-
.filter(item => item && typeof item.title === 'string' && item.title.trim())
141-
.slice(0, MAX_ITEMS)
142-
const range = formatWeekRange(data.weekStart, data.weekEnd)
143-
const updated = formatUpdated(data.updatedAt)
164+
const [searchParams, setSearchParams] = useSearchParams()
165+
const weeks = weeksFromFile(data)
166+
const requested = searchParams.get('week') || ''
167+
const selected = weeks.find(week => weekKey(week) === requested) || weeks[0]
168+
const items = itemsFor(selected)
169+
const range = selected ? formatWeekRange(selected.weekStart, selected.weekEnd) : ''
170+
const updated = formatUpdated(selected?.updatedAt)
171+
172+
const onWeekChange = (key: string) => {
173+
const next = new URLSearchParams(searchParams)
174+
if (!key || key === weekKey(weeks[0] || {})) next.delete('week')
175+
else next.set('week', key)
176+
setSearchParams(next, { replace: true })
177+
}
144178

145179
return (
146180
<div className="app-page pt-20">
147-
<article className="page-inner w-full px-6 pb-4 sm:px-10">
148-
<div className="mx-auto max-w-2xl pb-16 pt-10 sm:pb-24 sm:pt-20">
149-
<p className="text-sm tracking-wide text-logseq-100/80">Weekly</p>
150-
<h1 className="mt-3 text-4xl font-semibold tracking-wide text-logseq-50 sm:text-5xl sm:leading-tight">
151-
What we worked on
181+
<article className="page-inner w-full px-5 pb-8 sm:px-10">
182+
<div className="mx-auto max-w-2xl pb-12 pt-8 sm:pb-20 sm:pt-16">
183+
<h1 className="text-3xl font-semibold tracking-wide text-logseq-50">
184+
Weekly
152185
</h1>
153-
<p className="mt-6 max-w-xl text-lg leading-relaxed text-logseq-50/80">
154-
A short list from the Logseq team. Not everything — just the few things worth knowing.
155-
</p>
156-
157-
{(range || updated) && (
158-
<p className="mt-8 text-sm text-logseq-100/80">
159-
{range && <span>{range}</span>}
160-
{range && updated && <span className="px-2 opacity-50">·</span>}
161-
{updated && <span>Updated {updated}</span>}
162-
</p>
186+
187+
{weeks.length > 0 && (
188+
<div className="mt-8">
189+
<label className="block text-sm text-logseq-100/80" htmlFor="weekly-week">
190+
Week
191+
</label>
192+
<select
193+
id="weekly-week"
194+
className="mt-2 w-full max-w-full rounded-md border border-logseq-500/70 bg-logseq-800 px-3 py-3 text-base text-gray-100 sm:max-w-sm [color-scheme:dark]"
195+
value={selected ? weekKey(selected) : ''}
196+
onChange={(event) => onWeekChange(event.target.value)}
197+
>
198+
{weeks.map(week => {
199+
const key = weekKey(week)
200+
return (
201+
<option key={key} value={key}>
202+
{formatWeekRange(week.weekStart, week.weekEnd)}
203+
</option>
204+
)
205+
})}
206+
</select>
207+
</div>
163208
)}
164209

165-
{data._note && (
166-
<p className="mt-6 max-w-xl text-sm leading-relaxed text-logseq-100/70">
167-
Sample page. These notes are placeholders, not a real week of work.
210+
{updated && (
211+
<p className="mt-4 text-sm text-logseq-100/70">
212+
Updated {updated}
168213
</p>
169214
)}
170215

171216
{items.length > 0 ? (
172-
<ul className="mt-12 divide-y divide-logseq-600/60">
217+
<ul className="mt-8 border-b border-white/10">
173218
{items.map((item, index) => (
174219
<WeeklyItemRow item={item} key={`${item.title}-${index}`}/>
175220
))}
176221
</ul>
177222
) : (
178-
<p className="mt-14 text-base text-logseq-50/70">
179-
Nothing highlighted this week. Check back after the next update.
223+
<p className="mt-10 text-base text-logseq-50/75">
224+
{range ? `Nothing from ${range}.` : 'Nothing here yet.'}
180225
</p>
181226
)}
182227
</div>

0 commit comments

Comments
 (0)