[IMP] web: better looking cids in url

Before this commit, cids in the url (in the hash part) were
separated by a comma, which was encoded by encodeURIComponent as
it is not considered as a safe character, resulting into "%2C"
appearing in the url in between company ids. This was kind of ugly
and made the url a bit hard to read.

This commit uses "-" as separator for cids, which is a safe
characters [1] to use in the url and which is thus left untouched by
encodeURIComponent.

AL request.

[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#description

closes odoo/odoo#136104

Related: odoo/enterprise#47723
Signed-off-by: Samuel Degueldre (sad) <sad@odoo.com>
This commit is contained in:
Aaron Bohy
2023-09-21 23:54:25 +00:00
parent 3911a69bbb
commit abae4d4a5c
3 changed files with 48 additions and 34 deletions
@@ -5,14 +5,19 @@ import { registry } from "@web/core/registry";
import { session } from "@web/session";
import { UPDATE_METHODS } from "@web/core/orm_service";
function parseCompanyIds(cidsFromHash) {
const cids = [];
if (typeof cidsFromHash === "string") {
cids.push(...cidsFromHash.split(",").map(Number));
} else if (typeof cidsFromHash === "number") {
cids.push(cidsFromHash);
const CIDS_HASH_SEPARATOR = "-";
function parseCompanyIds(cids, separator = ",") {
if (typeof cids === "string") {
return cids.split(separator).map(Number);
} else if (typeof cids === "number") {
return [cids];
}
return cids;
return [];
}
function formatCompanyIds(cids, separator = ",") {
return cids.join(separator);
}
function computeAllowedCompanyIds(cids) {
@@ -34,30 +39,30 @@ export const companyService = {
start(env, { user, router, cookie, action }) {
let cids;
if ("cids" in router.current.hash) {
cids = parseCompanyIds(router.current.hash.cids);
cids = parseCompanyIds(router.current.hash.cids, CIDS_HASH_SEPARATOR);
} else if ("cids" in cookie.current) {
cids = parseCompanyIds(cookie.current.cids);
}
const availableCompanies = session.user_companies.allowed_companies;
const allowedCompanyIds = computeAllowedCompanyIds(cids);
const nextAvailableCompanies = allowedCompanyIds.slice(); // not using a Set because order is important
const nextAvailableCompanies = allowedCompanyIds.slice(); // not using a Set because order is important
nextAvailableCompanies.add = (companyId) => {
if (!nextAvailableCompanies.includes(companyId)) {
nextAvailableCompanies.push(companyId);
availableCompanies[companyId].child_ids.map(nextAvailableCompanies.add);
}
}
};
nextAvailableCompanies.remove = (companyId) => {
if (nextAvailableCompanies.includes(companyId)) {
nextAvailableCompanies.splice(nextAvailableCompanies.indexOf(companyId), 1);
availableCompanies[companyId].child_ids.map(nextAvailableCompanies.remove);
}
}
};
const stringCIds = allowedCompanyIds.join(",");
router.replaceState({ cids: stringCIds }, { lock: true });
cookie.setCookie("cids", stringCIds);
const cidsHash = formatCompanyIds(allowedCompanyIds, CIDS_HASH_SEPARATOR);
router.replaceState({ cids: cidsHash }, { lock: true });
cookie.setCookie("cids", formatCompanyIds(allowedCompanyIds));
user.updateContext({ allowed_company_ids: allowedCompanyIds });
// reload the page if changes are being done to `res.company`
@@ -92,15 +97,20 @@ export const companyService = {
}
} else if (mode === "loginto") {
nextAvailableCompanies.splice(0, nextAvailableCompanies.length);
nextAvailableCompanies.add(companyId)
nextAvailableCompanies.add(companyId);
}
},
logNextCompanies() {
const next = nextAvailableCompanies.length ? nextAvailableCompanies : [allowedCompanyIds[0]]
router.pushState({ cids: next }, { lock: true });
cookie.setCookie("cids", next);
const next = nextAvailableCompanies.length
? nextAvailableCompanies
: [allowedCompanyIds[0]];
router.pushState(
{ cids: formatCompanyIds(next, CIDS_HASH_SEPARATOR) },
{ lock: true }
);
cookie.setCookie("cids", formatCompanyIds(next));
browser.setTimeout(() => browser.location.reload()); // history.pushState is a little async
}
},
};
},
};
@@ -119,7 +119,7 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
assert.containsN(scMenuEl, "[data-company-id] .fa-check-square", 2);
assert.containsN(scMenuEl, "[data-company-id] .fa-square-o", 1);
await prom;
assert.verifySteps(["cids=1%2C2"]);
assert.verifySteps(["cids=1-2"]);
});
QUnit.test("can toggle multiple companies at once", async (assert) => {
@@ -157,7 +157,7 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
assert.verifySteps([]);
await prom; // await toggle promise
assert.verifySteps(["cids=2%2C3"]);
assert.verifySteps(["cids=2-3"]);
});
QUnit.test("single company selected: toggling it off will keep it", async (assert) => {
@@ -231,7 +231,7 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
function onPushState(url) {
assert.step(url.split("#")[1]);
}
Object.assign(browser.location, { hash: "cids=3%2C1" });
Object.assign(browser.location, { hash: "cids=3-1" });
const scMenu = await createSwitchCompanyMenu({ onPushState });
const scMenuEl = target.querySelector(".o_burger_menu_companies");
@@ -261,7 +261,7 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
function onPushState(url) {
assert.step(url.split("#")[1]);
}
Object.assign(browser.location, { hash: "cids=2%2C3" });
Object.assign(browser.location, { hash: "cids=2-3" });
const scMenu = await createSwitchCompanyMenu({ onPushState });
const scMenuEl = target.querySelector(".o_burger_menu_companies");
@@ -101,13 +101,15 @@ QUnit.module("SwitchCompanyMenu", (hooks) => {
assert.containsN(target, "[data-company-id] .fa-check-square", 1);
assert.containsN(target, "[data-company-id] .fa-square-o", 4);
assert.deepEqual(
[...target.querySelectorAll("[data-company-id] .toggle_company")].map(
(el) => el.getAttribute('aria-checked')
[...target.querySelectorAll("[data-company-id] .toggle_company")].map((el) =>
el.getAttribute("aria-checked")
),
["true", "false", "false", "false", "false"]
);
assert.deepEqual(
[...target.querySelectorAll("[data-company-id] .log_into")].map((el) => el.getAttribute('aria-pressed')),
[...target.querySelectorAll("[data-company-id] .log_into")].map((el) =>
el.getAttribute("aria-pressed")
),
["true", "false", "false", "false", "false"]
);
@@ -123,17 +125,19 @@ QUnit.module("SwitchCompanyMenu", (hooks) => {
assert.containsN(target, "[data-company-id] .fa-check-square", 2);
assert.containsN(target, "[data-company-id] .fa-square-o", 3);
assert.deepEqual(
[...target.querySelectorAll("[data-company-id] .toggle_company")].map(
(el) => el.getAttribute('aria-checked')
[...target.querySelectorAll("[data-company-id] .toggle_company")].map((el) =>
el.getAttribute("aria-checked")
),
["true", "true", "false", "false", "false"]
);
assert.deepEqual(
[...target.querySelectorAll("[data-company-id] .log_into")].map((el) => el.getAttribute('aria-pressed')),
[...target.querySelectorAll("[data-company-id] .log_into")].map((el) =>
el.getAttribute("aria-pressed")
),
["true", "false", "false", "false", "false"]
);
await prom;
assert.verifySteps(["cids=3%2C2"]);
assert.verifySteps(["cids=3-2"]);
});
QUnit.test("can toggle multiple companies at once", async (assert) => {
@@ -176,7 +180,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => {
assert.verifySteps([]);
await prom; // await toggle promise
assert.verifySteps(["cids=2%2C1%2C4%2C5"]);
assert.verifySteps(["cids=2-1-4-5"]);
});
QUnit.test("single company selected: toggling it off will keep it", async (assert) => {
@@ -260,7 +264,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => {
function onPushState(url) {
assert.step(url.split("#")[1]);
}
Object.assign(browser.location, { hash: "cids=3%2C1" });
Object.assign(browser.location, { hash: "cids=3-1" });
const scMenu = await createSwitchCompanyMenu({ onPushState });
/**
@@ -295,7 +299,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => {
function onPushState(url) {
assert.step(url.split("#")[1]);
}
Object.assign(browser.location, { hash: "cids=2%2C1" });
Object.assign(browser.location, { hash: "cids=2-1" });
const scMenu = await createSwitchCompanyMenu({ onPushState });
/**
@@ -321,7 +325,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => {
*/
await click(target.querySelectorAll(".log_into")[2]);
assert.containsNone(target, ".dropdown-menu", "dropdown is directly closed");
assert.verifySteps(["cids=1%2C4%2C5"]);
assert.verifySteps(["cids=1-4-5"]);
});
QUnit.test("companies can be logged in even if some toggled within delay", async (assert) => {