Use stable textual ids for streams

This commit is contained in:
Max Goodhart
2020-06-16 17:19:30 -07:00
parent c95472f4ad
commit b2915b3072
5 changed files with 41 additions and 17 deletions

View File

@@ -12,6 +12,7 @@
] ]
], ],
"plugins": [ "plugins": [
"@babel/plugin-proposal-optional-chaining",
"babel-plugin-styled-components", "babel-plugin-styled-components",
[ [
"@babel/plugin-transform-react-jsx", "@babel/plugin-transform-react-jsx",

View File

@@ -24,6 +24,7 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.10.2", "@babel/core": "^7.10.2",
"@babel/plugin-proposal-optional-chaining": "^7.10.1",
"@babel/plugin-transform-react-jsx": "^7.10.1", "@babel/plugin-transform-react-jsx": "^7.10.1",
"@babel/preset-env": "^7.10.2", "@babel/preset-env": "^7.10.2",
"@svgr/webpack": "^5.4.0", "@svgr/webpack": "^5.4.0",

View File

@@ -19,19 +19,19 @@ function App() {
}, []) }, [])
const handleSetSpace = useCallback( const handleSetSpace = useCallback(
(idx, value) => { (idx, id) => {
const newSpaceIdxMap = new Map(spaceIdxMap) const newSpaceIdxMap = new Map(spaceIdxMap)
if (value !== undefined) { if (id !== undefined) {
newSpaceIdxMap.set(idx, value) newSpaceIdxMap.set(idx, id)
} else { } else {
newSpaceIdxMap.delete(idx) newSpaceIdxMap.delete(idx)
} }
setSpaceIdxMap(newSpaceIdxMap) setSpaceIdxMap(newSpaceIdxMap)
const newSpaceURLMap = new Map( const newSpaceURLMap = new Map(
Array.from(newSpaceIdxMap, ([spaceIdx, dataIdx]) => [ Array.from(newSpaceIdxMap, ([spaceIdx, streamId]) => [
spaceIdx, spaceIdx,
streamData[dataIdx].Link, streamData.find((d) => d._id === streamId)?.Link,
]), ]),
) )
ipcRenderer.send('set-videos', newSpaceURLMap) ipcRenderer.send('set-videos', newSpaceURLMap)
@@ -71,17 +71,17 @@ function App() {
</div> </div>
<div> <div>
{streamData {streamData
? streamData.map((row, idx) => <StreamLine idx={idx} row={row} />) ? streamData.map((row) => <StreamLine id={row._id} row={row} />)
: 'loading...'} : 'loading...'}
</div> </div>
</div> </div>
) )
} }
function StreamLine({ idx, row: { Source, Title, Link, Notes } }) { function StreamLine({ id, row: { Source, Title, Link, Notes } }) {
return ( return (
<StyledStreamLine> <StyledStreamLine>
<StyledIdx>{idx}</StyledIdx> <StyledId>{id}</StyledId>
<div> <div>
<strong>{Source}</strong> <a href={Link}>{Title || Link}</a> {Notes} <strong>{Source}</strong> <a href={Link}>{Title || Link}</a> {Notes}
</div> </div>
@@ -99,11 +99,7 @@ function GridInput({
const handleChange = useCallback( const handleChange = useCallback(
(ev) => { (ev) => {
const { name, value } = ev.target const { name, value } = ev.target
const newValue = value ? Number(value) : NaN onChangeSpace(Number(name), value)
onChangeSpace(
Number(name),
Number.isFinite(newValue) ? newValue : undefined,
)
}, },
[onChangeSpace], [onChangeSpace],
) )
@@ -185,14 +181,14 @@ const StyledGridInput = styled.input`
} }
` `
const StyledIdx = styled.div` const StyledId = styled.div`
flex-shrink: 0; flex-shrink: 0;
margin-right: 5px; margin-right: 5px;
background: #333; background: #333;
color: white; color: white;
padding: 3px; padding: 3px;
border-radius: 5px; border-radius: 5px;
width: 2em; width: 3em;
text-align: center; text-align: center;
` `

View File

@@ -45,3 +45,29 @@ export async function* pollSpreadsheetData(creds, sheetId, tabName) {
await sleep(refreshInterval) await sleep(refreshInterval)
} }
} }
export async function* processData(dataGen) {
// Give each stream a unique and recognizable short id.
const idMap = new Map()
for await (const data of dataGen) {
for (const stream of data) {
const { Link, Source } = stream
if (!idMap.has(Link)) {
let counter = 0
let newId
const normalizedSource = Source.toLowerCase()
.replace(/[^\w]/g, '')
.replace(/^the|^https?(www)?/, '')
do {
const sourcePart = normalizedSource.substr(0, 3).toLowerCase()
const counterPart = counter === 0 ? '' : counter
newId = `${sourcePart}${counterPart}`
counter++
} while (idMap.has(newId))
idMap.set(Link, newId)
}
stream._id = idMap.get(Link)
}
yield data
}
}

View File

@@ -3,7 +3,7 @@ import yargs from 'yargs'
import { app, BrowserWindow, BrowserView, ipcMain, shell } from 'electron' import { app, BrowserWindow, BrowserView, ipcMain, shell } from 'electron'
import { interpret } from 'xstate' import { interpret } from 'xstate'
import { pollPublicData, pollSpreadsheetData } from './data' import { pollPublicData, pollSpreadsheetData, processData } from './data'
import viewStateMachine from './viewStateMachine' import viewStateMachine from './viewStateMachine'
import { boxesFromSpaceURLMap } from './geometry' import { boxesFromSpaceURLMap } from './geometry'
@@ -177,7 +177,7 @@ async function main() {
dataGen = pollPublicData() dataGen = pollPublicData()
} }
for await (const data of dataGen) { for await (const data of processData(dataGen)) {
mainWin.webContents.send('stream-data', data) mainWin.webContents.send('stream-data', data)
overlayView.webContents.send('stream-data', data) overlayView.webContents.send('stream-data', data)
} }