Skip to content

Persist active queues across reloads #131

Merged
merged 4 commits into from
Dec 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/components/AppView/AppView.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,10 @@ export default function AppView({ setDarkMode }){
<Box className={classes.leftCol}>
<ItemTableAppBar title="webqueue2" setDarkMode={setDarkMode} />
<QueueSelector
queueSelectorOpen={queueSelectorOpen}
setQueueSelectorOpen={setQueueSelectorOpen}
selectedQueues={selectedQueues}
setSelectedQueues={setSelectedQueues}
open={queueSelectorOpen}
setOpen={setQueueSelectorOpen}
value={selectedQueues}
setValue={setSelectedQueues}
/>
<ItemTable data={items} rowCanBeSelected={sidebarOpen}/>
</Box>
Expand Down
2 changes: 1 addition & 1 deletion src/components/AuthProvider/AuthProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function AuthProvider({ children }) {
const [loggedIn, setLoggedIn] = useState( false );
const [token, setToken] = useState( null );

const [cookies] = useCookies();
const [cookies] = useCookies(["csrf_refresh_token"]);

async function tryRefresh(csrf_refresh_token){
if (csrf_refresh_token === undefined){
Expand Down
108 changes: 80 additions & 28 deletions src/components/QueueSelector/QueueSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,48 +5,100 @@ import { Autocomplete } from "@material-ui/lab";
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
import CheckBoxIcon from '@material-ui/icons/CheckBox';
import CircularProgress from '@material-ui/core/CircularProgress';
import { useCookies } from "react-cookie";
import { useToken } from "../AuthProvider/";

export default function QueueSelector({ queueSelectorOpen, setQueueSelectorOpen, selectedQueues, setSelectedQueues }) {
const open = queueSelectorOpen;
const setOpen = setQueueSelectorOpen;
/**
* Get queue names and number of items.
* @param {String} access_token A valid API access token.
* @returns Array of objects containing queue names and item counts.
*/
const getQueueCounts = async (access_token) => {
if (access_token === null){
return undefined
}

let myHeaders = new Headers();
myHeaders.append("Authorization", `Bearer ${access_token}`);
let requestOptions = { headers: myHeaders };

const apiResponse = await fetch(`/api/get_queues`, requestOptions);
const queueCountJson = await apiResponse.json();

return queueCountJson;
};

export default function QueueSelector({ open, setOpen, value, setValue }) {
const [queueCounts, setQueueCounts] = useState([]);
const [isFirstRender, setIsFirstRender] = useState(true);
const access_token = useToken();
const loading = open && queueCounts.length === 0;

const [cookies, setCookie] = useCookies(["active-queues"]);
const activeQueues = cookies['active-queues'] !== undefined ? cookies['active-queues'].split(',') : [];

const theme = useTheme();

// Prepopulate Active Queues from Cookies
useEffect( _ => {
const getQueueCounts = async _ => {
if (access_token === null){
return undefined
}

let myHeaders = new Headers();
myHeaders.append("Authorization", `Bearer ${access_token}`);
let requestOptions = { headers: myHeaders };

const apiResponse = await fetch(`/api/get_queues`, requestOptions);
const queueCountJson = await apiResponse.json();
setQueueCounts(queueCountJson);
};
if (access_token === null){
return undefined;
}

if (loading) {
getQueueCounts();
if (isFirstRender) {
( async _ => {
// Get queue counts
let queueCountsJson = await getQueueCounts(access_token);

// Find queue count info for queue names in active queues
let activeQueuesInfo = activeQueues.map((queueName) => (
queueCountsJson.find( ({ name }) => queueName === name )
));

// Filter undefined values
activeQueuesInfo = activeQueuesInfo.filter( (entry) => entry !== undefined);

setValue(activeQueuesInfo);
setIsFirstRender(false);
})();
}
}, []);

// Get queue counts if QueueSelector is open
useEffect( _ => {
(async _ => {
if (loading) {
let queueCountsJson = await getQueueCounts(access_token);
setQueueCounts(queueCountsJson);
}
})()
}, [loading, access_token]);

// Delete queue counts if QueueSelector is closed
useEffect(() => {
if (!open) {
setQueueCounts([]);
}
}, [open]);

const theme = useTheme();

const handleChange = (event, newValue) => {
setSelectedQueues(newValue)
setValue(newValue)

// Set active-queues cookie to csv of selected queue names
const activeQueueOptions = {
path: "/",
expires: (_ => {
let expiration_date = new Date();
expiration_date.setDate(expiration_date.getDate() + 365);
return expiration_date;
})()
};
const activeQueues = newValue.map( (value) => value.name).join(',');
setCookie("active-queues", activeQueues, activeQueueOptions);
};

// Function to render checkboxes in dropdown
// See `renderOptions` prop at https://material-ui.com/api/autocomplete/#props
const optionRenderer = (option, { selected }) => (
<>
<Checkbox
Expand All @@ -60,13 +112,14 @@ export default function QueueSelector({ queueSelectorOpen, setQueueSelectorOpen,
);

return(
// Box is used for margin because Autocomplete CSS overrides don't work as expected.
<Box margin={`${theme.spacing(1)}px`}>
<Autocomplete
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
placeholder={selectedQueues.length === 0 ? "Click or type to select queues." : ""}
placeholder={value.length === 0 ? "Click or type to select queues." : ""}
autoFocus
// The MUI Autocomplete component uses the InputProps.startAdornment to store chips fpr multi-selection.
// Using InputProps.startAdornment directly will override those chips. Code below is a workaround.
Expand All @@ -90,9 +143,8 @@ export default function QueueSelector({ queueSelectorOpen, setQueueSelectorOpen,
}}
/>
)}

options={queueCounts}
value={selectedQueues}
value={value}
onChange={handleChange}
getOptionLabel={(option) => `${option.name} (${option.number_of_items})`}
renderOption={optionRenderer}
Expand All @@ -114,11 +166,11 @@ export default function QueueSelector({ queueSelectorOpen, setQueueSelectorOpen,

QueueSelector.propTypes = {
/** State variable to manage open status. */
"queueSelectorOpen": PropTypes.bool.isRequired,
"open": PropTypes.bool.isRequired,
/** Function to update state variable that manages open status. */
"setQueueSelectorOpen": PropTypes.func.isRequired,
"setOpen": PropTypes.func.isRequired,
/** State variable to manage selected queues. */
"selectedQueues": PropTypes.array.isRequired,
"value": PropTypes.array.isRequired,
/** Function to update state variable that manages selected queues. */
"setSelectedQueues": PropTypes.func.isRequired,
"setValue": PropTypes.func.isRequired,
};