added recently used realms to the top (#502)

* added recently used realms to the top

fixing: #396

* fixed add realm button

* moved setting recent to setRealm
cleanup to where realm list is updated
This commit is contained in:
Erik Jan de Wit 2021-04-08 21:20:35 +02:00 committed by GitHub
parent 5142d1b4bc
commit eb9092116d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 78 additions and 13 deletions

View file

@ -23,6 +23,7 @@ import { SubGroups } from "./groups/SubGroupsContext";
import { useRealm } from "./context/realm-context/RealmContext";
import { useAdminClient, asyncStateFetch } from "./context/auth/AdminClient";
import { ErrorRenderer } from "./components/error/ErrorRenderer";
import { RecentUsed } from "./components/realm-selector/recent-used";
export const mainPageContentId = "kc-main-content-page-container";
@ -44,11 +45,14 @@ const RealmPathSelector = ({ children }: { children: ReactNode }) => {
const { realm } = useParams<{ realm: string }>();
const adminClient = useAdminClient();
const handleError = useErrorHandler();
const recentUsed = new RecentUsed();
useEffect(
() =>
asyncStateFetch(
() => adminClient.realms.find(),
(realms) => {
recentUsed.clean(realms.map((r) => r.realm!));
if (realms.findIndex((r) => r.realm == realm) !== -1) {
setRealm(realm);
}

View file

@ -1,6 +1,7 @@
import React from "react";
import { useHistory, useLocation } from "react-router-dom";
import { useTranslation } from "react-i18next";
import _ from "lodash";
import {
Nav,
NavItem,
@ -8,6 +9,7 @@ import {
NavList,
PageSidebar,
} from "@patternfly/react-core";
import { RealmSelector } from "./components/realm-selector/RealmSelector";
import { useRealm } from "./context/realm-context/RealmContext";
import { DataLoader } from "./components/data-loader/DataLoader";
@ -21,7 +23,7 @@ export const PageNav: React.FunctionComponent = () => {
const { realm } = useRealm();
const adminClient = useAdminClient();
const realmLoader = async () => {
return await adminClient.realms.find();
return _.sortBy(await adminClient.realms.find(), "realm");
};
const history = useHistory();

View file

@ -84,6 +84,7 @@
"maxLength": "Max length {{length}}",
"createRealm": "Create Realm",
"recent": "Recent",
"jumpToSection": "Jump to section",

View file

@ -12,6 +12,7 @@ import {
Split,
ContextSelector,
ContextSelectorItem,
Label,
} from "@patternfly/react-core";
import { CheckIcon } from "@patternfly/react-icons";
@ -19,6 +20,7 @@ import { toUpperCase } from "../../util";
import RealmRepresentation from "keycloak-admin/lib/defs/realmRepresentation";
import { useRealm } from "../../context/realm-context/RealmContext";
import { WhoAmIContext } from "../../context/whoami/WhoAmI";
import { RecentUsed } from "./recent-used";
import "./realm-selector.css";
@ -34,6 +36,8 @@ export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
const [filteredItems, setFilteredItems] = useState(realmList);
const history = useHistory();
const { t } = useTranslation("common");
const recentUsed = new RecentUsed();
const RealmText = ({ value }: { value: string }) => (
<Split className="keycloak__realm_selector__list-item-split">
<SplitItem isFilled>{toUpperCase(value)}</SplitItem>
@ -41,7 +45,7 @@ export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
</Split>
);
const AddRealm = ({ className }: { className?: string }) => (
const AddRealm = () => (
<Button
component="div"
isBlock
@ -49,7 +53,6 @@ export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
history.push(`/${realm}/add-realm`);
setOpen(!open);
}}
className={className}
>
{t("createRealm")}
</Button>
@ -65,6 +68,11 @@ export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
setFilteredItems(filtered || []);
};
const selectRealm = (realm: string) => {
setRealm(realm);
setOpen(!open);
};
useEffect(() => {
onFilter();
}, [search]);
@ -73,9 +81,8 @@ export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
<DropdownItem
key={`realm-dropdown-item-${r.realm}`}
onClick={() => {
setRealm(r.realm!);
history.push(`/${r.realm}/`);
setOpen(!open);
selectRealm(r.realm!);
history.push(`/${realm}/`);
}}
>
<RealmText value={r.realm!} />
@ -104,20 +111,32 @@ export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
screenReaderLabel={toUpperCase(realm)}
onToggle={() => setOpen(!open)}
onSelect={(_, r) => {
const value = (r as ReactElement).props.value;
setRealm(value || "master");
setOpen(!open);
let element: ReactElement;
if (Array.isArray(r)) {
element = (r as ReactElement[])[0];
} else {
element = r as ReactElement;
}
const value = element.props.value || "master";
selectRealm(value);
}}
searchInputValue={search}
onSearchInputChange={(value) => setSearch(value)}
onSearchButtonClick={() => onFilter()}
className="keycloak__realm_selector__context_selector"
>
{filteredItems.map((item) => (
<ContextSelectorItem key={item.id}>
<RealmText value={item.realm!} />
{recentUsed.used.map((realm) => (
<ContextSelectorItem key={realm}>
<RealmText value={realm} /> <Label>{t("recent")}</Label>
</ContextSelectorItem>
))}
{filteredItems
.filter((r) => !recentUsed.used.includes(r.realm!))
.map((item) => (
<ContextSelectorItem key={item.id}>
<RealmText value={item.realm!} />
</ContextSelectorItem>
))}
<ContextSelectorItem key="add">
<AddRealm />
</ContextSelectorItem>

View file

@ -0,0 +1,32 @@
export class RecentUsed {
private readonly MAX_NUM = 3;
private readonly KEY = "recent-used-realms";
private recentUsedRealms: string[];
constructor() {
this.recentUsedRealms = JSON.parse(localStorage.getItem(this.KEY) || "[]");
}
private save() {
this.recentUsedRealms = this.recentUsedRealms.slice(0, this.MAX_NUM);
localStorage.setItem(this.KEY, JSON.stringify(this.recentUsedRealms));
}
clean(existingRealms: string[]) {
this.recentUsedRealms = this.recentUsedRealms.filter((realm) =>
existingRealms.includes(realm)
);
this.save();
}
get used(): string[] {
return this.recentUsedRealms;
}
setRecentUsed(realm: string) {
if (!this.recentUsedRealms.includes(realm)) {
this.recentUsedRealms.unshift(realm);
this.save();
}
}
}

View file

@ -1,4 +1,5 @@
import React, { useContext, useState } from "react";
import { RecentUsed } from "../../components/realm-selector/recent-used";
type RealmContextType = {
realm: string;
@ -16,9 +17,15 @@ export const RealmContextProvider = ({
children,
}: RealmContextProviderProps) => {
const [realm, setRealm] = useState("");
const recentUsed = new RecentUsed();
const set = (realm: string) => {
recentUsed.setRecentUsed(realm);
setRealm(realm);
};
return (
<RealmContext.Provider value={{ realm, setRealm }}>
<RealmContext.Provider value={{ realm, setRealm: set }}>
{children}
</RealmContext.Provider>
);