Skip to content

Commit 03f63cc

Browse files
committed
LiveUrlSubscriber should handle only path and query
1 parent 18b1af0 commit 03f63cc

File tree

5 files changed

+116
-133
lines changed

5 files changed

+116
-133
lines changed

src/LiveComponent/assets/dist/live_controller.js

Lines changed: 107 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class RequestBuilder {
3333
fetchOptions.headers = {
3434
Accept: 'application/vnd.live-component+html',
3535
'X-Requested-With': 'XMLHttpRequest',
36-
'X-Live-Url': window.location.href
36+
'X-Live-Url': window.location.pathname + window.location.search
3737
};
3838
const totalFiles = Object.entries(files).reduce((total, current) => total + current.length, 0);
3939
const hasFingerprints = Object.keys(children).length > 0;
@@ -1975,6 +1975,110 @@ class ValueStore {
19751975
}
19761976
}
19771977

1978+
function isValueEmpty(value) {
1979+
if (null === value || value === '' || undefined === value || (Array.isArray(value) && value.length === 0)) {
1980+
return true;
1981+
}
1982+
if (typeof value !== 'object') {
1983+
return false;
1984+
}
1985+
for (const key of Object.keys(value)) {
1986+
if (!isValueEmpty(value[key])) {
1987+
return false;
1988+
}
1989+
}
1990+
return true;
1991+
}
1992+
function toQueryString(data) {
1993+
const buildQueryStringEntries = (data, entries = {}, baseKey = '') => {
1994+
Object.entries(data).forEach(([iKey, iValue]) => {
1995+
const key = baseKey === '' ? iKey : `${baseKey}[${iKey}]`;
1996+
if ('' === baseKey && isValueEmpty(iValue)) {
1997+
entries[key] = '';
1998+
}
1999+
else if (null !== iValue) {
2000+
if (typeof iValue === 'object') {
2001+
entries = { ...entries, ...buildQueryStringEntries(iValue, entries, key) };
2002+
}
2003+
else {
2004+
entries[key] = encodeURIComponent(iValue)
2005+
.replace(/%20/g, '+')
2006+
.replace(/%2C/g, ',');
2007+
}
2008+
}
2009+
});
2010+
return entries;
2011+
};
2012+
const entries = buildQueryStringEntries(data);
2013+
return Object.entries(entries)
2014+
.map(([key, value]) => `${key}=${value}`)
2015+
.join('&');
2016+
}
2017+
function fromQueryString(search) {
2018+
search = search.replace('?', '');
2019+
if (search === '')
2020+
return {};
2021+
const insertDotNotatedValueIntoData = (key, value, data) => {
2022+
const [first, second, ...rest] = key.split('.');
2023+
if (!second) {
2024+
data[key] = value;
2025+
return value;
2026+
}
2027+
if (data[first] === undefined) {
2028+
data[first] = Number.isNaN(Number.parseInt(second)) ? {} : [];
2029+
}
2030+
insertDotNotatedValueIntoData([second, ...rest].join('.'), value, data[first]);
2031+
};
2032+
const entries = search.split('&').map((i) => i.split('='));
2033+
const data = {};
2034+
entries.forEach(([key, value]) => {
2035+
value = decodeURIComponent(value.replace(/\+/g, '%20'));
2036+
if (!key.includes('[')) {
2037+
data[key] = value;
2038+
}
2039+
else {
2040+
if ('' === value)
2041+
return;
2042+
const dotNotatedKey = key.replace(/\[/g, '.').replace(/]/g, '');
2043+
insertDotNotatedValueIntoData(dotNotatedKey, value, data);
2044+
}
2045+
});
2046+
return data;
2047+
}
2048+
class UrlUtils extends URL {
2049+
has(key) {
2050+
const data = this.getData();
2051+
return Object.keys(data).includes(key);
2052+
}
2053+
set(key, value) {
2054+
const data = this.getData();
2055+
data[key] = value;
2056+
this.setData(data);
2057+
}
2058+
get(key) {
2059+
return this.getData()[key];
2060+
}
2061+
remove(key) {
2062+
const data = this.getData();
2063+
delete data[key];
2064+
this.setData(data);
2065+
}
2066+
getData() {
2067+
if (!this.search) {
2068+
return {};
2069+
}
2070+
return fromQueryString(this.search);
2071+
}
2072+
setData(data) {
2073+
this.search = toQueryString(data);
2074+
}
2075+
}
2076+
class HistoryStrategy {
2077+
static replace(url) {
2078+
history.replaceState(history.state, '', url);
2079+
}
2080+
}
2081+
19782082
class Component {
19792083
constructor(element, name, props, listeners, id, backend, elementDriver) {
19802084
this.fingerprint = '';
@@ -2146,7 +2250,7 @@ class Component {
21462250
this.processRerender(html, backendResponse);
21472251
const liveUrl = await backendResponse.getLiveUrl();
21482252
if (liveUrl) {
2149-
HistoryStrategy.replace(new UrlUtils(liveUrl));
2253+
HistoryStrategy.replace(new UrlUtils(liveUrl + window.location.hash, window.location.origin));
21502254
}
21512255
this.backendRequest = null;
21522256
thisPromiseResolve(backendResponse);
@@ -2752,110 +2856,6 @@ class PollingPlugin {
27522856
}
27532857
}
27542858

2755-
function isValueEmpty(value) {
2756-
if (null === value || value === '' || undefined === value || (Array.isArray(value) && value.length === 0)) {
2757-
return true;
2758-
}
2759-
if (typeof value !== 'object') {
2760-
return false;
2761-
}
2762-
for (const key of Object.keys(value)) {
2763-
if (!isValueEmpty(value[key])) {
2764-
return false;
2765-
}
2766-
}
2767-
return true;
2768-
}
2769-
function toQueryString(data) {
2770-
const buildQueryStringEntries = (data, entries = {}, baseKey = '') => {
2771-
Object.entries(data).forEach(([iKey, iValue]) => {
2772-
const key = baseKey === '' ? iKey : `${baseKey}[${iKey}]`;
2773-
if ('' === baseKey && isValueEmpty(iValue)) {
2774-
entries[key] = '';
2775-
}
2776-
else if (null !== iValue) {
2777-
if (typeof iValue === 'object') {
2778-
entries = { ...entries, ...buildQueryStringEntries(iValue, entries, key) };
2779-
}
2780-
else {
2781-
entries[key] = encodeURIComponent(iValue)
2782-
.replace(/%20/g, '+')
2783-
.replace(/%2C/g, ',');
2784-
}
2785-
}
2786-
});
2787-
return entries;
2788-
};
2789-
const entries = buildQueryStringEntries(data);
2790-
return Object.entries(entries)
2791-
.map(([key, value]) => `${key}=${value}`)
2792-
.join('&');
2793-
}
2794-
function fromQueryString(search) {
2795-
search = search.replace('?', '');
2796-
if (search === '')
2797-
return {};
2798-
const insertDotNotatedValueIntoData = (key, value, data) => {
2799-
const [first, second, ...rest] = key.split('.');
2800-
if (!second) {
2801-
data[key] = value;
2802-
return value;
2803-
}
2804-
if (data[first] === undefined) {
2805-
data[first] = Number.isNaN(Number.parseInt(second)) ? {} : [];
2806-
}
2807-
insertDotNotatedValueIntoData([second, ...rest].join('.'), value, data[first]);
2808-
};
2809-
const entries = search.split('&').map((i) => i.split('='));
2810-
const data = {};
2811-
entries.forEach(([key, value]) => {
2812-
value = decodeURIComponent(value.replace(/\+/g, '%20'));
2813-
if (!key.includes('[')) {
2814-
data[key] = value;
2815-
}
2816-
else {
2817-
if ('' === value)
2818-
return;
2819-
const dotNotatedKey = key.replace(/\[/g, '.').replace(/]/g, '');
2820-
insertDotNotatedValueIntoData(dotNotatedKey, value, data);
2821-
}
2822-
});
2823-
return data;
2824-
}
2825-
class UrlUtils extends URL {
2826-
has(key) {
2827-
const data = this.getData();
2828-
return Object.keys(data).includes(key);
2829-
}
2830-
set(key, value) {
2831-
const data = this.getData();
2832-
data[key] = value;
2833-
this.setData(data);
2834-
}
2835-
get(key) {
2836-
return this.getData()[key];
2837-
}
2838-
remove(key) {
2839-
const data = this.getData();
2840-
delete data[key];
2841-
this.setData(data);
2842-
}
2843-
getData() {
2844-
if (!this.search) {
2845-
return {};
2846-
}
2847-
return fromQueryString(this.search);
2848-
}
2849-
setData(data) {
2850-
this.search = toQueryString(data);
2851-
}
2852-
}
2853-
class HistoryStrategy {
2854-
static replace(url) {
2855-
history.replaceState(history.state, '', url);
2856-
}
2857-
}
2858-
28592859
class QueryStringPlugin {
28602860
constructor(mapping) {
28612861
this.mapping = mapping;
@@ -2869,7 +2869,7 @@ class QueryStringPlugin {
28692869
urlUtils.set(mapping.name, value);
28702870
});
28712871
if (currentUrl !== urlUtils.toString()) {
2872-
HistoryStrategy.replace(urlUtils);
2872+
HistoryStrategy.replace(new UrlUtils(currentUrl));
28732873
}
28742874
});
28752875
}

