Skip to content

Commit 599bac9

Browse files
committed
LiveUrlSubscriber should handle only path and query
1 parent 9d8fb3d commit 599bac9

File tree

5 files changed

+115
-132
lines changed

5 files changed

+115
-132
lines changed

src/LiveComponent/assets/dist/live_controller.js

Lines changed: 106 additions & 106 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;
@@ -1984,6 +1984,110 @@ class ValueStore {
19841984
}
19851985
}
19861986

1987+
function isValueEmpty(value) {
1988+
if (null === value || value === '' || undefined === value || (Array.isArray(value) && value.length === 0)) {
1989+
return true;
1990+
}
1991+
if (typeof value !== 'object') {
1992+
return false;
1993+
}
1994+
for (const key of Object.keys(value)) {
1995+
if (!isValueEmpty(value[key])) {
1996+
return false;
1997+
}
1998+
}
1999+
return true;
2000+
}
2001+
function toQueryString(data) {
2002+
const buildQueryStringEntries = (data, entries = {}, baseKey = '') => {
2003+
Object.entries(data).forEach(([iKey, iValue]) => {
2004+
const key = baseKey === '' ? iKey : `${baseKey}[${iKey}]`;
2005+
if ('' === baseKey && isValueEmpty(iValue)) {
2006+
entries[key] = '';
2007+
}
2008+
else if (null !== iValue) {
2009+
if (typeof iValue === 'object') {
2010+
entries = { ...entries, ...buildQueryStringEntries(iValue, entries, key) };
2011+
}
2012+
else {
2013+
entries[key] = encodeURIComponent(iValue)
2014+
.replace(/%20/g, '+')
2015+
.replace(/%2C/g, ',');
2016+
}
2017+
}
2018+
});
2019+
return entries;
2020+
};
2021+
const entries = buildQueryStringEntries(data);
2022+
return Object.entries(entries)
2023+
.map(([key, value]) => `${key}=${value}`)
2024+
.join('&');
2025+
}
2026+
function fromQueryString(search) {
2027+
search = search.replace('?', '');
2028+
if (search === '')
2029+
return {};
2030+
const insertDotNotatedValueIntoData = (key, value, data) => {
2031+
const [first, second, ...rest] = key.split('.');
2032+
if (!second) {
2033+
data[key] = value;
2034+
return value;
2035+
}
2036+
if (data[first] === undefined) {
2037+
data[first] = Number.isNaN(Number.parseInt(second)) ? {} : [];
2038+
}
2039+
insertDotNotatedValueIntoData([second, ...rest].join('.'), value, data[first]);
2040+
};
2041+
const entries = search.split('&').map((i) => i.split('='));
2042+
const data = {};
2043+
entries.forEach(([key, value]) => {
2044+
value = decodeURIComponent(String(value || '').replace(/\+/g, '%20'));
2045+
if (!key.includes('[')) {
2046+
data[key] = value;
2047+
}
2048+
else {
2049+
if ('' === value)
2050+
return;
2051+
const dotNotatedKey = key.replace(/\[/g, '.').replace(/]/g, '');
2052+
insertDotNotatedValueIntoData(dotNotatedKey, value, data);
2053+
}
2054+
});
2055+
return data;
2056+
}
2057+
class UrlUtils extends URL {
2058+
has(key) {
2059+
const data = this.getData();
2060+
return Object.keys(data).includes(key);
2061+
}
2062+
set(key, value) {
2063+
const data = this.getData();
2064+
data[key] = value;
2065+
this.setData(data);
2066+
}
2067+
get(key) {
2068+
return this.getData()[key];
2069+
}
2070+
remove(key) {
2071+
const data = this.getData();
2072+
delete data[key];
2073+
this.setData(data);
2074+
}
2075+
getData() {
2076+
if (!this.search) {
2077+
return {};
2078+
}
2079+
return fromQueryString(this.search);
2080+
}
2081+
setData(data) {
2082+
this.search = toQueryString(data);
2083+
}
2084+
}
2085+
class HistoryStrategy {
2086+
static replace(url) {
2087+
history.replaceState(history.state, '', url);
2088+
}
2089+
}
2090+
19872091
class Component {
19882092
constructor(element, name, props, listeners, id, backend, elementDriver) {
19892093
this.fingerprint = '';
@@ -2155,7 +2259,7 @@ class Component {
21552259
this.processRerender(html, backendResponse);
21562260
const liveUrl = await backendResponse.getLiveUrl();
21572261
if (liveUrl) {
2158-
HistoryStrategy.replace(new UrlUtils(liveUrl));
2262+
HistoryStrategy.replace(new UrlUtils(liveUrl + window.location.hash, window.location.origin));
21592263
}
21602264
this.backendRequest = null;
21612265
thisPromiseResolve(backendResponse);
@@ -2781,110 +2885,6 @@ class PollingPlugin {
27812885
}
27822886
}
27832887

2784-
function isValueEmpty(value) {
2785-
if (null === value || value === '' || undefined === value || (Array.isArray(value) && value.length === 0)) {
2786-
return true;
2787-
}
2788-
if (typeof value !== 'object') {
2789-
return false;
2790-
}
2791-
for (const key of Object.keys(value)) {
2792-
if (!isValueEmpty(value[key])) {
2793-
return false;
2794-
}
2795-
}
2796-
return true;
2797-
}
2798-
function toQueryString(data) {
2799-
const buildQueryStringEntries = (data, entries = {}, baseKey = '') => {
2800-
Object.entries(data).forEach(([iKey, iValue]) => {
2801-
const key = baseKey === '' ? iKey : `${baseKey}[${iKey}]`;
2802-
if ('' === baseKey && isValueEmpty(iValue)) {
2803-
entries[key] = '';
2804-
}
2805-
else if (null !== iValue) {
2806-
if (typeof iValue === 'object') {
2807-
entries = { ...entries, ...buildQueryStringEntries(iValue, entries, key) };
2808-
}
2809-
else {
2810-
entries[key] = encodeURIComponent(iValue)
2811-
.replace(/%20/g, '+')
2812-
.replace(/%2C/g, ',');
2813-
}
2814-
}
2815-
});
2816-
return entries;
2817-
};
2818-
const entries = buildQueryStringEntries(data);
2819-
return Object.entries(entries)
2820-
.map(([key, value]) => `${key}=${value}`)
2821-
.join('&');
2822-
}
2823-
function fromQueryString(search) {
2824-
search = search.replace('?', '');
2825-
if (search === '')
2826-
return {};
2827-
const insertDotNotatedValueIntoData = (key, value, data) => {
2828-
const [first, second, ...rest] = key.split('.');
2829-
if (!second) {
2830-
data[key] = value;
2831-
return value;
2832-
}
2833-
if (data[first] === undefined) {
2834-
data[first] = Number.isNaN(Number.parseInt(second)) ? {} : [];
2835-
}
2836-
insertDotNotatedValueIntoData([second, ...rest].join('.'), value, data[first]);
2837-
};
2838-
const entries = search.split('&').map((i) => i.split('='));
2839-
const data = {};
2840-
entries.forEach(([key, value]) => {
2841-
value = decodeURIComponent(String(value || '').replace(/\+/g, '%20'));
2842-
if (!key.includes('[')) {
2843-
data[key] = value;
2844-
}
2845-
else {
2846-
if ('' === value)
2847-
return;
2848-
const dotNotatedKey = key.replace(/\[/g, '.').replace(/]/g, '');
2849-
insertDotNotatedValueIntoData(dotNotatedKey, value, data);
2850-
}
2851-
});
2852-
return data;
2853-
}
2854-
class UrlUtils extends URL {
2855-
has(key) {
2856-
const data = this.getData();
2857-
return Object.keys(data).includes(key);
2858-
}
2859-
set(key, value) {
2860-
const data = this.getData();
2861-
data[key] = value;
2862-
this.setData(data);
2863-
}
2864-
get(key) {
2865-
return this.getData()[key];
2866-
}
2867-
remove(key) {
2868-
const data = this.getData();
2869-
delete data[key];
2870-
this.setData(data);
2871-
}
2872-
getData() {
2873-
if (!this.search) {
2874-
return {};
2875-
}
2876-
return fromQueryString(this.search);
2877-
}
2878-
setData(data) {
2879-
this.search = toQueryString(data);
2880-
}
2881-
}
2882-
class HistoryStrategy {
2883-
static replace(url) {
2884-
history.replaceState(history.state, '', url);
2885-
}
2886-
}
2887-
28882888
class QueryStringPlugin {
28892889
constructor(mapping) {
28902890
this.mapping = mapping;

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)