Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add upgrade motivation banner #7768

Merged
merged 31 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ca65b31
WIP [ci skip] start implementing upgrade banner
dieknolle3333 Apr 17, 2024
2319a68
style banner and add more conditions
dieknolle3333 Apr 17, 2024
6b93366
style banner
dieknolle3333 Apr 17, 2024
67be3a5
[ci skip] WIP: styling banner
dieknolle3333 Apr 22, 2024
713cb2a
adjust styling more
dieknolle3333 Apr 24, 2024
dc5f6a7
WIP: use themeprovider to adjust nested theme
dieknolle3333 Apr 24, 2024
b7039fa
fix avatar margin if banner is shown above navbar
dieknolle3333 Apr 24, 2024
ec267a4
remove comment
dieknolle3333 Apr 24, 2024
65e83b4
remove dev edits
dieknolle3333 Apr 24, 2024
9f95bcb
improve styling and add testing options
dieknolle3333 Apr 25, 2024
c679a07
Merge branch 'master' into upgrade-motivation-banner
dieknolle3333 Apr 25, 2024
cc26cb2
Merge branch 'master' into upgrade-motivation-banner
dieknolle3333 Apr 29, 2024
f630f94
fix commit date format
dieknolle3333 Apr 29, 2024
68418e3
add helper to parse date
dieknolle3333 Apr 29, 2024
5ea5ed7
add changelog
dieknolle3333 Apr 29, 2024
1e0c83c
[ci skip] WIP: address review
dieknolle3333 Apr 30, 2024
9592cf1
WIP[ci skip] address review 2
dieknolle3333 May 1, 2024
7e2508a
add WK link and improve style
dieknolle3333 May 6, 2024
d19f69d
merge master
dieknolle3333 May 6, 2024
e4665ae
remove testing residue
dieknolle3333 May 6, 2024
87ee706
remove test remainder 2
dieknolle3333 May 7, 2024
b920078
rename navbarHeight var and banners module
dieknolle3333 May 8, 2024
74c39c5
Merge branch 'master' into upgrade-motivation-banner
dieknolle3333 May 8, 2024
81b5e1b
improve scenario where both upgrade and maintenance banner are shown;…
dieknolle3333 May 14, 2024
7d81e8f
merge master
dieknolle3333 May 14, 2024
194d4d1
change order in AI job modal to make mito inferral second
dieknolle3333 May 14, 2024
a0a0413
add tooltip again
dieknolle3333 May 15, 2024
55fc506
merge master
dieknolle3333 May 15, 2024
ad0d4f6
revert application.conf edits
dieknolle3333 May 15, 2024
2787816
Merge branch 'master' into upgrade-motivation-banner
dieknolle3333 May 15, 2024
9f3a18d
Merge branch 'master' into upgrade-motivation-banner
dieknolle3333 May 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- Minor improvements for the timetracking overview (faster data loading, styling). [#7789](https://github.com/scalableminds/webknossos/pull/7789)
- Updated several backend dependencies for optimized stability and performance. [#7782](https://github.com/scalableminds/webknossos/pull/7782)
- Voxelytics workflows can be searched by name and hash. [#7790](https://github.com/scalableminds/webknossos/pull/7790)
- If a self-hosted WEBKNOSSOS instance has not been updated for six months or more, a closable banner proposes an upgrade to webknossos.org. [#7768](https://github.com/scalableminds/webknossos/pull/7768)

### Changed
- Non-admin or -manager users can no longer start long-running jobs that create datasets. This includes annotation materialization and AI inferrals. [#7753](https://github.com/scalableminds/webknossos/pull/7753)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import {
getBuildInfo,
listCurrentAndUpcomingMaintenances,
updateNovelUserExperienceInfos,
} from "admin/admin_rest_api";
import { Alert } from "antd";
import { Alert, Button, Space } from "antd";
import FormattedDate from "components/formatted_date";
import { useInterval } from "libs/react_helpers";
import dayjs from "dayjs";
import { useFetch, useInterval } from "libs/react_helpers";
import { parseCTimeDefaultDate } from "libs/utils";
import _ from "lodash";
import constants from "oxalis/constants";
import { setNavbarHeightAction } from "oxalis/model/actions/ui_actions";
Expand All @@ -14,15 +17,17 @@ import { OxalisState } from "oxalis/store";
import React, { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { MaintenanceInfo } from "types/api_flow_types";
import * as Utils from "libs/utils";

const INITIAL_DELAY = 5000;
const INTERVAL_TO_FETCH_MAINTENANCES_MS = 60000; // 1min
const UPGRADE_BANNER_DISMISSAL_TIMESTAMP_LOCAL_STORAGE_KEY = "upgradeBannerWasClickedAway";

const BANNER_STYLE: React.CSSProperties = {
position: "absolute",
top: 0,
left: 0,
height: constants.MAINTENANCE_BANNER_HEIGHT,
height: constants.BANNER_HEIGHT,
};

function setNavbarHeight(newNavbarHeight: number) {
Expand Down Expand Up @@ -114,16 +119,27 @@ export function MaintenanceBanner() {
setClosestUpcomingMaintenance(_.first(closestUpcomingMaintenance));
}

const [shouldShowUpcomingMaintenanceBanner, setShouldShowUpcomingMaintenanceBanner] =
useState(false);

useEffect(() => {
if (currentMaintenance || closestUpcomingMaintenance) {
setNavbarHeight(constants.DEFAULT_NAVBAR_HEIGHT + constants.MAINTENANCE_BANNER_HEIGHT);
const newShouldShowUpcomingMaintenanceBanner =
closestUpcomingMaintenance != null && activeUser != null;
if (newShouldShowUpcomingMaintenanceBanner !== shouldShowUpcomingMaintenanceBanner) {
setShouldShowUpcomingMaintenanceBanner(newShouldShowUpcomingMaintenanceBanner);
}
}, [closestUpcomingMaintenance, activeUser, shouldShowUpcomingMaintenanceBanner]);

useEffect(() => {
if (currentMaintenance || shouldShowUpcomingMaintenanceBanner) {
setNavbarHeight(constants.DEFAULT_NAVBAR_HEIGHT + constants.BANNER_HEIGHT);
}

if (currentMaintenance == null && closestUpcomingMaintenance == null) {
// Reset Navbar height if maintenance is over
setNavbarHeight(constants.DEFAULT_NAVBAR_HEIGHT);
}
}, [currentMaintenance, closestUpcomingMaintenance]);
}, [currentMaintenance, closestUpcomingMaintenance, shouldShowUpcomingMaintenanceBanner]);

useEffect(() => {
// Do an initial fetch of the maintenance status so that users are notified
Expand All @@ -144,3 +160,104 @@ export function MaintenanceBanner() {

return null;
}

export function UpgradeVersionBanner() {
const white = "var(--ant-color-text-primary)";
const blue = "var(--ant-color-primary)";
const UPGRADE_BANNER_STYLE: React.CSSProperties = {
position: "absolute",
top: 0,
left: 0,
height: constants.BANNER_HEIGHT,
textAlign: "center",
backgroundColor: blue,
color: white,
fontSize: "medium",
minWidth: "fit-content",
zIndex: 999,
};
const currentDate = dayjs();

const activeUser = useSelector((state: OxalisState) => state.activeUser);

const isVersionOutdated = useFetch(
async () => {
if (!activeUser) return false;
await Utils.sleep(INITIAL_DELAY);
let buildInfo = await getBuildInfo();
const lastCommitDate = parseCTimeDefaultDate(buildInfo.webknossos.commitDate);
const needsUpdate = currentDate.diff(lastCommitDate, "month") >= 6;
return needsUpdate;
},
false,
[activeUser],
);

const [shouldBannerBeShown, setShouldBannerBeShown] = useState(false);

useEffect(() => {
if (!isVersionOutdated || activeUser == null) {
setShouldBannerBeShown(false);
return;
}
const lastTimeBannerWasClickedAway = localStorage.getItem(
UPGRADE_BANNER_DISMISSAL_TIMESTAMP_LOCAL_STORAGE_KEY,
);
if (lastTimeBannerWasClickedAway == null) {
setShouldBannerBeShown(true);
return;
}

const parsedDate = dayjs(lastTimeBannerWasClickedAway);
setShouldBannerBeShown(currentDate.diff(parsedDate, "day") >= 3);
}, [activeUser, isVersionOutdated, currentDate]);

useEffect(() => {
if (shouldBannerBeShown) {
setNavbarHeight(constants.DEFAULT_NAVBAR_HEIGHT + constants.BANNER_HEIGHT);
} else {
setNavbarHeight(constants.DEFAULT_NAVBAR_HEIGHT);
}
}, [shouldBannerBeShown]);

return shouldBannerBeShown ? (
<Alert
className="upgrade-banner"
message={
<Space size="middle">
<Space size="small">
You are using an outdated version of WEBKNOSSOS. Switch to
<a
className="upgrade-banner-wk-link"
target="_blank"
href="https://webknossos.org"
rel="noreferrer noopener"
>
webknossos.org
</a>
for automatic updates and exclusive features!
</Space>
<Button
className="upgrade-banner-button"
href="https://webknossos.org/self-hosted-upgrade"
size="small"
>
Learn more
</Button>
</Space>
}
banner
style={UPGRADE_BANNER_STYLE}
closable
onClose={() => {
localStorage.setItem(
UPGRADE_BANNER_DISMISSAL_TIMESTAMP_LOCAL_STORAGE_KEY,
dayjs().toISOString(),
);
setNavbarHeight(constants.DEFAULT_NAVBAR_HEIGHT);
}}
type="info"
showIcon={false}
/>
) : null;
}
2 changes: 2 additions & 0 deletions frontend/javascripts/libs/format_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import duration from "dayjs/plugin/duration";
import updateLocale from "dayjs/plugin/updateLocale";
import relativeTime from "dayjs/plugin/relativeTime";
import localizedFormat from "dayjs/plugin/localizedFormat";
import customParseFormat from "dayjs/plugin/customParseFormat";
import calendar from "dayjs/plugin/calendar";
import utc from "dayjs/plugin/utc";
import weekday from "dayjs/plugin/weekday";
Expand All @@ -22,6 +23,7 @@ dayjs.extend(relativeTime);
dayjs.extend(utc);
dayjs.extend(calendar);
dayjs.extend(weekday);
dayjs.extend(customParseFormat);
dayjs.extend(localeData);
dayjs.extend(localizedFormat);
dayjs.updateLocale("en", {
Expand Down
10 changes: 10 additions & 0 deletions frontend/javascripts/libs/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
} from "oxalis/constants";
import window, { document, location } from "libs/window";
import { ArbitraryObject, Comparator } from "types/globals";
import dayjs from "dayjs";

type UrlParams = Record<string, string>;
// Fix JS modulo bug
Expand Down Expand Up @@ -563,6 +564,15 @@ export function isFileExtensionEqualTo(
return passedExtension === extensionOrExtensions;
}

// Parses dates in format "Thu Jan 1 00:00:00 1970 +0000".
export function parseCTimeDefaultDate(dateString: string) {
const commitDateWithoutWeekday = dateString.replace(
/(Mon)|(Tue)|(Wed)|(Thu)|(Fri)|(Sat)|(Sun)\w*/,
"",
);
return dayjs(commitDateWithoutWeekday, "MMM D HH:mm:ss YYYY ZZ");
}

// Only use this function if you really need a busy wait (useful
// for testing performance-related edge cases). Prefer `sleep`
// otherwise.
Expand Down
30 changes: 15 additions & 15 deletions frontend/javascripts/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Tag,
Input,
InputRef,
ConfigProvider,
} from "antd";
import _ from "lodash";
import {
Expand All @@ -25,7 +26,7 @@ import {
import { useHistory, Link } from "react-router-dom";

import classnames from "classnames";
import { connect } from "react-redux";
import { connect, useSelector } from "react-redux";
import React, { useState, useEffect, useRef } from "react";
import Toast from "libs/toast";
import type {
Expand Down Expand Up @@ -61,8 +62,8 @@ import { PricingEnforcedSpan } from "components/pricing_enforcers";
import { ItemType, MenuItemType, SubMenuType } from "antd/lib/menu/hooks/useItems";
import { MenuClickEventHandler } from "rc-menu/lib/interface";
import constants from "oxalis/constants";
import { MaintenanceBanner } from "maintenance_banner";
import { getSystemColorTheme } from "theme";
import { MaintenanceBanner, UpgradeVersionBanner } from "banners";
import { getAntdTheme, getSystemColorTheme } from "theme";

const { Header } = Layout;

Expand Down Expand Up @@ -482,8 +483,7 @@ function NotificationIcon({
position: "relative",
display: "flex",
marginRight: 12,
paddingTop:
navbarHeight > constants.DEFAULT_NAVBAR_HEIGHT ? constants.MAINTENANCE_BANNER_HEIGHT : 0,
paddingTop: navbarHeight > constants.DEFAULT_NAVBAR_HEIGHT ? constants.BANNER_HEIGHT : 0,
}}
>
<Tooltip title="See what's new in WEBKNOSSOS" placement="bottomLeft">
Expand Down Expand Up @@ -610,8 +610,7 @@ function LoggedInAvatar({
selectedKeys={["prevent highlighting of this menu"]}
mode="horizontal"
style={{
paddingTop:
navbarHeight > constants.DEFAULT_NAVBAR_HEIGHT ? constants.MAINTENANCE_BANNER_HEIGHT : 0,
paddingTop: navbarHeight > constants.DEFAULT_NAVBAR_HEIGHT ? constants.BANNER_HEIGHT : 0,
lineHeight: `${constants.DEFAULT_NAVBAR_HEIGHT}px`,
}}
theme="dark"
Expand Down Expand Up @@ -701,6 +700,9 @@ function LoggedInAvatar({
}

function AnonymousAvatar() {
const bannerHeight = useSelector(
(state: OxalisState) => state.uiInformation.navbarHeight - constants.DEFAULT_NAVBAR_HEIGHT,
);
return (
<Popover
placement="bottomRight"
Expand All @@ -722,6 +724,7 @@ function AnonymousAvatar() {
icon={<UserOutlined />}
style={{
marginLeft: 8,
marginTop: bannerHeight,
}}
/>
</Popover>
Expand Down Expand Up @@ -899,15 +902,15 @@ function Navbar({
})}
>
<MaintenanceBanner />
<ConfigProvider theme={{ ...getAntdTheme("light") }}>
<UpgradeVersionBanner />
</ConfigProvider>
<Menu
mode="horizontal"
selectedKeys={selectedKeys}
onOpenChange={(openKeys) => setIsHelpMenuOpen(openKeys.includes(HELP_MENU_KEY))}
style={{
paddingTop:
navbarHeight > constants.DEFAULT_NAVBAR_HEIGHT
? constants.MAINTENANCE_BANNER_HEIGHT
: 0,
paddingTop: navbarHeight > constants.DEFAULT_NAVBAR_HEIGHT ? constants.BANNER_HEIGHT : 0,
lineHeight: `${constants.DEFAULT_NAVBAR_HEIGHT}px`,
}}
theme="dark"
Expand All @@ -930,10 +933,7 @@ function Navbar({
style={{
flex: 1,
display: "flex",
paddingTop:
navbarHeight > constants.DEFAULT_NAVBAR_HEIGHT
? constants.MAINTENANCE_BANNER_HEIGHT
: 0,
paddingTop: navbarHeight > constants.DEFAULT_NAVBAR_HEIGHT ? constants.BANNER_HEIGHT : 0,
}}
/>

Expand Down
2 changes: 1 addition & 1 deletion frontend/javascripts/oxalis/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ const Constants = {
BUCKET_SIZE: 32 ** 3,
VIEWPORT_WIDTH,
DEFAULT_NAVBAR_HEIGHT: 48,
MAINTENANCE_BANNER_HEIGHT: 38,
BANNER_HEIGHT: 38,
// For reference, the area of a large brush size (let's say, 300px) corresponds to
// pi * 300 ^ 2 == 282690.
// We multiply this with 5, since the labeling is not done
Expand Down
24 changes: 12 additions & 12 deletions frontend/javascripts/oxalis/view/action-bar/starting_job_modals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -438,17 +438,17 @@ export function StartAIJobModal({ aIJobModalState }: StartAIJobModalProps) {
<Tooltip title="Coming soon">
<Radio.Button
className="aIJobSelection"
disabled
checked={aIJobModalState === "nuclei_inferral"}
onClick={() => Store.dispatch(setAIJobModalStateAction("nuclei_inferral"))}
checked={aIJobModalState === "mitochondria_inferral"}
disabled={!Store.getState().activeUser?.isSuperUser}
onClick={() => Store.dispatch(setAIJobModalStateAction("mitochondria_inferral"))}
>
<Card bordered={false}>
<Space direction="vertical" size="small">
<Row className="ai-job-title">Nuclei detection</Row>
<Row className="ai-job-title">Mitochondria detection</Row>
<Row>
<img
src={`/assets/images/${jobNameToImagePath.nuclei_inferral}`}
alt={"Nuclei detection example"}
src={`/assets/images/${jobNameToImagePath.mitochondria_inferral}`}
alt={"Mitochondria detection example"}
style={centerImageStyle}
/>
</Row>
Expand All @@ -459,17 +459,17 @@ export function StartAIJobModal({ aIJobModalState }: StartAIJobModalProps) {
<Tooltip title="Coming soon">
<Radio.Button
className="aIJobSelection"
checked={aIJobModalState === "mitochondria_inferral"}
disabled={!Store.getState().activeUser?.isSuperUser}
onClick={() => Store.dispatch(setAIJobModalStateAction("mitochondria_inferral"))}
disabled
checked={aIJobModalState === "nuclei_inferral"}
onClick={() => Store.dispatch(setAIJobModalStateAction("nuclei_inferral"))}
>
<Card bordered={false}>
<Space direction="vertical" size="small">
<Row className="ai-job-title">Mitochondria detection</Row>
<Row className="ai-job-title">Nuclei detection</Row>
<Row>
<img
src={`/assets/images/${jobNameToImagePath.mitochondria_inferral}`}
alt={"Mitochondria detection example"}
src={`/assets/images/${jobNameToImagePath.nuclei_inferral}`}
alt={"Nuclei detection example"}
style={centerImageStyle}
/>
</Row>
Expand Down
Loading