From 85490b13a2ef1e1918f6f1875130def67c3bfc9a Mon Sep 17 00:00:00 2001 From: kevin oliveira Date: Sun, 4 Oct 2020 00:30:17 -0300 Subject: [PATCH 1/5] bookmark endpoints this feature allows the user to bookmark endpoints, displaying those endpoints on top of all other endpoints. the status of bookmarked is persisted on localstorage. --- .../components/requestCard/header/header.scss | 38 +++++++++---- .../components/requestCard/header/index.tsx | 54 ++++++++++--------- playground/src/components/section/index.tsx | 21 ++++++++ .../src/components/section/section.scss | 38 +++++++++++++ .../localStorage/bookmarkedEndpoints.ts | 23 ++++++++ playground/src/helpers/requestModel/index.ts | 12 +++++ playground/src/pages/home/index.tsx | 48 +++++++++++++---- .../src/resources/styles/shared-colors.scss | 4 ++ playground/src/stores/requests.ts | 9 ++++ 9 files changed, 203 insertions(+), 44 deletions(-) create mode 100644 playground/src/components/section/index.tsx create mode 100644 playground/src/components/section/section.scss create mode 100644 playground/src/helpers/localStorage/bookmarkedEndpoints.ts diff --git a/playground/src/components/requestCard/header/header.scss b/playground/src/components/requestCard/header/header.scss index 9dd4ddb02..265d5a106 100644 --- a/playground/src/components/requestCard/header/header.scss +++ b/playground/src/components/requestCard/header/header.scss @@ -14,12 +14,37 @@ } } +.bookmarkIcon { + margin-right: 10px; + color: $c3; + transition: all 0.1s ease-out; + + &.bookmarked { + color: $red; + &:hover { + color: $red-light-4; + } + } + &:hover { + color: $red; + } +} + +.hrefIcon { + margin-right: 10px; + color: $c3; + // margin: 5px; + &:hover { + color: $c8; + } +} + .statusCircle { margin-right: 10px; transition: all 0.2s ease-out; - &.blue { - color: $blue; + &.gray { + color: $c3; } &.green { @@ -53,14 +78,5 @@ .callName { display: flex; align-items: center; - - .hrefIcon { - margin-left: 10px; - color: $c3; - margin: 5px; - &:hover { - color: $c8; - } - } } } diff --git a/playground/src/components/requestCard/header/index.tsx b/playground/src/components/requestCard/header/index.tsx index 5d3fb44de..0d32069ee 100644 --- a/playground/src/components/requestCard/header/index.tsx +++ b/playground/src/components/requestCard/header/index.tsx @@ -1,7 +1,8 @@ -import { faChevronDown, faChevronUp, faCircle, faLink } from "@fortawesome/free-solid-svg-icons"; +import { faBookmark, faChevronDown, faChevronUp, faCircle } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import classNames from "classnames"; import { requestModel, RequestStatus } from "helpers/requestModel"; +import { observer } from 'mobx-react-lite'; import * as React from "react"; import s from "./header.scss"; @@ -10,12 +11,12 @@ interface HeaderProps { model: requestModel; closeCard?: () => void; } - -export default function Header(props: HeaderProps) { +export default observer(Header) +function Header(props: HeaderProps) { const { open, closeCard, model } = props; const colors: Record = { - notFetched: s.blue, + notFetched: s.gray, fetching: s.orange, error: s.red, sucess: s.green, @@ -23,27 +24,27 @@ export default function Header(props: HeaderProps) { }; const accentColorClass = colors[model.status]; + const onClickBookmark = (event: React.MouseEvent) => { + event.stopPropagation(); + model.toogleBookmark(); + } + if (!open) return ( <>
-
{model.name}
- -
-
- {model.status !== "notFetched" ? ( +
- ) : null} + {model.name} +
+
+
+
-
{model.name}
+
+ + {model.name} +
+
+
+
-
); } diff --git a/playground/src/components/section/index.tsx b/playground/src/components/section/index.tsx new file mode 100644 index 000000000..2d6bb1679 --- /dev/null +++ b/playground/src/components/section/index.tsx @@ -0,0 +1,21 @@ + +import * as React from "react"; +import classnames from "classnames"; +import s from "./section.scss"; + +interface TitleProps { + title: string + featured?: boolean; +} + +export function Section(props: React.PropsWithChildren) { + + const { title, featured, children } = props; + + return ( +
+
{title}
+
{children}
+
+ ); +} diff --git a/playground/src/components/section/section.scss b/playground/src/components/section/section.scss new file mode 100644 index 000000000..739be3156 --- /dev/null +++ b/playground/src/components/section/section.scss @@ -0,0 +1,38 @@ +@import "shared-colors"; + +.section { + &.featured { + .title { + color: $red-dark-1; + + &::after { + background: $red-dark-1; + } + } + } + + .title { + color: $c8; + font-weight: bold; + text-transform: capitalize; + + display: flex; + align-items: flex-start; + text-align: left; + + line-height: 25px; + + &::after { + content: ""; + margin: auto; + height: 2px; + background: $lightgray; + flex-grow: 1; + min-width: 25px; + margin-left: 12px; + } + } + .childrenWrapper { + margin: 20px 0px 40px 0px; + } +} diff --git a/playground/src/helpers/localStorage/bookmarkedEndpoints.ts b/playground/src/helpers/localStorage/bookmarkedEndpoints.ts new file mode 100644 index 000000000..6798ca930 --- /dev/null +++ b/playground/src/helpers/localStorage/bookmarkedEndpoints.ts @@ -0,0 +1,23 @@ +const BOOKMARK_LOCALSTORAGE_KEY = "bookmarked_endpoints"; + +export function persistEndpointBookmarkStatus(name: string, status: boolean) { + const currentBookmaked = getLocalStorageBookmarks(); + if (status) { // bookmark + setLocalStorageBookmarks([...currentBookmaked, name]); + } else { // unbookmark + setLocalStorageBookmarks(currentBookmaked.filter(n => n !== name)); + } +} + +export function getLocalStorageBookmarks(): string[] { + const content = localStorage.getItem(BOOKMARK_LOCALSTORAGE_KEY); + if (content) { + return JSON.parse(content); + } else { + return []; + } +} + +export function setLocalStorageBookmarks(names: string[]) { + localStorage.setItem(BOOKMARK_LOCALSTORAGE_KEY, JSON.stringify(names)); +} \ No newline at end of file diff --git a/playground/src/helpers/requestModel/index.ts b/playground/src/helpers/requestModel/index.ts index b61cdad03..fe3783943 100644 --- a/playground/src/helpers/requestModel/index.ts +++ b/playground/src/helpers/requestModel/index.ts @@ -1,5 +1,6 @@ import { observable } from "mobx"; import { AnnotationJson } from "resources/types/ast"; +import { persistEndpointBookmarkStatus } from "helpers/localStorage/bookmarkedEndpoints"; export type RequestStatus = "notFetched" | "sucess" | "fetching" | "error"; @@ -17,6 +18,7 @@ interface ConstructorArgument { baseUrl: string; deviceId: string; annotations: ModelAnotations; + bookmarked: boolean; } export class requestModel { @@ -38,6 +40,15 @@ export class requestModel { @observable public status: RequestStatus; + @observable + public bookmarked: boolean; + + + public async toogleBookmark() { + this.bookmarked = !this.bookmarked; + persistEndpointBookmarkStatus(this.name, this.bookmarked) + } + public async call(args: any, callBack?: (status: RequestStatus) => void) { this.args = args; this.loading = true; @@ -97,6 +108,7 @@ export class requestModel { this.deviceId = config.deviceId; this.baseUrl = config.baseUrl; this.annotations = config.annotations; + this.bookmarked = config.bookmarked; this.loading = false; this.response = undefined; this.error = undefined; diff --git a/playground/src/pages/home/index.tsx b/playground/src/pages/home/index.tsx index 6b98095b4..b3d98afa1 100644 --- a/playground/src/pages/home/index.tsx +++ b/playground/src/pages/home/index.tsx @@ -1,10 +1,12 @@ +import * as React from "react"; +import { Section } from 'components/section'; import { RequestCard } from "components/requestCard"; import { SearchInput } from "components/searchInput"; import { observer } from "mobx-react-lite"; -import * as React from "react"; import RootStore from "stores"; import { useDebounce } from "use-debounce"; import s from "./home.scss"; +import { requestModel } from 'helpers/requestModel'; function Home() { const { requestsStore } = React.useContext(RootStore); @@ -12,20 +14,48 @@ function Home() { const [searchStringDebounced] = useDebounce(searchString, 500); const { api } = requestsStore; - const Cards = Object.entries(api) - .filter(([fnName, _]) => - fnName.toLocaleLowerCase().includes(searchStringDebounced.toLocaleLowerCase()), - ) - .map(([fnName, FnModel]) => { - return ; - }); + + const filterBySearch = (value: [string, requestModel], _index: number, _array: [string, requestModel][]): boolean => { + const [fnName] = value; + return fnName.toLocaleLowerCase().includes(searchStringDebounced.toLocaleLowerCase()); + } + + const filterBookmarked = (value: [string, requestModel], _index: number, _array: [string, requestModel][]): boolean => { + const [, model] = value; + return model.bookmarked; + } + + const renderCard = (value: [string, requestModel], _index: number, _array: [string, requestModel][]): JSX.Element => { + const [fnName, model] = value; + return ; + } + + const BookmarkedCards = Object.entries(api) + .filter(filterBySearch) + .filter(filterBookmarked) + .map(renderCard); + + const AllCards = Object.entries(api) + .filter(filterBySearch) + .map(renderCard); + + const hasBookmarks = BookmarkedCards.length > 0; return (
- {Cards} + {hasBookmarks ? ( + <> +
+ {BookmarkedCards} +
+
+ {AllCards} +
+ + ) : AllCards}
); } diff --git a/playground/src/resources/styles/shared-colors.scss b/playground/src/resources/styles/shared-colors.scss index eda7f63f6..7f966d073 100644 --- a/playground/src/resources/styles/shared-colors.scss +++ b/playground/src/resources/styles/shared-colors.scss @@ -15,6 +15,10 @@ $blue-dark-1: darken($blue, 5%); $red: #ec2c81; $red-dark-1: darken($red, 5%); +$red-light-1: lighten($red, 5%); +$red-light-2: lighten($red, 10%); +$red-light-3: lighten($red, 15%); +$red-light-4: lighten($red, 20%); $orange: #ff9800; $orange-dark-1: darken($orange, 5%); diff --git a/playground/src/stores/requests.ts b/playground/src/stores/requests.ts index 51a9e2e8b..bfd7ba2d7 100644 --- a/playground/src/stores/requests.ts +++ b/playground/src/stores/requests.ts @@ -1,3 +1,4 @@ +import { getLocalStorageBookmarks } from 'helpers/localStorage/bookmarkedEndpoints'; import { ModelAnotations, requestModel } from "helpers/requestModel"; import { observable } from "mobx"; import { ArgsType, AstJson, TypeDescription, TypeTable } from "resources/types/ast"; @@ -152,10 +153,17 @@ export class RequestsStore { return annotations; }; + private createBookmarkedEndpointIndex = (): Record => { + return getLocalStorageBookmarks().reduce((acc, name) => ({ ...acc, [name]: true }), {}) + } + + public createModels = (AST: AstJson) => { + console.log("createModels") const { endpointUrl, deviceId } = this.rootStore.configStore; const FNs = Object.entries(AST.functionTable); + const bookmarkedEndpointsIndex = this.createBookmarkedEndpointIndex(); this.api = FNs.reduce((acc, [fName, fStruct]) => { const argsMock = this.createMockBasedOnTypes(fStruct.args, AST.typeTable); const annotations = this.getAnotations(AST, fName); @@ -167,6 +175,7 @@ export class RequestsStore { baseUrl: endpointUrl, deviceId: deviceId!, annotations, + bookmarked: Boolean(bookmarkedEndpointsIndex[fName]) }), }; }, {}); From 06848f39fb1c645d9eb1b4a37e96f09697f8b9c9 Mon Sep 17 00:00:00 2001 From: kevin oliveira Date: Sun, 4 Oct 2020 02:14:12 -0300 Subject: [PATCH 2/5] prettier --- .../localStorage/bookmarkedEndpoints.ts | 30 ++++++++++--------- playground/src/helpers/requestModel/index.ts | 3 +- playground/src/stores/requests.ts | 13 ++++---- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/playground/src/helpers/localStorage/bookmarkedEndpoints.ts b/playground/src/helpers/localStorage/bookmarkedEndpoints.ts index 6798ca930..cd40c9a4b 100644 --- a/playground/src/helpers/localStorage/bookmarkedEndpoints.ts +++ b/playground/src/helpers/localStorage/bookmarkedEndpoints.ts @@ -1,23 +1,25 @@ const BOOKMARK_LOCALSTORAGE_KEY = "bookmarked_endpoints"; export function persistEndpointBookmarkStatus(name: string, status: boolean) { - const currentBookmaked = getLocalStorageBookmarks(); - if (status) { // bookmark - setLocalStorageBookmarks([...currentBookmaked, name]); - } else { // unbookmark - setLocalStorageBookmarks(currentBookmaked.filter(n => n !== name)); - } + const currentBookmaked = getLocalStorageBookmarks(); + if (status) { + // bookmark + setLocalStorageBookmarks([...currentBookmaked, name]); + } else { + // unbookmark + setLocalStorageBookmarks(currentBookmaked.filter(n => n !== name)); + } } export function getLocalStorageBookmarks(): string[] { - const content = localStorage.getItem(BOOKMARK_LOCALSTORAGE_KEY); - if (content) { - return JSON.parse(content); - } else { - return []; - } + const content = localStorage.getItem(BOOKMARK_LOCALSTORAGE_KEY); + if (content) { + return JSON.parse(content); + } else { + return []; + } } export function setLocalStorageBookmarks(names: string[]) { - localStorage.setItem(BOOKMARK_LOCALSTORAGE_KEY, JSON.stringify(names)); -} \ No newline at end of file + localStorage.setItem(BOOKMARK_LOCALSTORAGE_KEY, JSON.stringify(names)); +} diff --git a/playground/src/helpers/requestModel/index.ts b/playground/src/helpers/requestModel/index.ts index fe3783943..14e9d90e9 100644 --- a/playground/src/helpers/requestModel/index.ts +++ b/playground/src/helpers/requestModel/index.ts @@ -43,10 +43,9 @@ export class requestModel { @observable public bookmarked: boolean; - public async toogleBookmark() { this.bookmarked = !this.bookmarked; - persistEndpointBookmarkStatus(this.name, this.bookmarked) + persistEndpointBookmarkStatus(this.name, this.bookmarked); } public async call(args: any, callBack?: (status: RequestStatus) => void) { diff --git a/playground/src/stores/requests.ts b/playground/src/stores/requests.ts index bfd7ba2d7..e0164bce2 100644 --- a/playground/src/stores/requests.ts +++ b/playground/src/stores/requests.ts @@ -1,4 +1,4 @@ -import { getLocalStorageBookmarks } from 'helpers/localStorage/bookmarkedEndpoints'; +import { getLocalStorageBookmarks } from "helpers/localStorage/bookmarkedEndpoints"; import { ModelAnotations, requestModel } from "helpers/requestModel"; import { observable } from "mobx"; import { ArgsType, AstJson, TypeDescription, TypeTable } from "resources/types/ast"; @@ -134,7 +134,7 @@ export class RequestsStore { const functionAnnotations = AST.annotations[`fn.${functionName}`] || []; const regex = RegExp(`fn.${functionName}\\.[^\.]*`); - const argsKeys = Object.keys(AST.annotations).filter((target) => regex.test(target)); + const argsKeys = Object.keys(AST.annotations).filter(target => regex.test(target)); const argsAnnotations = argsKeys.reduce((acc, argKey) => { // breaks 'fn.getBalance.bankCode' into ["fn", "getBalance", "bankCode"] @@ -154,12 +154,11 @@ export class RequestsStore { }; private createBookmarkedEndpointIndex = (): Record => { - return getLocalStorageBookmarks().reduce((acc, name) => ({ ...acc, [name]: true }), {}) - } - + return getLocalStorageBookmarks().reduce((acc, name) => ({ ...acc, [name]: true }), {}); + }; public createModels = (AST: AstJson) => { - console.log("createModels") + console.log("createModels"); const { endpointUrl, deviceId } = this.rootStore.configStore; const FNs = Object.entries(AST.functionTable); @@ -175,7 +174,7 @@ export class RequestsStore { baseUrl: endpointUrl, deviceId: deviceId!, annotations, - bookmarked: Boolean(bookmarkedEndpointsIndex[fName]) + bookmarked: Boolean(bookmarkedEndpointsIndex[fName]), }), }; }, {}); From a052c18a28a783acaea1e9156bc00aeed8eccf96 Mon Sep 17 00:00:00 2001 From: Kevin Olivera Date: Sun, 4 Oct 2020 20:16:18 -0300 Subject: [PATCH 3/5] adding storage-factory as dependency to use localStorage safely --- playground/package.json | 1 + .../src/helpers/localStorage/bookmarkedEndpoints.ts | 6 ++++-- playground/src/helpers/localStorage/safeLocalStorage.ts | 3 +++ playground/src/stores/config.ts | 9 +++++---- 4 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 playground/src/helpers/localStorage/safeLocalStorage.ts diff --git a/playground/package.json b/playground/package.json index 69e734fd5..beb86de96 100644 --- a/playground/package.json +++ b/playground/package.json @@ -79,6 +79,7 @@ "react-router-dom": "^5.1.2", "react-test-renderer": "^16.12.0", "sass-loader": "^7.3.1", + "storage-factory": "^0.1.1", "style-loader": "^0.23.1", "ts-jest": "^25.2.0", "ts-loader": "^6.2.1", diff --git a/playground/src/helpers/localStorage/bookmarkedEndpoints.ts b/playground/src/helpers/localStorage/bookmarkedEndpoints.ts index cd40c9a4b..6c8c751b2 100644 --- a/playground/src/helpers/localStorage/bookmarkedEndpoints.ts +++ b/playground/src/helpers/localStorage/bookmarkedEndpoints.ts @@ -1,3 +1,5 @@ +import { safeLocalStorage } from './safeLocalStorage'; + const BOOKMARK_LOCALSTORAGE_KEY = "bookmarked_endpoints"; export function persistEndpointBookmarkStatus(name: string, status: boolean) { @@ -12,7 +14,7 @@ export function persistEndpointBookmarkStatus(name: string, status: boolean) { } export function getLocalStorageBookmarks(): string[] { - const content = localStorage.getItem(BOOKMARK_LOCALSTORAGE_KEY); + const content = safeLocalStorage.getItem(BOOKMARK_LOCALSTORAGE_KEY); if (content) { return JSON.parse(content); } else { @@ -21,5 +23,5 @@ export function getLocalStorageBookmarks(): string[] { } export function setLocalStorageBookmarks(names: string[]) { - localStorage.setItem(BOOKMARK_LOCALSTORAGE_KEY, JSON.stringify(names)); + safeLocalStorage.setItem(BOOKMARK_LOCALSTORAGE_KEY, JSON.stringify(names)); } diff --git a/playground/src/helpers/localStorage/safeLocalStorage.ts b/playground/src/helpers/localStorage/safeLocalStorage.ts new file mode 100644 index 000000000..beabd2dd7 --- /dev/null +++ b/playground/src/helpers/localStorage/safeLocalStorage.ts @@ -0,0 +1,3 @@ +import { storageFactory } from "storage-factory"; + +export const safeLocalStorage = storageFactory(() => localStorage); diff --git a/playground/src/stores/config.ts b/playground/src/stores/config.ts index 57718df4a..8fcc98194 100644 --- a/playground/src/stores/config.ts +++ b/playground/src/stores/config.ts @@ -1,5 +1,6 @@ import { observable } from "mobx"; import { RootStore } from "."; +import { safeLocalStorage } from 'helpers/localStorage/safeLocalStorage'; const endpointUrlFallback = process.env.NODE_ENV === "development" @@ -40,13 +41,13 @@ export class ConfigStore { private syncWithLocalStorage = (override: boolean) => { if (!override) { - this.deviceId = localStorage.getItem("deviceId") || randomBytesHex(16); + this.deviceId = safeLocalStorage.getItem("deviceId") || randomBytesHex(16); this.endpointUrl = - (this.canChangeEndpoint && localStorage.getItem("endpointUrl")) || + (this.canChangeEndpoint && safeLocalStorage.getItem("endpointUrl")) || endpointUrlFallback; } else { - if (this.deviceId) localStorage.setItem("deviceId", this.deviceId); - if (this.endpointUrl) localStorage.setItem("endpointUrl", this.endpointUrl); + if (this.deviceId) safeLocalStorage.setItem("deviceId", this.deviceId); + if (this.endpointUrl) safeLocalStorage.setItem("endpointUrl", this.endpointUrl); } }; } From b09eda3b1fdfe71e89461266b1333350b5b43f9b Mon Sep 17 00:00:00 2001 From: Kevin Olivera Date: Sun, 4 Oct 2020 20:21:01 -0300 Subject: [PATCH 4/5] fix: playground prettier --- playground/src/helpers/localStorage/bookmarkedEndpoints.ts | 2 +- playground/src/stores/config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/playground/src/helpers/localStorage/bookmarkedEndpoints.ts b/playground/src/helpers/localStorage/bookmarkedEndpoints.ts index 6c8c751b2..c2f039a23 100644 --- a/playground/src/helpers/localStorage/bookmarkedEndpoints.ts +++ b/playground/src/helpers/localStorage/bookmarkedEndpoints.ts @@ -1,4 +1,4 @@ -import { safeLocalStorage } from './safeLocalStorage'; +import { safeLocalStorage } from "./safeLocalStorage"; const BOOKMARK_LOCALSTORAGE_KEY = "bookmarked_endpoints"; diff --git a/playground/src/stores/config.ts b/playground/src/stores/config.ts index 8fcc98194..adc806cdd 100644 --- a/playground/src/stores/config.ts +++ b/playground/src/stores/config.ts @@ -1,6 +1,6 @@ import { observable } from "mobx"; import { RootStore } from "."; -import { safeLocalStorage } from 'helpers/localStorage/safeLocalStorage'; +import { safeLocalStorage } from "helpers/localStorage/safeLocalStorage"; const endpointUrlFallback = process.env.NODE_ENV === "development" From 79418a9e8f5e8242aabb793a7f73397cd1dc467a Mon Sep 17 00:00:00 2001 From: Kevin Olivera Date: Sun, 4 Oct 2020 20:29:35 -0300 Subject: [PATCH 5/5] fix: playground prettier --- playground/src/helpers/localStorage/bookmarkedEndpoints.ts | 2 +- playground/src/stores/requests.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/playground/src/helpers/localStorage/bookmarkedEndpoints.ts b/playground/src/helpers/localStorage/bookmarkedEndpoints.ts index c2f039a23..63c7879fe 100644 --- a/playground/src/helpers/localStorage/bookmarkedEndpoints.ts +++ b/playground/src/helpers/localStorage/bookmarkedEndpoints.ts @@ -9,7 +9,7 @@ export function persistEndpointBookmarkStatus(name: string, status: boolean) { setLocalStorageBookmarks([...currentBookmaked, name]); } else { // unbookmark - setLocalStorageBookmarks(currentBookmaked.filter(n => n !== name)); + setLocalStorageBookmarks(currentBookmaked.filter((n) => n !== name)); } } diff --git a/playground/src/stores/requests.ts b/playground/src/stores/requests.ts index e0164bce2..e62332256 100644 --- a/playground/src/stores/requests.ts +++ b/playground/src/stores/requests.ts @@ -134,7 +134,7 @@ export class RequestsStore { const functionAnnotations = AST.annotations[`fn.${functionName}`] || []; const regex = RegExp(`fn.${functionName}\\.[^\.]*`); - const argsKeys = Object.keys(AST.annotations).filter(target => regex.test(target)); + const argsKeys = Object.keys(AST.annotations).filter((target) => regex.test(target)); const argsAnnotations = argsKeys.reduce((acc, argKey) => { // breaks 'fn.getBalance.bankCode' into ["fn", "getBalance", "bankCode"]