Skip to content

Commit

Permalink
feat(console): show version number for oss
Browse files Browse the repository at this point in the history
  • Loading branch information
gao-sun committed May 30, 2024
1 parent 0866b6e commit 85a5ec1
Show file tree
Hide file tree
Showing 15 changed files with 210 additions and 155 deletions.
5 changes: 5 additions & 0 deletions .changeset/rare-hornets-sneeze.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@logto/console": minor
---

show version number in the topbar
5 changes: 5 additions & 0 deletions packages/console/src/assets/icons/cube.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 0 additions & 38 deletions packages/console/src/components/Topbar/Contact/index.module.scss

This file was deleted.

43 changes: 0 additions & 43 deletions packages/console/src/components/Topbar/Contact/index.tsx

This file was deleted.

This file was deleted.

26 changes: 0 additions & 26 deletions packages/console/src/components/Topbar/DocumentNavButton/index.tsx

This file was deleted.

48 changes: 48 additions & 0 deletions packages/console/src/components/Topbar/index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,51 @@
margin-right: _.unit(4);
}
}

.button {
display: flex;
align-items: center;
padding: _.unit(1);
border-radius: 6px;
border: none;
background-color: transparent;
transition: background-color 0.2s ease-in-out;
user-select: none;
outline: none;
cursor: pointer;
margin-left: _.unit(-1);
text-decoration: none;
gap: _.unit(1);
font: var(--font-label-2);
color: var(--color-neutral-variant-30);

&:hover {
background-color: var(--color-hover-variant);
}

// Use this selector to override the hover effect in `<TextLink />`
&:not(:disabled):hover {
text-decoration: none;
}

&.active {
background-color: var(--color-focused-variant);
}

.icon {
width: 20px;
height: 20px;

> path {
fill: var(--color-neutral-variant-50);
}
}
}

.newVersionDot {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: var(--color-error);
margin-left: _.unit(0.5);
}
104 changes: 100 additions & 4 deletions packages/console/src/components/Topbar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import { isKeyInObject, trySafe } from '@silverhand/essentials';
import classNames from 'classnames';
import { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';

import ContactIcon from '@/assets/icons/contact-us.svg';
import CubeIcon from '@/assets/icons/cube.svg';
import DocumentIcon from '@/assets/icons/document-nav-button.svg';
import CloudLogo from '@/assets/images/cloud-logo.svg';
import Logo from '@/assets/images/logo.svg';
import { githubReleasesLink } from '@/consts';
import { isCloud } from '@/consts/env';
import DynamicT from '@/ds-components/DynamicT';
import Spacer from '@/ds-components/Spacer';
import TextLink from '@/ds-components/TextLink';
import useDocumentationUrl from '@/hooks/use-documentation-url';
import { onKeyDownHandler } from '@/utils/a11y';

import Contact from './Contact';
import DocumentNavButton from './DocumentNavButton';
import ContactModal from './ContactModal';
import TenantSelector from './TenantSelector';
import UserInfo from './UserInfo';
import * as styles from './index.module.scss';
import { currentVersion, isGreaterThanCurrentVersion } from './utils';

type Props = {
readonly className?: string;
Expand All @@ -35,11 +45,97 @@ function Topbar({ className, hideTenantSelector, hideTitle }: Props) {
</>
)}
<Spacer />
<DocumentNavButton />
<Contact />
<DocumentButton />
<HelpButton />
{!isCloud && <VersionButton />}
<UserInfo />
</div>
);
}

export default Topbar;

function DocumentButton() {
const { documentationSiteUrl } = useDocumentationUrl();
return (
<TextLink
href={documentationSiteUrl}
targetBlank="noopener"
className={styles.button}
icon={<DocumentIcon className={styles.icon} />}
>
<DynamicT forKey="topbar.docs" />
</TextLink>
);
}

function HelpButton() {
const [isContactOpen, setIsContactOpen] = useState(false);
const anchorRef = useRef<HTMLDivElement>(null);

return (
<>
<div
ref={anchorRef}
tabIndex={0}
className={styles.button}
role="button"
onKeyDown={onKeyDownHandler(() => {
setIsContactOpen(true);
})}
onClick={() => {
setIsContactOpen(true);
}}
>
<ContactIcon className={styles.icon} />
<span>
<DynamicT forKey="topbar.help" />
</span>
</div>
<ContactModal
isOpen={isContactOpen}
onCancel={() => {
setIsContactOpen(false);
}}
/>
</>
);
}

function VersionButton() {
const [isNewVersionAvailable, setIsNewVersionAvailable] = useState(false);

useEffect(() => {
void trySafe(
async () => {
const response = await fetch('https://numbers.logto.io/pull.json');
const json = await response.json();
if (
!isKeyInObject(json, 'latestRelease') ||
typeof json.latestRelease !== 'string' ||
!json.latestRelease.startsWith('v')
) {
return;
}
if (isGreaterThanCurrentVersion(json.latestRelease)) {
setIsNewVersionAvailable(true);
}
},
(error) => {
console.warn('Failed to check for new version', error);
}
);
}, []);

return (
<TextLink
href={githubReleasesLink}
targetBlank="noopener"
className={styles.button}
icon={<CubeIcon className={styles.icon} />}
>
v{currentVersion}
{isNewVersionAvailable && <div className={styles.newVersionDot} />}
</TextLink>
);
}
28 changes: 28 additions & 0 deletions packages/console/src/components/Topbar/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { version as currentVersion } from '../../../../core/package.json';

import { isGreaterThanCurrentVersion } from './utils';

describe('isGreaterThanCurrentVersion', () => {
it('should return true if the target version is greater than the current Logto version', () => {
expect(isGreaterThanCurrentVersion('v2.0')).toBe(true);
expect(isGreaterThanCurrentVersion('2.0')).toBe(true);
expect(isGreaterThanCurrentVersion('v1.999.0')).toBe(true);
expect(isGreaterThanCurrentVersion('1.999.0')).toBe(true);
});

it('should return false if the target version is less than the current Logto version', () => {
expect(isGreaterThanCurrentVersion('v0.1.1')).toBe(false);
expect(isGreaterThanCurrentVersion('0.1.1')).toBe(false);
expect(isGreaterThanCurrentVersion('v1.15')).toBe(false);
expect(isGreaterThanCurrentVersion('1.15')).toBe(false);
});

it('should return false if the target version is malformed', () => {
expect(isGreaterThanCurrentVersion('vv1.1')).toBe(false);
expect(isGreaterThanCurrentVersion('foo')).toBe(false);
});

it('should return false if the target version is equal to the current Logto version', () => {
expect(isGreaterThanCurrentVersion(currentVersion)).toBe(false);
});
});
21 changes: 21 additions & 0 deletions packages/console/src/components/Topbar/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { version } from '../../../../core/package.json';

export { version as currentVersion } from '../../../../core/package.json';

/** Check if the target version is greater than the current Logto version. */
export const isGreaterThanCurrentVersion = (target: string) => {
const latestComponents = (target.startsWith('v') ? target.slice(1) : target).split('.');
const currentComponents = version.split('.');

for (const [index, component] of latestComponents.entries()) {
const current = currentComponents[index];
if (!current || Number(current) < Number(component)) {
return true;
}

if (Number(current) > Number(component)) {
return false;
}
}
return false;
};
Loading

0 comments on commit 85a5ec1

Please sign in to comment.