src/LiveComponent/assets/src/Backend/RequestBuilder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export default class {
2626
fetchOptions.headers = {
2727
Accept: 'application/vnd.live-component+html',
2828
'X-Requested-With': 'XMLHttpRequest',
29-
'X-Live-Url' : window.location.href
29+
'X-Live-Url' : window.location.pathname + window.location.search
3030
};
3131

3232
const totalFiles = Object.entries(files).reduce((total, current) => total + current.length, 0);

src/LiveComponent/assets/src/Component/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ export default class Component {
331331
this.processRerender(html, backendResponse);
332332
const liveUrl = await backendResponse.getLiveUrl();
333333
if (liveUrl) {
334-
HistoryStrategy.replace(new UrlUtils(liveUrl));
334+
HistoryStrategy.replace(new UrlUtils(liveUrl + window.location.hash, window.location.origin));
335335
}
336336

337337
// finally resolve this promise

src/LiveComponent/assets/src/Component/plugins/QueryStringPlugin.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ interface QueryMapping {
1212
export default class implements PluginInterface {
1313
constructor(private readonly mapping: { [p: string]: QueryMapping }) {}
1414

15+
//@todo delete
1516
attachToComponent(component: Component): void {
1617
component.on('render:finished', (component: Component) => {
1718
const urlUtils = new UrlUtils(window.location.href);

src/LiveComponent/src/EventListener/LiveUrlSubscriber.php

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -79,38 +79,20 @@ private function getLivePropsToMap(Request $request): array
7979
return $urlLiveProps;
8080
}
8181

82-
// @todo use requestStack ?
8382
private function computeNewUrl(string $previousUrl, array $newProps): string
8483
{
8584
$parsed = parse_url($previousUrl);
86-
$baseUrl = $parsed['scheme'].'://';
87-
if (isset($parsed['user'])) {
88-
$baseUrl .= $parsed['user'];
89-
if (isset($parsed['pass'])) {
90-
$baseUrl .= ':'.$parsed['pass'];
91-
}
92-
$baseUrl .= '@';
93-
}
94-
$baseUrl .= $parsed['host'];
95-
if (isset($parsed['port'])) {
96-
$baseUrl .= ':'.$parsed['port'];
97-
}
9885

99-
$path = $parsed['path'] ?? '';
86+
$url = $parsed['path'] ?? '';
10087
if (isset($parsed['query'])) {
101-
$path .= '?'.$parsed['query'];
88+
$url .= '?'.$parsed['query'];
10289
}
103-
parse_str($parsed['query'] ?? '', $previousParams);
90+
parse_str($parsed['query'] ?? '', $previousQueryParams);
10491

105-
$match = $this->router->match($path);
106-
$newUrl = $this->router->generate(
107-
$match['_route'],
108-
array_merge($previousParams, $newProps)
92+
return $this->router->generate(
93+
$this->router->match($url)['_route'],
94+
array_merge($previousQueryParams, $newProps)
10995
);
110-
111-
$fragment = $parsed['fragment'] ?? '';
112-
113-
return $baseUrl.$newUrl.$fragment;
11496
}
11597

11698
/**

0 commit comments

Comments
 (0)