Skip to content

Commit

Permalink
feat: move variables page navigation to sidebar
Browse files Browse the repository at this point in the history
  • Loading branch information
Julusian committed Jan 19, 2025
1 parent 2f33675 commit a3627be
Show file tree
Hide file tree
Showing 14 changed files with 207 additions and 142 deletions.
2 changes: 1 addition & 1 deletion webui/src/Buttons/Presets/PresetsConnectionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const PresetsConnectionList = observer(function PresetsConnectionList({

const connectionInfo = connections.getInfo(id)
const moduleInfo = connectionInfo ? modules.modules.get(connectionInfo.instance_type) : undefined
const compactName = moduleInfo?.name?.replace(/\;.*/, '...')
const compactName = connectionInfo ? modules.getModuleFriendlyName(connectionInfo.instance_type) : undefined

return (
<CButton title={moduleInfo?.name} key={id} color="primary" onClick={() => setConnectionAndCategory([id, null])}>
Expand Down
40 changes: 38 additions & 2 deletions webui/src/Layout/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ import classNames from 'classnames'
import { useLocalStorage, useMediaQuery } from 'usehooks-ts'
import { Link } from '@tanstack/react-router'
import { Transition, TransitionStatus } from 'react-transition-group'
import { observer } from 'mobx-react-lite'
import { RootAppStoreContext } from '../Stores/RootAppStore.js'
import { useSortedConnectionsThatHaveVariables } from '../Stores/Util.js'

export interface SidebarStateProps {
showToggle: boolean
Expand Down Expand Up @@ -77,6 +80,7 @@ export function SidebarStateProvider({ children }: React.PropsWithChildren): Rea

interface SidebarMenuItemProps {
name: string
subheading?: string
icon: IconDefinition | null
notifications?: React.ComponentType<Record<string, never>>
path?: string
Expand All @@ -94,7 +98,16 @@ function SidebarMenuItemLabel(item: SidebarMenuItemProps) {
</span>
)}

<span className="flex-fill">{item.name}</span>
<span>
<span className="flex-fill">{item.name}</span>
{!!item.subheading && (
<>
<br />
<small>{item.subheading}</small>
</>
)}
</span>

{item.target === '_new' && <FontAwesomeIcon icon={faExternalLinkSquare} />}
{!!item.notifications && <item.notifications />}
</>
Expand Down Expand Up @@ -149,7 +162,11 @@ export const MySidebar = memo(function MySidebar() {
<SidebarMenuItem name="Remote" icon={null} path="/surfaces/outbound" />
</SidebarMenuItemGroup>
<SidebarMenuItem name="Triggers" icon={faClock} path="/triggers" />
<SidebarMenuItem name="Variables" icon={faDollarSign} path="/variables" />
<SidebarMenuItemGroup name="Variables" icon={faDollarSign} path="/variables">
<SidebarMenuItem name="Custom Variables" icon={null} path="/variables/custom" />
<SidebarMenuItem name="Internal" icon={null} path="/variables/internal" />
<SidebarVariablesGroups />
</SidebarMenuItemGroup>
<SidebarMenuItem name="Settings" icon={faCog} path="/settings" />
<SidebarMenuItem name="Import / Export" icon={faFileImport} path="/import-export" />
<SidebarMenuItem name="Log" icon={faClipboardList} path="/log" />
Expand Down Expand Up @@ -196,6 +213,25 @@ export const MySidebar = memo(function MySidebar() {
)
})

const SidebarVariablesGroups = observer(function SidebarVariablesGroups() {
const { modules } = useContext(RootAppStoreContext)

const sortedConnections = useSortedConnectionsThatHaveVariables()

return (
<>
{sortedConnections.map((connectionInfo) => (
<SidebarMenuItem
name={connectionInfo.label}
subheading={modules.getModuleFriendlyName(connectionInfo.instance_type)}
icon={null}
path={`/variables/${connectionInfo.label}`}
/>
))}
</>
)
})

/**
* This is a stripped down copy of CSidebar from coreui-react.
* Since changing the sidebar, it no longer makes sense to be able to hide it entirely,
Expand Down
4 changes: 4 additions & 0 deletions webui/src/Stores/ModuleInfoStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,8 @@ export class ModuleInfoStore {
break
}
})

getModuleFriendlyName(moduleId: string): string | undefined {
return this.modules.get(moduleId)?.name?.replace(/\;.*/, '...')
}
}
25 changes: 25 additions & 0 deletions webui/src/Stores/Util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useContext } from 'react'
import { RootAppStoreContext } from './RootAppStore.js'
import { useComputed } from '../util.js'
import { ClientConnectionConfig } from '@companion-app/shared/Model/Connections.js'

export interface ClientConnectionConfigWithId extends ClientConnectionConfig {
id: string
}

export function useSortedConnectionsThatHaveVariables(): ClientConnectionConfigWithId[] {
const { variablesStore, connections } = useContext(RootAppStoreContext)

return useComputed(() => {
const result: ClientConnectionConfigWithId[] = []

for (const [id, connection] of connections.connections) {
const connectionVariables = variablesStore.variables.get(connection.label)
if (connectionVariables && connectionVariables.size > 0) {
result.push({ ...connection, id })
}
}

return result.sort((a, b) => a.sortOrder - b.sortOrder)
}, [variablesStore.variables, connections.connections])
}
12 changes: 0 additions & 12 deletions webui/src/Stores/VariablesStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,18 +139,6 @@ export class VariablesStore {

return definitions
}

public connectionLabelsWithDefinitions = computed((): string[] => {
const labels: string[] = []

for (const [label, variables] of this.variables) {
if (label === 'internal' || variables.size > 0) {
labels.push(label)
}
}

return labels
})
}

export interface VariableDefinitionExt extends VariableDefinition {
Expand Down
11 changes: 3 additions & 8 deletions webui/src/Variables/CustomVariablesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,15 @@ import { CustomVariableDefinition } from '@companion-app/shared/Model/CustomVari
import { RootAppStoreContext } from '../Stores/RootAppStore.js'
import { observer } from 'mobx-react-lite'
import { NonIdealState } from '../Components/NonIdealState.js'
import { Link } from '@tanstack/react-router'

const DRAG_ID = 'custom-variables'

interface CustomVariableDefinitionExt extends CustomVariableDefinition {
name: string
}

interface CustomVariablesListProps {
setShowCustom: (show: boolean) => void
}

export const CustomVariablesList = observer(function CustomVariablesList({ setShowCustom }: CustomVariablesListProps) {
const doBack = useCallback(() => setShowCustom(false), [setShowCustom])

export const CustomVariablesListPage = observer(function CustomVariablesList() {
const { socket, notifier, variablesStore: customVariables } = useContext(RootAppStoreContext)

const [variableValues, setVariableValues] = useState<CompanionVariableValues>({})
Expand Down Expand Up @@ -203,7 +198,7 @@ export const CustomVariablesList = observer(function CustomVariablesList({ setSh
<div>
<h4 style={{ marginBottom: '0.8rem' }}>Variables</h4>
<CButtonGroup size="sm">
<CButton color="primary" onClick={doBack}>
<CButton color="primary" as={Link} to="/variables">
<FontAwesomeIcon icon={faArrowLeft} />
&nbsp; Go back
</CButton>
Expand Down
112 changes: 27 additions & 85 deletions webui/src/Variables/index.tsx
Original file line number Diff line number Diff line change
@@ -1,84 +1,17 @@
import React, { useCallback, useContext, useState } from 'react'
import React, { useContext } from 'react'
import { CButton, CButtonGroup } from '@coreui/react'
import { useComputed } from '../util.js'
import { VariablesTable } from '../Components/VariablesTable.js'
import { CustomVariablesList } from './CustomVariablesList.js'
import { RootAppStoreContext } from '../Stores/RootAppStore.js'
import { observer } from 'mobx-react-lite'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faArrowLeft } from '@fortawesome/free-solid-svg-icons'
import { Link, useParams } from '@tanstack/react-router'
import { useSortedConnectionsThatHaveVariables } from '../Stores/Util.js'

export const ConnectionVariablesPage = observer(function ConnectionVariables() {
const { connections } = useContext(RootAppStoreContext)
export const ConnectionVariablesPage = observer(function VariablesConnectionList() {
const { modules } = useContext(RootAppStoreContext)

const [connectionId, setConnectionId] = useState<string | null>(null)
const [showCustom, setShowCustom] = useState(false)

// Reset selection on resetToken change
// useEffect(() => {
// setConnectionId(null)
// }, [resetToken])

if (showCustom) {
return <CustomVariablesList setShowCustom={setShowCustom} />
} else if (connectionId) {
let connectionLabel = connections.getLabel(connectionId) ?? '?'
if (connectionId === 'internal') connectionLabel = 'internal'

return <VariablesList selectedConnectionLabel={connectionLabel} setConnectionId={setConnectionId} />
} else {
return <VariablesConnectionList setConnectionId={setConnectionId} setShowCustom={setShowCustom} />
}
})

interface VariablesConnectionListProps {
setConnectionId: (connectionId: string | null) => void
setShowCustom: (show: boolean) => void
}

const VariablesConnectionList = observer(function VariablesConnectionList({
setConnectionId,
setShowCustom,
}: VariablesConnectionListProps) {
const { connections, modules, variablesStore } = useContext(RootAppStoreContext)

const connectionsLabelMap: ReadonlyMap<string, string> = useComputed(() => {
const labelMap = new Map<string, string>()
for (const [connectionId, connectionInfo] of connections.connections.entries()) {
labelMap.set(connectionInfo.label, connectionId)
}
return labelMap
}, [connections])

const options = variablesStore.connectionLabelsWithDefinitions.get().map((label) => {
if (label === 'internal') {
return (
<CButton key={label} color="primary" onClick={() => setConnectionId('internal')}>
Internal
</CButton>
)
}

if (label === 'custom') {
return ''
}

const connectionId = connectionsLabelMap.get(label)
const connectionInfo = connectionId ? connections.connections.get(connectionId) : undefined
const moduleInfo = connectionInfo ? modules.modules.get(connectionInfo.instance_type) : undefined
const compactName = moduleInfo?.name?.replace(/\;.*/, '...')

return (
<CButton
title={moduleInfo?.name}
key={connectionId}
color="primary"
onClick={() => setConnectionId(connectionId ?? null)}
>
<h6>{connectionInfo?.label ?? '?'}</h6> <small>{compactName ?? '?'}</small>
</CButton>
)
})
const sortedConnections = useSortedConnectionsThatHaveVariables()

return (
<div>
Expand All @@ -88,38 +21,47 @@ const VariablesConnectionList = observer(function VariablesConnectionList({
live updating of messages, making customization quick and easy.
</p>
<div className="variables-category-grid">
<CButton color="primary" onClick={() => setShowCustom(true)}>
<CButton color="primary" as={Link} to="/variables/custom">
Custom Variables
</CButton>

{options}
<CButton color="primary" as={Link} to="/variables/internal">
Internal
</CButton>
{sortedConnections.map((connectionInfo) => {
const compactName = modules.getModuleFriendlyName(connectionInfo.instance_type)

return (
<CButton key={connectionInfo.id} color="primary" as={Link} to={`/variables/${connectionInfo.label}`}>
<h6>{connectionInfo?.label ?? '?'}</h6> <small>{compactName ?? '?'}</small>
</CButton>
)
})}
</div>
</div>
)
})

interface VariablesListProps {
selectedConnectionLabel: string
setConnectionId: (connectionId: string | null) => void
}
export function VariablesListPage() {
const { label } = useParams({ from: '/_app/variables/$label' })

function VariablesList({ selectedConnectionLabel, setConnectionId }: VariablesListProps) {
const doBack = useCallback(() => setConnectionId(null), [setConnectionId])
// Future: if label is not found, redirect to /variables
// throw redirect({ to: '/variables' })

return (
<div className="variables-panel">
<h4 style={{ marginBottom: '0.8rem' }}>Variables</h4>
<CButtonGroup size="sm">
<CButton color="primary" onClick={doBack}>
<CButton color="primary" as={Link} to="/variables">
<FontAwesomeIcon icon={faArrowLeft} />
&nbsp; Go back
</CButton>
<CButton color="secondary" onClick={doBack} disabled>
{selectedConnectionLabel}
<CButton color="secondary" disabled>
{label}
</CButton>
</CButtonGroup>

<VariablesTable label={selectedConnectionLabel} />
<VariablesTable label={label} />
<br style={{ clear: 'both' }} />
</div>
)
Expand Down
Loading

0 comments on commit a3627be

Please sign in to comment.