Skip to content

Commit 849e425

Browse files
committed
make HighlightJSON a normal recursive component
1 parent f50cc3a commit 849e425

File tree

1 file changed

+56
-72
lines changed

1 file changed

+56
-72
lines changed

app/pages/system/AuditLog.tsx

Lines changed: 56 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import cn from 'classnames'
1212
import { differenceInMilliseconds } from 'date-fns'
1313
import { memo, useCallback, useMemo, useRef, useState } from 'react'
1414
import { match } from 'ts-pattern'
15+
import { JsonValue } from 'type-fest'
1516

1617
import { api } from '@oxide/api'
1718
import { Logs16Icon, Logs24Icon } from '@oxide/design-system/icons/react'
@@ -77,78 +78,63 @@ const Primitive = ({ value }: { value: null | boolean | number | string }) => (
7778
</span>
7879
)
7980

80-
// TODO: avoid converting JSON to string and then parsing again. just need a better memo
81-
82-
// silly faux highlighting
83-
// avoids unnecessary import of a library and all that overhead
84-
const HighlightJSON = memo(({ jsonString }: { jsonString: string }) => {
85-
const renderValue = (
86-
value: null | boolean | number | string | object,
87-
depth = 0
88-
): React.ReactNode => {
89-
if (
90-
value === null ||
91-
typeof value === 'boolean' ||
92-
typeof value === 'number' ||
93-
typeof value === 'string'
94-
) {
95-
return <Primitive value={value} />
96-
}
97-
98-
if (Array.isArray(value)) {
99-
if (value.length === 0) return <span className="text-quaternary">[]</span>
81+
// memo is important to avoid re-renders if the value hasn't changed. value
82+
// passed in must be referentially stable, which should generally be the case
83+
// with API responses
84+
const HighlightJSON = memo(({ json, depth = 0 }: { json: JsonValue; depth?: number }) => {
85+
if (json === undefined) return null
86+
87+
if (
88+
json === null ||
89+
typeof json === 'boolean' ||
90+
typeof json === 'number' ||
91+
typeof json === 'string'
92+
) {
93+
return <Primitive value={json} />
94+
}
10095

101-
return (
102-
<>
103-
<span className="text-quaternary">[</span>
104-
{'\n'}
105-
{value.map((item, index) => (
106-
<span key={index}>
107-
<Indent depth={depth + 1} />
108-
{renderValue(item, depth + 1)}
109-
{index < value.length - 1 && <span className="text-quaternary">,</span>}
110-
{'\n'}
111-
</span>
112-
))}
113-
<Indent depth={depth} />
114-
<span className="text-quaternary">]</span>
115-
</>
116-
)
117-
}
96+
if (Array.isArray(json)) {
97+
if (json.length === 0) return <span className="text-quaternary">[]</span>
98+
99+
return (
100+
<>
101+
<span className="text-quaternary">[</span>
102+
{'\n'}
103+
{json.map((item, index) => (
104+
<span key={index}>
105+
<Indent depth={depth + 1} />
106+
<HighlightJSON json={item} depth={depth + 1} />
107+
{index < json.length - 1 && <span className="text-quaternary">,</span>}
108+
{'\n'}
109+
</span>
110+
))}
111+
<Indent depth={depth} />
112+
<span className="text-quaternary">]</span>
113+
</>
114+
)
115+
}
118116

119-
if (typeof value === 'object') {
120-
const entries = Object.entries(value)
121-
if (entries.length === 0) return <span className="text-quaternary">{'{}'}</span>
117+
const entries = Object.entries(json)
118+
if (entries.length === 0) return <span className="text-quaternary">{'{}'}</span>
122119

123-
return (
124-
<>
125-
<span className="text-quaternary">{'{'}</span>
120+
return (
121+
<>
122+
<span className="text-quaternary">{'{'}</span>
123+
{'\n'}
124+
{entries.map(([key, val], index) => (
125+
<span key={key}>
126+
<Indent depth={depth + 1} />
127+
<span className="text-default">{key}</span>
128+
<span className="text-quaternary">: </span>
129+
<HighlightJSON json={val} depth={depth + 1} />
130+
{index < entries.length - 1 && <span className="text-quaternary">,</span>}
126131
{'\n'}
127-
{entries.map(([key, val], index) => (
128-
<span key={key}>
129-
<Indent depth={depth + 1} />
130-
<span className="text-default">{key}</span>
131-
<span className="text-quaternary">: </span>
132-
{renderValue(val, depth + 1)}
133-
{index < entries.length - 1 && <span className="text-quaternary">,</span>}
134-
{'\n'}
135-
</span>
136-
))}
137-
<Indent depth={depth} />
138-
<span className="text-quaternary">{'}'}</span>
139-
</>
140-
)
141-
}
142-
143-
return String(value)
144-
}
145-
146-
try {
147-
const parsed = JSON.parse(jsonString)
148-
return <>{renderValue(parsed)}</>
149-
} catch {
150-
return <>{jsonString}</>
151-
}
132+
</span>
133+
))}
134+
<Indent depth={depth} />
135+
<span className="text-quaternary">{'}'}</span>
136+
</>
137+
)
152138
})
153139

154140
// todo
@@ -254,9 +240,7 @@ export default function SiloAuditLogsPage() {
254240
const log = allItems[virtualRow.index]
255241
const isExpanded = expandedItem === virtualRow.index.toString()
256242
// only bother doing all this computation if we're the expanded row
257-
const jsonString = isExpanded
258-
? JSON.stringify(camelToSnakeJson(log), null, 2)
259-
: ''
243+
const json = isExpanded ? camelToSnakeJson(log) : undefined
260244

261245
const [userId, siloId] = match(log.actor)
262246
.with({ kind: 'silo_user' }, (actor) => [actor.siloUserId, actor.siloId])
@@ -336,7 +320,7 @@ export default function SiloAuditLogsPage() {
336320
{isExpanded && (
337321
<div className="h-72 border-t px-[var(--content-gutter)] py-3 border-secondary">
338322
<pre className="h-full overflow-auto border-l pl-4 text-mono-code border-secondary">
339-
<HighlightJSON jsonString={jsonString} />
323+
<HighlightJSON json={json as JsonValue} />
340324
</pre>
341325
</div>
342326
)}

0 commit comments

Comments
 (0)