Skip to content

Commit 4c6d9fd

Browse files
committed
Transforms working
1 parent b4ff158 commit 4c6d9fd

File tree

8 files changed

+69
-66
lines changed

8 files changed

+69
-66
lines changed

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Outline Viewer [<img alt="Outline Viewer logo" src="public/favicon.svg" height="90" align="right" />](https://opml.xml.style/)
22

3-
[![NodePing status](https://img.shields.io/nodeping/status/9rpjcz1i-8nzx-442d-8yzk-tm7l5zfhbllw?label=Current%20status)](https://nodeping.com/reports/checks/9rpjcz1i-8nzx-442d-8yzk-tm7l5zfhbllw)
4-
[![NodePing uptime](https://img.shields.io/nodeping/uptime/9rpjcz1i-8nzx-442d-8yzk-tm7l5zfhbllw?label=30-day%20uptime)](https://nodeping.com/reports/uptime/9rpjcz1i-8nzx-442d-8yzk-tm7l5zfhbllw)
3+
[![NodePing status](https://img.shields.io/nodeping/status/O0XVZ8N8-AB6K-4DZG-80XJ-5P7F7HP3DCMG?label=Current%20status)](https://nodeping.com/reports/checks/O0XVZ8N8-AB6K-4DZG-80XJ-5P7F7HP3DCMG)
4+
[![NodePing uptime](https://img.shields.io/nodeping/uptime/O0XVZ8N8-AB6K-4DZG-80XJ-5P7F7HP3DCMG?label=30-day%20uptime)](https://nodeping.com/reports/uptime/O0XVZ8N8-AB6K-4DZG-80XJ-5P7F7HP3DCMG)
55
[![deploy](https://github.com/fileformat/opml.xml.style/actions/workflows/gcr-deploy.yaml/badge.svg)](https://github.com/fileformat/opml.xml.style/actions/workflows/gcr-deploy.yaml)
66

7-
This is a graphical viewer for `opml.xml` files. Try it at [opml.xml.style](https://opml.xml.style/)!
7+
This is a graphical viewer for `opml.xml` outlines. Try it at [opml.xml.style](https://opml.xml.style/)!
88

99
## Running locally
1010

messages/de.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,15 @@
4646
"url": "URL"
4747
},
4848
"Transform": {
49-
"initialcap": "Erster Buchstabe groß, Interpunktion zu Leerzeichen",
49+
"initialcap": "Erster Buchstabe groß",
5050
"label": "Seitennamen-Transformation",
5151
"original": "Keine Änderung",
52-
"titlecase": "Titel-Schreibweise, Interpunktion zu Leerzeichen"
52+
"titlecase": "Titel-Schreibweise"
5353
},
5454
"ViewPage": {
5555
"feed_icon_alt": "RSS/Atom-Feed-Symbol",
5656
"home": "Startseite",
57-
"poweredby": "Angetrieben durch opml.xml.style",
57+
"poweredby": "Angetrieben durch xml.style",
5858
"title": "Gliederung"
5959
}
6060
}

messages/en.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,15 @@
4646
"url": "URL"
4747
},
4848
"Transform": {
49-
"initialcap": "First letter capitalized, punctation to spaces",
49+
"initialcap": "First letter capitalized",
5050
"label": "Page name transformation",
5151
"original": "No change",
52-
"titlecase": "Title cased, punctuation to spaces"
52+
"titlecase": "Title cased"
5353
},
5454
"ViewPage": {
5555
"feed_icon_alt": "RSS/Atom Feed Icon",
5656
"home": "Home page",
57-
"poweredby": "Powered by opml.xml.style",
57+
"poweredby": "Powered by xml.style",
5858
"title": "Outline"
5959
}
6060
}

messages/es.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,15 @@
4646
"url": "URL"
4747
},
4848
"Transform": {
49-
"initialcap": "Primera letra en mayúscula, puntuación a espacios",
49+
"initialcap": "Primera letra en mayúscula",
5050
"label": "Transformación del nombre de la página",
5151
"original": "Sin cambio",
52-
"titlecase": "Título en mayúsculas, puntuación a espacios"
52+
"titlecase": "Título en mayúsculas"
5353
},
5454
"ViewPage": {
5555
"feed_icon_alt": "Icono de alimentación RSS/Atom",
5656
"home": "Página de inicio",
57-
"poweredby": "Desarrollado por opml.xml.style",
57+
"poweredby": "Desarrollado por xml.style",
5858
"title": "Mapa del sitio"
5959
}
6060
}

messages/fr.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,15 @@
4646
"url": "URL"
4747
},
4848
"Transform": {
49-
"initialcap": "Première lettre en majuscule, ponctuation en espaces",
49+
"initialcap": "Première lettre en majuscule",
5050
"label": "Transformation du nom de la page",
5151
"original": "Aucun changement",
52-
"titlecase": "Majuscule à chaque mot, ponctuation en espaces"
52+
"titlecase": "Majuscule à chaque mot"
5353
},
5454
"ViewPage": {
5555
"feed_icon_alt": "Icône de flux RSS/Atom",
5656
"home": "Page d'accueil",
57-
"poweredby": "Propulsé par opml.xml.style",
57+
"poweredby": "Propulsé par xml.style",
5858
"title": "Plan du site"
5959
}
6060
}

src/app/view.html/page.tsx

+46-41
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { PoweredBy } from '@/components/PoweredBy';
1212
import { trackUsage } from '@/lib/usage';
1313
import { DEFAULT_SORT } from '@/components/SortSelect';
1414
import { loadOutline } from '@/lib/loadOutline';
15-
//import { DEFAULT_TRANSFORM } from '@/components/TransformSelect';
15+
import { DEFAULT_TRANSFORM, getTransform } from '@/components/TransformSelect';
1616

1717
export default async function View({
1818
searchParams,
@@ -44,81 +44,86 @@ export default async function View({
4444

4545
trackUsage(url_str);
4646

47-
const sme:OpmlData = await loadOutline(url_str);
48-
const items:TreeItem[] = sme.root.children;
47+
const sme: OpmlData = await loadOutline(url_str);
48+
const items: TreeItem[] = sme.root.children;
4949

5050
if (useRssStyle) {
5151
transform(items, addRssStyle);
5252
}
5353

5454
if (sort === 'name') {
55-
sortTreeName(items);
55+
sortTree(items, (a, b) => a.label.localeCompare(b.label));
5656
} else if (sort === 'dirfirst') {
57-
sortTreeDirFirst(items);
57+
sortTree(items, compareDirFirst);
58+
} else if (sort === 'url') {
59+
sortTree(items, compareUrl);
5860
}
5961

62+
const transformer = getTransform(getFirst(urlParams['transform'], DEFAULT_TRANSFORM));
63+
if (transformer) {
64+
transform(items, transformer);
65+
}
66+
67+
6068
const title = sme.title || customTitle;
6169
if (!sme.success) {
6270
showDebug = true;
6371
}
6472

6573
return (
6674
<>
67-
<Container maxWidth={false} disableGutters={true} sx={{ minHeight: '100vh' }}>
75+
<Container maxWidth={false} disableGutters={true} sx={{ minHeight: '100vh' }}>
6876
<NavBar debug={showDebug} exit={showExit} language={showLanguage} messages={sme.messages} mode={showMode} title={title} returnUrl={returnUrl} />
69-
<Container
70-
maxWidth="md"
71-
disableGutters={true}
72-
sx={{ alignItems: "center", display: "flex", flexDirection: "column", justifyContent: "top",minHeight: '100vh' }}
73-
>
74-
<Box
75-
sx={{
76-
display: 'flex',
77-
flexDirection: 'column',
78-
width: '100%',
79-
}}
77+
<Container
78+
maxWidth="md"
79+
disableGutters={true}
80+
sx={{ alignItems: "center", display: "flex", flexDirection: "column", justifyContent: "top", minHeight: '100vh' }}
8081
>
81-
{sme.success || items.length ? <OpmlTreeView items={items} /> : <h1>Failed to load outline</h1>}
82-
</Box>
83-
<PoweredBy />
82+
<Box
83+
sx={{
84+
display: 'flex',
85+
flexDirection: 'column',
86+
width: '100%',
87+
}}
88+
>
89+
{sme.success || items.length ? <OpmlTreeView items={items} /> : <h1>Failed to load outline</h1>}
90+
</Box>
91+
<PoweredBy />
92+
</Container>
8493
</Container>
85-
</Container>
8694
</>
8795

8896
);
8997
}
9098

91-
function sortTreeName(items: TreeItem[]) {
99+
100+
101+
function sortTree(items: TreeItem[], sortfn: (a: TreeItem, b: TreeItem) => number) {
92102
if (items.length == 0) {
93103
return;
94104
}
95105
if (items.length > 1) {
96-
items.sort((a, b) => a.label.localeCompare(b.label));
106+
items.sort(sortfn);
97107
}
98108

99109
for (const item of items) {
100-
sortTreeName(item.children);
110+
sortTree(item.children, sortfn);
101111
}
102112
}
103113

104-
function sortTreeDirFirst(items: TreeItem[]) {
105-
if (items.length == 0) {
106-
return;
107-
}
108-
if (items.length > 1) {
109-
items.sort((a, b) => {
110-
if (a.children.length > 0 && b.children.length == 0) {
111-
return -1;
112-
} else if (a.children.length == 0 && b.children.length > 0) {
113-
return 1;
114-
}
115-
return a.label.localeCompare(b.label)
116-
});
114+
function compareDirFirst(a: TreeItem, b: TreeItem) {
115+
if (a.children.length > 0 && b.children.length == 0) {
116+
return -1;
117+
} else if (a.children.length == 0 && b.children.length > 0) {
118+
return 1;
117119
}
120+
return a.label.localeCompare(b.label)
121+
}
118122

119-
for (const item of items) {
120-
sortTreeDirFirst(item.children);
121-
}
123+
function compareUrl(a: TreeItem, b: TreeItem) {
124+
const aUrl = a.htmlUrl || a.xmlUrl || a.label;
125+
const bUrl = b.htmlUrl || b.xmlUrl || b.label;
126+
return aUrl.localeCompare(bUrl);
122127
}
123128

124129
function addRssStyle(item: TreeItem) {
@@ -127,7 +132,7 @@ function addRssStyle(item: TreeItem) {
127132
}
128133
}
129134

130-
function transform(items: TreeItem[], transformer: (item:TreeItem) => void) {
135+
function transform(items: TreeItem[], transformer: (item: TreeItem) => void) {
131136
for (const item of items) {
132137
transformer(item);
133138
if (item.children.length > 0) {

src/components/ProTip.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export default function ProTip() {
1919
<Typography sx={{ mt: 6, mb: 3, textAlign: 'center', color: 'text.secondary' }}>
2020
{t.rich('tip', {
2121
Icon: () => <LightBulbIcon sx={{ mr: 1, verticalAlign: 'middle' }} />,
22-
Link: (chunks) => <NextLink href="/integration.html">{chunks}</NextLink>,
22+
Link: (chunks) => <NextLink href="https://www.xml.style/opml/">{chunks}</NextLink>,
2323
})}
2424
</Typography>
2525
);

src/components/TransformSelect.tsx

+7-9
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,33 @@ import FormControl from '@mui/material/FormControl';
44
import Select from '@mui/material/Select';
55
import { useTranslations } from 'next-intl';
66
import { UseFormRegisterReturn } from 'react-hook-form';
7+
import { TreeItem } from '@/lib/types';
78

89

910
type Transform = (typeof transforms)[number]
1011

1112
const transforms = ['original', 'initialcap', 'titlecase'] as const;
1213

13-
//LATER: maybe also \p{Symbol}?
14-
15-
function initialCap(s: string): string {
16-
s = s.replace(/[^\p{Letter}\p{Number}]+/gu, ' ');
17-
return s.length == 0 ? s : s.charAt(0).toUpperCase() + s.slice(1);
14+
function initialCap(item: TreeItem): void {
15+
item.label = item.label.length == 0 ? item.label : item.label.charAt(0).toUpperCase() + item.label.slice(1);
1816
}
1917

2018
function titleCaseWord(s: string): string {
2119
return s.length == 0 ? s : s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
2220
}
2321

24-
function titleCase(s: string): string {
25-
return s.replace(/[^\p{Letter}\p{Number}]+/gu, ' ').split(/\s+/).map(titleCaseWord).join(' ');
22+
function titleCase(item: TreeItem): void {
23+
item.label = item.label.split(/\s+/).map(titleCaseWord).join(' ');
2624
}
2725

2826
const DEFAULT_TRANSFORM:Transform = 'initialcap';
2927

30-
const transformMap = new Map<string, (s: string) => string>([
28+
const transformMap = new Map<string, (item:TreeItem) => void>([
3129
['initialcap', initialCap],
3230
['titlecase', titleCase],
3331
]);
3432

35-
function getTransform(value: string): ((s: string) => string) | null {
33+
function getTransform(value: string): ((item: TreeItem) => void) | null {
3634
return transformMap.get(value) || null;
3735
}
3836

0 commit comments

Comments
 (0)