Skip to content

Commit

Permalink
Only show the translations for a subset of selected languages
Browse files Browse the repository at this point in the history
  • Loading branch information
lukbukkit committed Sep 4, 2023
1 parent 66c642f commit e9954ba
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 94 deletions.
7 changes: 6 additions & 1 deletion resources/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { AppLoader } from './AppLoader'
import { ErrorMessage } from './ErrorMessage'
import { AppFooter } from './AppFooter'
import { IconActions } from './IconActions'
import { SelectHighlights } from './IconActionHighlight'
import 'inter-ui/inter.css'
import './App.css'

Expand All @@ -26,7 +27,11 @@ const GeistApp = () => {
<Text h1>tldr translation progress</Text>
</Page.Header>
<Page.Content>
<DataFetcher loading={<AppLoader />} error={<ErrorMessage />}>
<DataFetcher
loading={<AppLoader />}
error={<ErrorMessage />}
selection={<SelectHighlights />}
>
<IconActions />
<DataTable />
</DataFetcher>
Expand Down
51 changes: 38 additions & 13 deletions resources/src/Data.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { ReactElement, createContext, useEffect, useState, PropsWithChildren } from 'react'
import {
createContext,
PropsWithChildren,
ReactElement,
useEffect,
useState,
useTransition,
} from 'react'

// https://reactjs.org/docs/faq-ajax.html
// https://reactjs.org/docs/hooks-reference.html#usecontext
Expand Down Expand Up @@ -32,13 +39,21 @@ const DataContext = createContext<{
error: string | null
highlighted: Set<Language>
setHighlighted: (languages: Language[]) => void
}>({ data: null, error: null, highlighted: new Set(), setHighlighted: () => {} })

const DataFetcher = (props: PropsWithChildren<{ error: ReactElement; loading: ReactElement }>) => {
}>({
data: null,
error: null,
highlighted: new Set(['en']),
setHighlighted: () => {},
})

const DataFetcher = (
props: PropsWithChildren<{ error: ReactElement; loading: ReactElement; selection: ReactElement }>,
) => {
const [error, setError] = useState<string | null>(null)
const [isLoaded, setIsLoaded] = useState<boolean>(false)
const [data, setData] = useState<TranslationData | null>(null)
const [highlighted, setHighlighted] = useState<Set<Language>>(new Set())
const [highlighted, setHighlighted] = useState<Set<Language>>(new Set(['en']))
const [isPending, startTransition] = useTransition()
const webStorageHighlightedKey = 'language-highlighted'

// Fetch the data.json file
Expand All @@ -53,12 +68,16 @@ const DataFetcher = (props: PropsWithChildren<{ error: ReactElement; loading: Re
})
.then(
(result) => {
setData(result as TranslationData)
setIsLoaded(true)
startTransition(() => {
setData(result as TranslationData)
setIsLoaded(true)
})
},
(error) => {
setError(error.toString())
setIsLoaded(true)
startTransition(() => {
setError(error.toString())
setIsLoaded(true)
})
},
)
}, [])
Expand All @@ -68,14 +87,16 @@ const DataFetcher = (props: PropsWithChildren<{ error: ReactElement; loading: Re
useEffect(() => {
const stored = localStorage.getItem(webStorageHighlightedKey)
if (stored) {
setHighlighted(new Set(JSON.parse(stored)))
let languages: Set<string> = new Set(JSON.parse(stored))
languages.add('en')
startTransition(() => setHighlighted(new Set(languages)))
}
}, [])

const setHighlightedExternal = (languages: Language[]) => {
// Convert the array to a set for better performance and broadcast the change
// See: https://stackoverflow.com/a/57277566/4106848
setHighlighted(new Set(languages))
startTransition(() => setHighlighted(new Set(languages)))
// Persist the user selection of highlighted columns
localStorage.setItem(webStorageHighlightedKey, JSON.stringify(Array.from(languages)))
}
Expand All @@ -90,7 +111,7 @@ const DataFetcher = (props: PropsWithChildren<{ error: ReactElement; loading: Re
return <DataContext.Provider value={provided}>{props.error}</DataContext.Provider>
}

if (!isLoaded) {
if (!isLoaded || isPending) {
return props.loading
}

Expand All @@ -100,7 +121,11 @@ const DataFetcher = (props: PropsWithChildren<{ error: ReactElement; loading: Re
highlighted: highlighted,
setHighlighted: setHighlightedExternal,
}
return <DataContext.Provider value={provided}>{props.children}</DataContext.Provider>
return (
<DataContext.Provider value={provided}>
{highlighted.size < 2 ? props.selection : props.children}
</DataContext.Provider>
)
}

export { DataFetcher, DataContext, TranslationStatus }
Expand Down
56 changes: 35 additions & 21 deletions resources/src/IconActionHighlight.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useContext, useState, useEffect } from 'react'
import { Checkbox, Grid, Modal, Tooltip, useModal } from '@geist-ui/core'
import { Bookmark } from '@geist-ui/icons'
import { useContext, useState } from 'react'
import { Button, Checkbox, Grid, Modal, Text, Tooltip, useModal } from '@geist-ui/core'
import { MessageCircle, Save } from '@geist-ui/icons'
import { useEscClose } from './useEscClose'
import { DataContext, Language } from './Data'

Expand All @@ -12,7 +12,9 @@ const HighlightCheckboxes = (props: {

const checkboxes = data!.languages.map((language) => (
<Grid key={language} xs={6}>
<Checkbox value={language}>{language}</Checkbox>
<Checkbox value={language} disabled={language === 'en'}>
{language}
</Checkbox>
</Grid>
))

Expand All @@ -25,6 +27,28 @@ const HighlightCheckboxes = (props: {
)
}

const SelectHighlights = (props: {}) => {
// Columns which are being highlighted
const { highlighted, setHighlighted } = useContext(DataContext)
// We only want to apply the highlight selection once it has been confirmed, so we create a temporary store
const [tmpHighlighted, setTmpHighlighted] = useState(Array.from(highlighted))

return (
<>
<Text h3>Select languages to show</Text>
<br />
<HighlightCheckboxes
highlighted={tmpHighlighted}
onChange={(selected) => setTmpHighlighted(selected)}
/>
<br />
<Button auto type="secondary" icon={<Save />} onClick={() => setHighlighted(tmpHighlighted)}>
Continue
</Button>
</>
)
}

const IconActionHighlight = (props: { side?: boolean }) => {
const { visible, setVisible, bindings } = useModal()
useEscClose(visible, setVisible)
Expand All @@ -33,23 +57,13 @@ const IconActionHighlight = (props: { side?: boolean }) => {
const { highlighted, setHighlighted } = useContext(DataContext)
// We only want to apply the highlight selection once it has been confirmed, so we create a temporary store
const [tmpHighlighted, setTmpHighlighted] = useState(Array.from(highlighted))
// An extra state to begin saving the selection and applying it to the table
const [saving, setSaving] = useState(false)

useEffect(() => {
if (saving) {
setHighlighted(tmpHighlighted)
setVisible(false)
setSaving(false)
}
}, [saving, setHighlighted, setVisible, tmpHighlighted])

const placement = props.side ? 'left' : 'top'

return (
<>
<Tooltip text="Highlight" enterDelay={0} placement={placement}>
<Bookmark
<Tooltip text="Languages" enterDelay={0} placement={placement}>
<MessageCircle
className="cursor-pointer"
size={28}
onClick={() => {
Expand All @@ -60,8 +74,8 @@ const IconActionHighlight = (props: { side?: boolean }) => {
/>
</Tooltip>
<Modal {...bindings}>
<Modal.Title>Highlight columns</Modal.Title>
<Modal.Subtitle>Choose columns to highlight</Modal.Subtitle>
<Modal.Title>Select Languages</Modal.Title>
<Modal.Subtitle>Choose languages to display</Modal.Subtitle>
<Modal.Content>
<HighlightCheckboxes
highlighted={tmpHighlighted}
Expand All @@ -78,9 +92,9 @@ const IconActionHighlight = (props: { side?: boolean }) => {
</Modal.Action>
<Modal.Action
onClick={() => {
setSaving(true)
setVisible(false)
setHighlighted(tmpHighlighted)
}}
loading={saving}
>
Save
</Modal.Action>
Expand All @@ -89,4 +103,4 @@ const IconActionHighlight = (props: { side?: boolean }) => {
)
}

export { IconActionHighlight }
export { IconActionHighlight, SelectHighlights }
113 changes: 54 additions & 59 deletions resources/src/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,20 @@ const DataTableHeader = () => {

// We're applying the sticky class to each <th>, because Chrome does not support sticky on <thead> and <tr>
// https://bugs.chromium.org/p/chromium/issues/detail?id=702927
const languageRows = data?.languages.map((lang) => {
let classNames = 'vertical-padding sticky bg-white-transparent'
if (lang.length > 3) {
classNames += ' small-font'
}
if (highlighted.has(lang)) {
classNames += ' highlighted'
}
const languageRows = data?.languages
.filter((lang) => highlighted.has(lang))
.map((lang) => {
let classNames = 'vertical-padding sticky bg-white-transparent'
if (lang.length > 3) {
classNames += ' small-font'
}

return (
<th key={lang} className={classNames}>
{lang}
</th>
)
})
return (
<th key={lang} className={classNames}>
{lang}
</th>
)
})

return (
<thead>
Expand Down Expand Up @@ -58,19 +57,17 @@ const DataTableOSHeader = (props: { os: OperatingSystem }) => {
const { data, highlighted } = useContext(DataContext)
const osProgress = data!.entries[props.os].progress

const percentages = data!.languages.map((lang) => {
let classNames = 'vertical-padding small-font'
const percentages = data!.languages
.filter((lang) => highlighted.has(lang))
.map((lang) => {
let classNames = 'vertical-padding small-font'

if (highlighted.has(lang)) {
classNames += ' highlighted'
}

return (
<td key={lang} className={classNames}>
{osProgress[lang]}%
</td>
)
})
return (
<td key={lang} className={classNames}>
{osProgress[lang]}%
</td>
)
})

return (
<tr className="background-blue" id={props.os}>
Expand Down Expand Up @@ -105,41 +102,39 @@ const DataTableOSPageRow = (props: { os: OperatingSystem; pageName: string }) =>
}

// Pick symbols from: https://rsms.me/inter/#charset
const cells = data!.languages.map((lang) => {
let classNames = 'cursor-pointer'
let onClick = null
let symbol = '?'

if (lang in pageData.status) {
const status = pageData.status[lang]
switch (status) {
case TranslationStatus.Translated:
classNames += ' background-green'
onClick = () => handleClick(FileAction.View, lang)
symbol = '✓'
break
case TranslationStatus.Outdated:
classNames += ' background-yellow'
onClick = () => handleClick(FileAction.View, lang)
symbol = '◇'
break
const cells = data!.languages
.filter((lang) => highlighted.has(lang))
.map((lang) => {
let classNames = 'cursor-pointer'
let onClick = null
let symbol = '?'

if (lang in pageData.status) {
const status = pageData.status[lang]
switch (status) {
case TranslationStatus.Translated:
classNames += ' background-green'
onClick = () => handleClick(FileAction.View, lang)
symbol = '✓'
break
case TranslationStatus.Outdated:
classNames += ' background-yellow'
onClick = () => handleClick(FileAction.View, lang)
symbol = '◇'
break
}
} else {
classNames += ' background-red'
onClick = () => handleClick(FileAction.Create, lang)
symbol = '✗'
}
} else {
classNames += ' background-red'
onClick = () => handleClick(FileAction.Create, lang)
symbol = '✗'
}

if (highlighted.has(lang)) {
classNames += ' highlighted'
}

return (
<td key={lang} className={classNames} onClick={onClick}>
{symbol}
</td>
)
})
return (
<td key={lang} className={classNames} onClick={onClick}>
{symbol}
</td>
)
})

return (
<tr>
Expand Down

0 comments on commit e9954ba

Please sign in to comment.