Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion web_refresher/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Web Refresher",
"version": "18.0.1.0.0",
"version": "18.0.2.0.0",
"author": "Compassion Switzerland, Tecnativa, Odoo Community Association (OCA)",
"license": "AGPL-3",
"website": "https://github.com/OCA/web",
Expand Down
132 changes: 131 additions & 1 deletion web_refresher/static/src/js/refresher.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Copyright 2023 Taras Shabaranskyi
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */

import {Component} from "@odoo/owl";
import {Component, onMounted, onWillUnmount} from "@odoo/owl";
import {useDebounced} from "@web/core/utils/timing";
import {useService} from "@web/core/utils/hooks";

Expand Down Expand Up @@ -39,13 +39,59 @@ export function useRefreshAnimation(timeout) {
}

export class Refresher extends Component {
autoRefreshIntervalKey = "oca.web_refresher.auto_refresh";
refreshDefaultSettingsKey = "default";
setup() {
super.setup();
this.action = useService("action");
this.refreshAnimation = useRefreshAnimation(1000);
this.onClickRefresh = useDebounced(this.onClickRefresh, 200);
this.onChangeAutoRefreshInterval = this.onChangeAutoRefreshInterval.bind(this);
this.runningRefresherId = null;
onMounted(() => {
const intervalValue = this._getLocalStorageValue(window.location.pathname);
this.onChangeAutoRefreshInterval({
value: intervalValue.refreshInterval,
textContent: intervalValue.intervalText,
});
});
onWillUnmount(() => {
if (this.runningRefresherId) {
clearTimeout(this.runningRefresherId);
}
});
}

_getLocalStorageValue(lookupKey) {
const jsonValue = localStorage.getItem(this.autoRefreshIntervalKey);
const refreshSettings = jsonValue ? JSON.parse(jsonValue) : {};
if (!lookupKey) {
return refreshSettings;
}
let returnInterval = -1;
let returnText = "Off";
if (Object.hasOwn(refreshSettings, lookupKey)) {
const {refreshInterval = -1, intervalText = "Off"} =
refreshSettings[lookupKey];
returnInterval = parseInt(refreshInterval ?? -1);
returnText = intervalText;
} else if (Object.hasOwn(refreshSettings, this.refreshDefaultSettingsKey)) {
const {refreshInterval = -1, intervalText = "Off"} =
refreshSettings[this.refreshDefaultSettingsKey];
returnInterval = parseInt(refreshInterval ?? -1);
returnText = intervalText;
}
return {refreshInterval: returnInterval, intervalText: returnText};
}

_setLocalStorageValue(settingsKey, {refreshInterval, intervalText}) {
// Get current settings
const jsonValue = localStorage.getItem(this.autoRefreshIntervalKey);
const refreshSettings = jsonValue ? JSON.parse(jsonValue) : {};
refreshSettings[settingsKey] = {refreshInterval, intervalText};
const value = JSON.stringify(refreshSettings);
localStorage.setItem(this.autoRefreshIntervalKey, value);
}
/**
* @returns {Boolean}
* @private
Expand Down Expand Up @@ -81,6 +127,16 @@ export class Refresher extends Component {
if (!updated) {
updated = this._searchModelRefresh();
}
// Check the refreshInterval is greater than 0 and start a timer for the next refresh
if (this.refreshInterval > 0) {
// Always attempt to clear a running timeout in case the refresh was done manually
if (typeof this.runningRefresherId === "number") {
clearTimeout(this.runningRefresherId);
}
this.runningRefresherId = setTimeout(() => {
this.refresh();
}, this.refreshInterval);
}
Comment on lines +130 to +139
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consolidated logic for starting the refresh interval in the current refresh to allow recursive calls

return updated;
}

Expand All @@ -101,6 +157,23 @@ export class Refresher extends Component {
this.action.doAction(viewAction, options);
}

_isRefreshIntervalDefault() {
const localStoredIntervals = this._getLocalStorageValue();

if (
Object.hasOwn(localStoredIntervals, this.refreshDefaultSettingsKey) &&
Object.hasOwn(
localStoredIntervals[this.refreshDefaultSettingsKey],
"refreshInterval"
) &&
this.refreshInterval ===
localStoredIntervals[this.refreshDefaultSettingsKey].refreshInterval
) {
return true;
}
return false;
}

async onClickRefresh() {
const {searchModel, pagerProps} = this.props;
if (!searchModel && !pagerProps) {
Expand All @@ -111,6 +184,63 @@ export class Refresher extends Component {
this.refreshAnimation();
}
}

setRefreshAsDefault() {
this._setLocalStorageValue(this.refreshDefaultSettingsKey, {
refreshInterval: this.refreshInterval,
intervalText: document.getElementById("auto-refresh-interval-text")
.textContent,
});
this._setIntervalUi(
document.getElementById("auto-refresh-interval-text").textContent
);
}

onChangeAutoRefreshInterval(clickedOption) {
const newInterval =
parseInt(clickedOption.value ?? clickedOption.target.value) ?? -1;
const newIntervalText =
clickedOption.textContent ?? clickedOption.target.textContent ?? "Off";
this.refreshInterval = newInterval;
this._setIntervalUi(newIntervalText);
if (this.runningRefresherId) {
clearTimeout(this.runningRefresherId);
}
this.refresh();

this._setLocalStorageValue(window.location.pathname, {
refreshInterval: newInterval,
intervalText: newIntervalText,
});
}

_setIntervalUi(intervalText) {
// Check if the refresh is active and spin the refresh button if active
const manualRefreshIcon = document.getElementById("manual-refresh-icon");
if (manualRefreshIcon) {
if (!this.refreshInterval || this.refreshInterval <= 0) {
manualRefreshIcon.classList.remove("fa-spin");
} else {
manualRefreshIcon.classList.add("fa-spin");
}
}
// Set the interval dropdown text to the selected interval
const refreshText = document.getElementById("auto-refresh-interval-text");
if (refreshText) {
refreshText.textContent = intervalText;
}
// Check if the current interval is the default and set the star icon accordingly
const setAsDefaultIcon = document.getElementById("set-as-default-icon");
if (setAsDefaultIcon) {
if (this._isRefreshIntervalDefault()) {
setAsDefaultIcon.classList.remove("fa-star-o");
setAsDefaultIcon.classList.add("fa-star");
} else {
setAsDefaultIcon.classList.remove("fa-star");
setAsDefaultIcon.classList.add("fa-star-o");
}
}
}
}

Object.assign(Refresher, {
Expand Down
72 changes: 65 additions & 7 deletions web_refresher/static/src/xml/refresher.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,71 @@
<template>
<t t-name="web_refresher.Button">
<nav class="oe_refresher" aria-label="Refresher" aria-atomic="true">
<button
class="fa fa-refresh btn btn-icon oe_pager_refresh"
aria-label="Refresh"
t-on-click="onClickRefresh"
title="Refresh"
tabindex="-1"
/>
<div class="btn-group" role="group">
<div class="btn-group" role="group">
<button
id="auto-refresh-dd"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added id for easy selections

class="btn btn-secondary dropdown-toggle"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
Auto Refresh: <span id="auto-refresh-interval-text">Off</span>
</button>
<div class="dropdown-menu" aria-labelledby="auto-refresh-dd">
<button
class="dropdown-item"
t-on-click="onChangeAutoRefreshInterval"
value="-1"
>Off</button>
Comment on lines +20 to +24
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Used -1 as the "off" value to avoid null/undefined confusions. #javascriptthings

<button
class="dropdown-item"
t-on-click="onChangeAutoRefreshInterval"
value="1000"
>1s</button>
Comment on lines +25 to +29
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure 1s is really appropriate. I could easily be persuaded to remove 1s

<button
class="dropdown-item"
t-on-click="onChangeAutoRefreshInterval"
value="5000"
>5s</button>
<button
class="dropdown-item"
t-on-click="onChangeAutoRefreshInterval"
value="10000"
>10s</button>
<button
class="dropdown-item"
t-on-click="onChangeAutoRefreshInterval"
value="30000"
>30s</button>
<button
class="dropdown-item"
t-on-click="onChangeAutoRefreshInterval"
value="60000"
>1min</button>
</div>
</div>
<button
id="set-as-default-btn"
class="btn btn-secondary m-0"
aria-label="Set as Default"
t-on-click="setRefreshAsDefault"
title="Set as Default"
tabindex="-1"
>
<i id="set-as-default-icon" class="fa" />
</button>
<button
id="manual-refresh-btn"
class="btn btn-secondary m-0"
aria-label="Refresh"
t-on-click="onClickRefresh"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintain existing flow with the a reset of the new timeout when manually clicked

title="Refresh"
tabindex="-1"
>
<i id="manual-refresh-icon" class="fa fa-refresh" />
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the icons to an <i> to allow the fa-spin to be added and removed without spinning the whole button 🤣

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added id for easy selections

</button>
</div>
</nav>
</t>
</template>