diff --git a/src/App.tsx b/src/App.tsx
index b315dc56f4..2d85b88813 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -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);
}
diff --git a/src/PageNav.tsx b/src/PageNav.tsx
index 91120f990d..09a13f6a40 100644
--- a/src/PageNav.tsx
+++ b/src/PageNav.tsx
@@ -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();
diff --git a/src/common-messages.json b/src/common-messages.json
index 0db0b31e01..e288163ca8 100644
--- a/src/common-messages.json
+++ b/src/common-messages.json
@@ -84,6 +84,7 @@
"maxLength": "Max length {{length}}",
"createRealm": "Create Realm",
+ "recent": "Recent",
"jumpToSection": "Jump to section",
diff --git a/src/components/realm-selector/RealmSelector.tsx b/src/components/realm-selector/RealmSelector.tsx
index e8669c5d6d..1228b4f09a 100644
--- a/src/components/realm-selector/RealmSelector.tsx
+++ b/src/components/realm-selector/RealmSelector.tsx
@@ -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 }) => (
{toUpperCase(value)}
@@ -41,7 +45,7 @@ export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
);
- const AddRealm = ({ className }: { className?: string }) => (
+ const AddRealm = () => (
@@ -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) => {
{
- setRealm(r.realm!);
- history.push(`/${r.realm}/`);
- setOpen(!open);
+ selectRealm(r.realm!);
+ history.push(`/${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) => (
-
-
+ {recentUsed.used.map((realm) => (
+
+
))}
+ {filteredItems
+ .filter((r) => !recentUsed.used.includes(r.realm!))
+ .map((item) => (
+
+
+
+ ))}
diff --git a/src/components/realm-selector/recent-used.ts b/src/components/realm-selector/recent-used.ts
new file mode 100644
index 0000000000..be49955821
--- /dev/null
+++ b/src/components/realm-selector/recent-used.ts
@@ -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();
+ }
+ }
+}
diff --git a/src/context/realm-context/RealmContext.tsx b/src/context/realm-context/RealmContext.tsx
index f98a1c86e7..c55c9b2a25 100644
--- a/src/context/realm-context/RealmContext.tsx
+++ b/src/context/realm-context/RealmContext.tsx
@@ -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 (
-
+
{children}
);