Skip to content

Commit

Permalink
Notification: simple stable-latest version warning logic (#125)
Browse files Browse the repository at this point in the history
I implemented the simple idea exposed in #71.
I think we can start with this simple logic that's easy to explain and
grow from here if necessary.

- on `latest`: a notification to warn the user about some features may
not yet be available is shown
- on non-`stable` nor `latest` versions: a notification to warn the user
about possible reading and old/outdated version is shown

Note that both notification are pretty simple, it does not require
external dependencies (e.g. `semver`) and it's easy to explain to users.
The warning is shown only if `stable` is an active version --since we
link to it from both.

## On `latest`



![Screenshot_2023-09-13_12-31-59](https://github.com/readthedocs/addons/assets/244656/fd44859d-bf14-4f98-b586-449212141f00)


## On non-`stable`


![Screenshot_2023-09-13_12-31-19](https://github.com/readthedocs/addons/assets/244656/0a9014f9-ab95-42f3-8050-3c927cd97847)

----

_However_, I think the best way for users to make usage of this addon is
by implementing the logic that fits their needs by themselves. We don't
allow users to customize this logic just yet, but we plan to do it in
the future when the addons is more settled down.

Closes #71
  • Loading branch information
humitos authored Sep 18, 2023
1 parent 9f78364 commit 6167eb7
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 56 deletions.
16 changes: 8 additions & 8 deletions dist/readthedocs-addons.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/readthedocs-addons.js.map

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
"keyboard-event-to-string": "^2.1.0",
"lit": "^2.7.1",
"prettier": "^2.8.4",
"semver": "^7.3.8",
"tippy.js": "^6.3.7",
"tslib": "^2.4.0",
"visual-dom-diff": "^0.7.2",
Expand Down
126 changes: 81 additions & 45 deletions src/notification.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import semverMaxSatisfying from "semver/ranges/max-satisfying";
import semverCoerce from "semver/functions/coerce";
import { library, icon } from "@fortawesome/fontawesome-svg-core";
import {
faCircleXmark,
faFlask,
faCodePullRequest,
faLayerGroup,
faHourglassHalf,
} from "@fortawesome/free-solid-svg-icons";
import { html, nothing, render, LitElement } from "lit";

Expand Down Expand Up @@ -33,8 +32,10 @@ export class NotificationElement extends LitElement {
this.urls = {
build: null,
external: null,
stable: null,
};
this.highest_version = null;
this.reading_latest_version = null;
this.stable_version_available = null;
}

loadConfig(config) {
Expand All @@ -57,11 +58,10 @@ export class NotificationElement extends LitElement {
}

if (
!IS_PRODUCTION &&
config.addons.non_latest_version_warning.enabled &&
config.versions.current.type !== "external"
) {
this.calculateHighestVersion();
this.calculateStableLatestVersionWarning();
}
}

Expand All @@ -77,62 +77,98 @@ export class NotificationElement extends LitElement {
return this.renderExternalVersionWarning();
}
} else if (
!IS_PRODUCTION &&
this.config.addons.non_latest_version_warning.enabled &&
this.highest_version
(this.reading_latest_version || this.stable_version_available)
) {
return this.renderNonLatestVersionWarning();
return this.renderStableLatestVersionWarning();
}
return nothing;
}

calculateHighestVersion() {
// Convert versions like `v1` into `1.0.0` to be able to compare them
calculateStableLatestVersionWarning() {
// The logic is pretty simple:
// - if the user is reading the "latest" version: shows a notification to warn
// the user about reading the latest development version.
// - if the user is reading a non-"stable" version: shows a notification to warn
// the user about reading a version that may be old.
//
// This does not cover all the cases where this notification could be useful,
// but users with different needs should be able to implement their own custom logic.
const versions = this.config.addons.non_latest_version_warning.versions;
const coercedVersions = versions.map((v) => semverCoerce(v));
const coercedHighest = semverMaxSatisfying(coercedVersions, ">=0.0.0");

// Get back the original `v1` to generate the URLs and display the correct name
const index = coercedVersions.indexOf(coercedHighest);
const highest = versions[index];

if (highest && highest !== this.config.versions.current.slug) {
this.highest_version = {
name: highest,
// TODO: get this URL from the API
url: `${window.location.protocol}//${window.location.hostname}/${this.config.projects.current.language}/${highest}/`,
};
const latest_index = versions.indexOf("latest");
const stable_index = versions.indexOf("stable");
const current_version = this.config.versions.current;
const current_project = this.config.projects.current;

if (current_version.slug === "latest") {
this.reading_latest_version = true;
} else if (stable_index && current_version.slug !== "stable") {
this.stable_version_available = true;
}

if (stable_index) {
this.urls.stable = `/${current_project.language.code}/stable/`;
}
}

renderNonLatestVersionWarning() {
renderStableLatestVersionWarning() {
library.add(faCircleXmark);
library.add(faLayerGroup);
library.add(faHourglassHalf);
library.add(faFlask);
const xmark = icon(faCircleXmark, {
title: "Close notification",
});
const iconLayerGroup = icon(faLayerGroup, {
title: "This version is not the latest one",
classes: ["header", "icon"],
});
if (this.reading_latest_version) {
const iconFlask = icon(faFlask, {
classes: ["header", "icon"],
});

return html`
<div>
${iconLayerGroup.node[0]}
<div class="title">
This is an <span>old version</span>
<a href="#" class="right" @click=${this.closeNotification}>
${xmark.node[0]}
</a>
return html`
<div>
${iconFlask.node[0]}
<div class="title">
This is the <span>latest development version</span>
<a href="#" class="right" @click=${this.closeNotification}>
${xmark.node[0]}
</a>
</div>
<div class="content">
Some features may not be yet available in the publised stable
version. Read the
<a href="${this.urls.stable}"
>stable version of this documentation</a
>.
</div>
</div>
<div class="content">
You are reading an old version of this documentation. The latest
stable version is
<a href="${this.highest_version.url}">${this.highest_version.name}</a
>.
`;
}

if (this.stable_version_available) {
const iconHourglassHalf = icon(faHourglassHalf, {
classes: ["header", "icon"],
});

return html`
<div>
${iconHourglassHalf.node[0]}
<div class="title">
This <em>may</em> be an
<span>old version of this documentation</span>
<a href="#" class="right" @click=${this.closeNotification}>
${xmark.node[0]}
</a>
</div>
<div class="content">
You may be reading and old version of this documentation. Read the
<a href="${this.urls.stable}"
>latest stable version of this documentation</a
>.
</div>
</div>
</div>
`;
`;
}

return nothing;
}

renderExternalVersionWarning() {
Expand Down

0 comments on commit 6167eb7

Please sign in to comment.