Skip to content

Commit

Permalink
feat: rework user permissions in the ui to use a react context
Browse files Browse the repository at this point in the history
  • Loading branch information
Julusian committed Oct 2, 2024
1 parent e560a4c commit 81bf9cb
Show file tree
Hide file tree
Showing 15 changed files with 678 additions and 640 deletions.
10 changes: 5 additions & 5 deletions packages/webui/src/client/lib/KeyboardFocusIndicator.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { Meteor } from 'meteor/meteor'
import * as React from 'react'

import { getAllowStudio, getAllowConfigure, getAllowService } from '../lib/localStorage'

import { MeteorCall } from '../lib/meteorApi'
import { getCurrentTime } from './systemTime'
import { catchError } from './lib'
import { UserPermissions } from '../ui/UserPermissions'

interface IKeyboardFocusIndicatorState {
inFocus: boolean
}
interface IKeyboardFocusIndicatorProps {
showWhenFocused?: boolean
userPermissions: Readonly<UserPermissions>
}

export class KeyboardFocusIndicator extends React.Component<
Expand Down Expand Up @@ -54,9 +54,9 @@ export class KeyboardFocusIndicator extends React.Component<
url: window.location.href + window.location.search,
width: window.innerWidth,
height: window.innerHeight,
studio: getAllowStudio(),
configure: getAllowConfigure(),
service: getAllowService(),
studio: this.props.userPermissions.studio,
configure: this.props.userPermissions.configure,
service: this.props.userPermissions.service,
}
if (focusNow) {
MeteorCall.userAction
Expand Down
10 changes: 5 additions & 5 deletions packages/webui/src/client/lib/localStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,35 +47,35 @@ function localStorageUnsetCachedItem(key: LocalStorageProperty): void {
export function setAllowStudio(studioMode: boolean): void {
localStorageSetCachedItem(LocalStorageProperty.STUDIO, studioMode ? '1' : '0')
}
export function getAllowStudio(): boolean {
export function getLocalAllowStudio(): boolean {
return localStorageGetCachedItem(LocalStorageProperty.STUDIO) === '1'
}

export function setAllowConfigure(configureMode: boolean): void {
localStorageSetCachedItem(LocalStorageProperty.CONFIGURE, configureMode ? '1' : '0')
}
export function getAllowConfigure(): boolean {
export function getLocalAllowConfigure(): boolean {
return localStorageGetCachedItem(LocalStorageProperty.CONFIGURE) === '1'
}

export function setAllowService(serviceMode: boolean): void {
localStorageSetCachedItem(LocalStorageProperty.SERVICE, serviceMode ? '1' : '0')
}
export function getAllowService(): boolean {
export function getLocalAllowService(): boolean {
return localStorageGetCachedItem(LocalStorageProperty.SERVICE) === '1'
}

export function setAllowDeveloper(developerMode: boolean): void {
localStorageSetCachedItem(LocalStorageProperty.DEVELOPER, developerMode ? '1' : '0')
}
export function getAllowDeveloper(): boolean {
export function getLocalAllowDeveloper(): boolean {
return localStorageGetCachedItem(LocalStorageProperty.DEVELOPER) === '1'
}

export function setAllowTesting(testingMode: boolean): void {
localStorageSetCachedItem(LocalStorageProperty.TESTING, testingMode ? '1' : '0')
}
export function getAllowTesting(): boolean {
export function getLocalAllowTesting(): boolean {
return localStorageGetCachedItem(LocalStorageProperty.TESTING) === '1'
}

Expand Down
3 changes: 1 addition & 2 deletions packages/webui/src/client/lib/logStatus.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Meteor } from 'meteor/meteor'
import { Tracker } from 'meteor/tracker'
import { getRandomString } from './tempLib'
import { getAllowStudio } from './localStorage'
import { logger } from './logging'

/*
Expand All @@ -12,7 +11,7 @@ import { logger } from './logging'
const browserSessionId = getRandomString(8)

// Only log status for studio users
const logStatusEnable = getAllowStudio()
const logStatusEnable = true // getAllowStudio() HACK: this needs to be setup to be reactive if this is wanted to work correctly

const previouslyLogged: {
connected?: boolean
Expand Down
230 changes: 90 additions & 140 deletions packages/webui/src/client/ui/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,8 @@ import 'moment/min/locales'
import { parse as queryStringParse } from 'query-string'
import Header from './Header'
import {
setAllowStudio,
setAllowConfigure,
getAllowStudio,
getAllowConfigure,
setAllowDeveloper,
setAllowTesting,
getAllowTesting,
getAllowDeveloper,
setAllowSpeaking,
setAllowVibrating,
setAllowService,
getAllowService,
setHelpMode,
setUIZoom,
getUIZoom,
Expand Down Expand Up @@ -48,6 +38,7 @@ import { Settings } from '../lib/Settings'
import { DocumentTitleProvider } from '../lib/DocumentTitleProvider'
import { catchError, firstIfArray, isRunningInPWA } from '../lib/lib'
import { protectString } from '@sofie-automation/shared-lib/dist/lib/protectedString'
import { useUserPermissions, UserPermissionsContext } from './UserPermissions'

const NullComponent = () => null

Expand All @@ -61,7 +52,7 @@ export const App: React.FC = function App() {

const [lastStart] = useState(Date.now())

const roles = useRoles()
const roles = useUserPermissions()
const featureFlags = useFeatureFlags()

useEffect(() => {
Expand Down Expand Up @@ -155,138 +146,97 @@ export const App: React.FC = function App() {
}, [])

return (
<Router getUserConfirmation={onNavigationUserConfirmation}>
<div className="container-fluid header-clear">
{/* Header switch - render the usual header for all pages but the rundown view */}
<ErrorBoundary>
<Switch>
<Route path="/rundown/:playlistId" component={NullComponent} />
<Route path="/countdowns/:studioId" component={NullComponent} />
<Route path="/activeRundown" component={NullComponent} />
<Route path="/prompter/:studioId" component={NullComponent} />
<Route
path="/"
render={(props) => (
<Header
{...props}
allowConfigure={roles.configure}
allowTesting={roles.testing}
allowDeveloper={roles.developer}
/>
)}
/>
</Switch>
</ErrorBoundary>
{/* Main app switch */}
<ErrorBoundary>
<Switch>
<Route exact path="/" component={RundownList} />
<Route path="/rundowns" render={() => <RundownList />} />
<Route
path="/rundown/:playlistId/shelf"
exact
render={(props) => (
<RundownView
playlistId={protectString(decodeURIComponent(props.match.params.playlistId))}
onlyShelf={true}
/>
)}
/>
<Route
path="/rundown/:playlistId"
render={(props) => (
<RundownView playlistId={protectString(decodeURIComponent(props.match.params.playlistId))} />
)}
/>
<Route
path="/activeRundown/:studioId"
render={(props) => (
<ActiveRundownView studioId={protectString(decodeURIComponent(props.match.params.studioId))} />
)}
/>
<Route
path="/prompter/:studioId"
render={(props) => (
<PrompterView studioId={protectString(decodeURIComponent(props.match.params.studioId))} />
)}
/>
{/* We switch to the general ClockView component, and allow it to do the switch between various types of countdowns */}
<Route
path="/countdowns/:studioId"
render={(props) => (
<ClockView studioId={protectString(decodeURIComponent(props.match.params.studioId))} />
)}
/>
<Route path="/status" render={() => <Status />} />
<Route path="/settings" render={() => <SettingsView />} />
<Route path="/testTools" render={() => <TestTools />} />
<Route>
<Redirect to="/" />
</Route>
</Switch>
</ErrorBoundary>
<ErrorBoundary>
<Switch>
{/* Put views that should NOT have the Notification center here: */}
<Route path="/countdowns/:studioId" component={NullComponent} />
<Route path="/prompter/:studioId" component={NullComponent} />
<Route path="/" component={ConnectionStatusNotification} />
</Switch>
</ErrorBoundary>
<ErrorBoundary>
<DocumentTitleProvider />
</ErrorBoundary>
<ErrorBoundary>
<ModalDialogGlobalContainer />
</ErrorBoundary>
</div>
</Router>
<UserPermissionsContext.Provider value={roles}>
<Router getUserConfirmation={onNavigationUserConfirmation}>
<div className="container-fluid header-clear">
{/* Header switch - render the usual header for all pages but the rundown view */}
<ErrorBoundary>
<Switch>
<Route path="/rundown/:playlistId" component={NullComponent} />
<Route path="/countdowns/:studioId" component={NullComponent} />
<Route path="/activeRundown" component={NullComponent} />
<Route path="/prompter/:studioId" component={NullComponent} />
<Route
path="/"
render={(props) => (
<Header
{...props}
allowConfigure={roles.configure}
allowTesting={roles.testing}
allowDeveloper={roles.developer}
/>
)}
/>
</Switch>
</ErrorBoundary>
{/* Main app switch */}
<ErrorBoundary>
<Switch>
<Route exact path="/" component={RundownList} />
<Route path="/rundowns" render={() => <RundownList />} />
<Route
path="/rundown/:playlistId/shelf"
exact
render={(props) => (
<RundownView
playlistId={protectString(decodeURIComponent(props.match.params.playlistId))}
onlyShelf={true}
/>
)}
/>
<Route
path="/rundown/:playlistId"
render={(props) => (
<RundownView playlistId={protectString(decodeURIComponent(props.match.params.playlistId))} />
)}
/>
<Route
path="/activeRundown/:studioId"
render={(props) => (
<ActiveRundownView studioId={protectString(decodeURIComponent(props.match.params.studioId))} />
)}
/>
<Route
path="/prompter/:studioId"
render={(props) => (
<PrompterView studioId={protectString(decodeURIComponent(props.match.params.studioId))} />
)}
/>
{/* We switch to the general ClockView component, and allow it to do the switch between various types of countdowns */}
<Route
path="/countdowns/:studioId"
render={(props) => (
<ClockView studioId={protectString(decodeURIComponent(props.match.params.studioId))} />
)}
/>
<Route path="/status" render={() => <Status />} />
<Route path="/settings" render={() => <SettingsView />} />
<Route path="/testTools" render={() => <TestTools />} />
<Route>
<Redirect to="/" />
</Route>
</Switch>
</ErrorBoundary>
<ErrorBoundary>
<Switch>
{/* Put views that should NOT have the Notification center here: */}
<Route path="/countdowns/:studioId" component={NullComponent} />
<Route path="/prompter/:studioId" component={NullComponent} />
<Route path="/" component={ConnectionStatusNotification} />
</Switch>
</ErrorBoundary>
<ErrorBoundary>
<DocumentTitleProvider />
</ErrorBoundary>
<ErrorBoundary>
<ModalDialogGlobalContainer />
</ErrorBoundary>
</div>
</Router>
</UserPermissionsContext.Provider>
)
}

function useRoles() {
const location = window.location

const [roles, setRoles] = useState({
studio: getAllowStudio(),
configure: getAllowConfigure(),
developer: getAllowDeveloper(),
testing: getAllowTesting(),
service: getAllowService(),
})

useEffect(() => {
if (!location.search) return

const params = queryStringParse(location.search)

if (params['studio']) setAllowStudio(params['studio'] === '1')
if (params['configure']) setAllowConfigure(params['configure'] === '1')
if (params['develop']) setAllowDeveloper(params['develop'] === '1')
if (params['testing']) setAllowTesting(params['testing'] === '1')
if (params['service']) setAllowService(params['service'] === '1')

if (params['admin']) {
const val = params['admin'] === '1'
setAllowStudio(val)
setAllowConfigure(val)
setAllowDeveloper(val)
setAllowTesting(val)
setAllowService(val)
}

setRoles({
studio: getAllowStudio(),
configure: getAllowConfigure(),
developer: getAllowDeveloper(),
testing: getAllowTesting(),
service: getAllowService(),
})
}, [location.search])

return roles
}

function useFeatureFlags() {
const location = window.location

Expand Down
Loading

0 comments on commit 81bf9cb

Please sign in to comment.