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:
parent
5142d1b4bc
commit
eb9092116d
6 changed files with 78 additions and 13 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
"maxLength": "Max length {{length}}",
|
||||
|
||||
"createRealm": "Create Realm",
|
||||
"recent": "Recent",
|
||||
|
||||
"jumpToSection": "Jump to section",
|
||||
|
||||
|
|
|
@ -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,16 +111,28 @@ 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) => (
|
||||
{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>
|
||||
|
|
32
src/components/realm-selector/recent-used.ts
Normal file
32
src/components/realm-selector/recent-used.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue