Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
9cdd021cda
|
|||
|
ef53870b35
|
|||
|
918a794784
|
|||
|
344a3067fa
|
|||
|
ad3e6ea402
|
|||
|
9e2545da7b
|
|||
|
480c97fb9d
|
|||
|
179cf49370
|
|||
|
aea0388267
|
|||
|
e4d02cda26
|
@@ -130,15 +130,24 @@ export default class PlaceDetails extends Component {
|
|||||||
|
|
||||||
formatMultiLine(val, type) {
|
formatMultiLine(val, type) {
|
||||||
if (!val) return null;
|
if (!val) return null;
|
||||||
const parts = val
|
const parts = [
|
||||||
.split(';')
|
...new Set(
|
||||||
.map((s) => s.trim())
|
val
|
||||||
.filter(Boolean);
|
.split(';')
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
),
|
||||||
|
];
|
||||||
if (parts.length === 0) return null;
|
if (parts.length === 0) return null;
|
||||||
|
|
||||||
if (type === 'phone') {
|
if (type === 'phone') {
|
||||||
return htmlSafe(
|
return htmlSafe(
|
||||||
parts.map((p) => `<a href="tel:${p}">${p}</a>`).join('<br>')
|
parts
|
||||||
|
.map((p) => {
|
||||||
|
const safeTel = p.replace(/[\s-]+/g, '');
|
||||||
|
return `<a href="tel:${safeTel}">${p}</a>`;
|
||||||
|
})
|
||||||
|
.join('<br>')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +157,17 @@ export default class PlaceDetails extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === 'whatsapp') {
|
||||||
|
return htmlSafe(
|
||||||
|
parts
|
||||||
|
.map((p) => {
|
||||||
|
const safeTel = p.replace(/[\s-]+/g, '');
|
||||||
|
return `<a href="https://wa.me/${safeTel}" target="_blank" rel="noopener noreferrer">${p}</a>`;
|
||||||
|
})
|
||||||
|
.join('<br>')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (type === 'url') {
|
if (type === 'url') {
|
||||||
return htmlSafe(
|
return htmlSafe(
|
||||||
parts
|
parts
|
||||||
@@ -165,8 +185,27 @@ export default class PlaceDetails extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get phone() {
|
get phone() {
|
||||||
const val = this.tags.phone || this.tags['contact:phone'];
|
const rawValues = [
|
||||||
return this.formatMultiLine(val, 'phone');
|
this.tags.phone,
|
||||||
|
this.tags['contact:phone'],
|
||||||
|
this.tags.mobile,
|
||||||
|
this.tags['contact:mobile'],
|
||||||
|
].filter(Boolean);
|
||||||
|
|
||||||
|
if (rawValues.length === 0) return null;
|
||||||
|
|
||||||
|
return this.formatMultiLine(rawValues.join(';'), 'phone');
|
||||||
|
}
|
||||||
|
|
||||||
|
get whatsapp() {
|
||||||
|
const rawValues = [
|
||||||
|
this.tags.whatsapp,
|
||||||
|
this.tags['contact:whatsapp'],
|
||||||
|
].filter(Boolean);
|
||||||
|
|
||||||
|
if (rawValues.length === 0) return null;
|
||||||
|
|
||||||
|
return this.formatMultiLine(rawValues.join(';'), 'whatsapp');
|
||||||
}
|
}
|
||||||
|
|
||||||
get email() {
|
get email() {
|
||||||
@@ -343,6 +382,15 @@ export default class PlaceDetails extends Component {
|
|||||||
</p>
|
</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.whatsapp}}
|
||||||
|
<p class="content-with-icon">
|
||||||
|
<Icon @name="whatsapp" @title="WhatsApp" />
|
||||||
|
<span>
|
||||||
|
{{this.whatsapp}}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{#if this.website}}
|
{{#if this.website}}
|
||||||
<p class="content-with-icon">
|
<p class="content-with-icon">
|
||||||
<Icon @name="globe" @title="Website" />
|
<Icon @name="globe" @title="Website" />
|
||||||
|
|||||||
4
app/icons/whatsapp.svg
Normal file
4
app/icons/whatsapp.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg viewBox="-1.66 0 740.82 740.82" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m630.06 107.66c-69.329-69.387-161.53-107.62-259.76-107.66-202.4 0-367.13 164.67-367.22 367.07-0.027 64.699 16.883 127.86 49.016 183.52l-52.095 190.23 194.67-51.047c53.634 29.244 114.02 44.656 175.48 44.682h0.151c202.38 0 367.13-164.69 367.21-367.09 0.039-98.088-38.121-190.32-107.45-259.71m-259.76 564.8h-0.125c-54.766-0.021-108.48-14.729-155.34-42.529l-11.146-6.613-115.52 30.293 30.834-112.59-7.258-11.543c-30.552-48.58-46.689-104.73-46.665-162.38 0.067-168.23 136.99-305.1 305.34-305.1 81.521 0.031 158.15 31.81 215.78 89.482s89.342 134.33 89.311 215.86c-0.07 168.24-136.99 305.12-305.21 305.12m167.42-228.51c-9.176-4.591-54.286-26.782-62.697-29.843-8.41-3.061-14.526-4.591-20.644 4.592-6.116 9.182-23.7 29.843-29.054 35.964-5.351 6.122-10.703 6.888-19.879 2.296-9.175-4.591-38.739-14.276-73.786-45.526-27.275-24.32-45.691-54.36-51.043-63.542-5.352-9.183-0.569-14.148 4.024-18.72 4.127-4.11 9.175-10.713 13.763-16.07 4.587-5.356 6.116-9.182 9.174-15.303 3.059-6.122 1.53-11.479-0.764-16.07s-20.643-49.739-28.29-68.104c-7.447-17.886-15.012-15.466-20.644-15.746-5.346-0.266-11.469-0.323-17.585-0.323-6.117 0-16.057 2.296-24.468 11.478-8.41 9.183-32.112 31.374-32.112 76.521s32.877 88.763 37.465 94.885c4.587 6.122 64.699 98.771 156.74 138.5 21.891 9.45 38.982 15.093 52.307 19.323 21.981 6.979 41.983 5.994 57.793 3.633 17.628-2.633 54.285-22.19 61.932-43.616 7.646-21.426 7.646-39.791 5.352-43.617-2.293-3.826-8.41-6.122-17.585-10.714" clip-rule="evenodd" fill-rule="evenodd"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -7,7 +7,15 @@ class MarcoOsmAuthStorage {
|
|||||||
localStorage.setItem('marco:osm_auth_state', serializedState);
|
localStorage.setItem('marco:osm_auth_state', serializedState);
|
||||||
}
|
}
|
||||||
loadState() {
|
loadState() {
|
||||||
return localStorage.getItem('marco:osm_auth_state');
|
const state = localStorage.getItem('marco:osm_auth_state');
|
||||||
|
if (!state) return false;
|
||||||
|
try {
|
||||||
|
JSON.parse(state);
|
||||||
|
return state;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to parse OSM auth state', e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +53,11 @@ export default class OsmAuthService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async restoreSession() {
|
async restoreSession() {
|
||||||
|
try {
|
||||||
|
await this.oauthClient.ready;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('oauthClient.ready failed', e);
|
||||||
|
}
|
||||||
const isAuthorized = await this.oauthClient.isAuthorized();
|
const isAuthorized = await this.oauthClient.isAuthorized();
|
||||||
if (isAuthorized) {
|
if (isAuthorized) {
|
||||||
this.isConnected = true;
|
this.isConnected = true;
|
||||||
|
|||||||
@@ -267,7 +267,6 @@ body {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-text {
|
.btn-text {
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ import womensAndMensRestroomSymbol from '@waysidemapping/pinhead/dist/icons/wome
|
|||||||
import loadingRing from '../icons/270-ring.svg?raw';
|
import loadingRing from '../icons/270-ring.svg?raw';
|
||||||
import nostrich from '../icons/nostrich-2.svg?raw';
|
import nostrich from '../icons/nostrich-2.svg?raw';
|
||||||
import remotestorage from '../icons/remotestorage.svg?raw';
|
import remotestorage from '../icons/remotestorage.svg?raw';
|
||||||
|
import whatsapp from '../icons/whatsapp.svg?raw';
|
||||||
import wikipedia from '../icons/wikipedia.svg?raw';
|
import wikipedia from '../icons/wikipedia.svg?raw';
|
||||||
|
|
||||||
const ICONS = {
|
const ICONS = {
|
||||||
@@ -218,6 +219,7 @@ const ICONS = {
|
|||||||
'village-buildings': villageBuildings,
|
'village-buildings': villageBuildings,
|
||||||
'wall-hanging-with-mountains-and-sun': wallHangingWithMountainsAndSun,
|
'wall-hanging-with-mountains-and-sun': wallHangingWithMountainsAndSun,
|
||||||
'womens-and-mens-restroom-symbol': womensAndMensRestroomSymbol,
|
'womens-and-mens-restroom-symbol': womensAndMensRestroomSymbol,
|
||||||
|
whatsapp,
|
||||||
wikipedia,
|
wikipedia,
|
||||||
parking_p: parkingP,
|
parking_p: parkingP,
|
||||||
car,
|
car,
|
||||||
@@ -229,6 +231,7 @@ const ICONS = {
|
|||||||
const FILLED_ICONS = [
|
const FILLED_ICONS = [
|
||||||
'fork-and-knife',
|
'fork-and-knife',
|
||||||
'wikipedia',
|
'wikipedia',
|
||||||
|
'whatsapp',
|
||||||
'cup-and-saucer',
|
'cup-and-saucer',
|
||||||
'coffee-bean',
|
'coffee-bean',
|
||||||
'shopping-basket',
|
'shopping-basket',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "marco",
|
"name": "marco",
|
||||||
"version": "1.18.2",
|
"version": "1.19.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Unhosted maps app",
|
"description": "Unhosted maps app",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
1
release/assets/main-BF2Ls-fG.css
Normal file
1
release/assets/main-BF2Ls-fG.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
release/assets/main-iQCWrjXX.js
Normal file
2
release/assets/main-iQCWrjXX.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -39,8 +39,8 @@
|
|||||||
<meta name="msapplication-TileColor" content="#F6E9A6">
|
<meta name="msapplication-TileColor" content="#F6E9A6">
|
||||||
<meta name="msapplication-TileImage" content="/icons/icon-144.png">
|
<meta name="msapplication-TileImage" content="/icons/icon-144.png">
|
||||||
|
|
||||||
<script type="module" crossorigin src="/assets/main-BZnE0aU1.js"></script>
|
<script type="module" crossorigin src="/assets/main-iQCWrjXX.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/main-NvtkUYBk.css">
|
<link rel="stylesheet" crossorigin href="/assets/main-BF2Ls-fG.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -255,4 +255,83 @@ module('Integration | Component | place-details', function (hooks) {
|
|||||||
assert.dom('.actions button').hasText('Save');
|
assert.dom('.actions button').hasText('Save');
|
||||||
assert.dom('.actions button').doesNotHaveClass('btn-secondary');
|
assert.dom('.actions button').doesNotHaveClass('btn-secondary');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('it aggregates phone and mobile tags without duplicates', async function (assert) {
|
||||||
|
const place = {
|
||||||
|
title: 'Phone Shop',
|
||||||
|
osmTags: {
|
||||||
|
phone: '+1-234-567-8900',
|
||||||
|
'contact:phone': '+1-234-567-8900; +1 000 000 0000',
|
||||||
|
mobile: '+1 987 654 3210',
|
||||||
|
'contact:mobile': '+1 987 654 3210',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await render(<template><PlaceDetails @place={{place}} /></template>);
|
||||||
|
|
||||||
|
// Use specific selector for the phone block since there's no cuisine or opening_hours
|
||||||
|
const metaInfos = Array.from(
|
||||||
|
this.element.querySelectorAll('.meta-info .content-with-icon')
|
||||||
|
);
|
||||||
|
const phoneBlock = metaInfos.find((el) => {
|
||||||
|
const iconSpan = el.querySelector('span.icon[title="Phone"]');
|
||||||
|
return !!iconSpan;
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(phoneBlock, 'Phone block is rendered');
|
||||||
|
|
||||||
|
const links = phoneBlock.querySelectorAll('a[href^="tel:"]');
|
||||||
|
assert.strictEqual(
|
||||||
|
links.length,
|
||||||
|
3,
|
||||||
|
'Rendered exactly 3 unique phone links'
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.strictEqual(links[0].getAttribute('href'), 'tel:+12345678900');
|
||||||
|
assert.strictEqual(links[1].getAttribute('href'), 'tel:+10000000000');
|
||||||
|
assert.strictEqual(links[2].getAttribute('href'), 'tel:+19876543210');
|
||||||
|
|
||||||
|
assert.dom(links[0]).hasText('+1-234-567-8900');
|
||||||
|
assert.dom(links[1]).hasText('+1 000 000 0000');
|
||||||
|
assert.dom(links[2]).hasText('+1 987 654 3210');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it formats whatsapp tags into wa.me links', async function (assert) {
|
||||||
|
const place = {
|
||||||
|
title: 'Chat Shop',
|
||||||
|
osmTags: {
|
||||||
|
'contact:whatsapp': '+1 234-567 8900',
|
||||||
|
whatsapp: '+44 987 654 321', // Also tests multiple values
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await render(<template><PlaceDetails @place={{place}} /></template>);
|
||||||
|
|
||||||
|
const metaInfos = Array.from(
|
||||||
|
this.element.querySelectorAll('.meta-info .content-with-icon')
|
||||||
|
);
|
||||||
|
const whatsappBlock = metaInfos.find((el) => {
|
||||||
|
const iconSpan = el.querySelector('span.icon[title="WhatsApp"]');
|
||||||
|
return !!iconSpan;
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(whatsappBlock, 'WhatsApp block is rendered');
|
||||||
|
|
||||||
|
const links = whatsappBlock.querySelectorAll('a[href^="https://wa.me/"]');
|
||||||
|
assert.strictEqual(links.length, 2, 'Rendered exactly 2 WhatsApp links');
|
||||||
|
|
||||||
|
// Verify it stripped the dashes and spaces for the wa.me URL
|
||||||
|
assert.strictEqual(
|
||||||
|
links[0].getAttribute('href'),
|
||||||
|
'https://wa.me/+44987654321'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
links[1].getAttribute('href'),
|
||||||
|
'https://wa.me/+12345678900'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify it kept the dashes and spaces for the visible text
|
||||||
|
assert.dom(links[0]).hasText('+44 987 654 321');
|
||||||
|
assert.dom(links[1]).hasText('+1 234-567 8900');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ module('Unit | Service | osm-auth', function (hooks) {
|
|||||||
// Because restoreSession runs in the constructor, we might need to overwrite it after, but it's async.
|
// Because restoreSession runs in the constructor, we might need to overwrite it after, but it's async.
|
||||||
// Let's just create it, let the original restoreSession fail or do nothing, and then we stub and re-call it.
|
// Let's just create it, let the original restoreSession fail or do nothing, and then we stub and re-call it.
|
||||||
|
|
||||||
|
service.oauthClient.ready = Promise.resolve();
|
||||||
service.oauthClient.isAuthorized = async () => true;
|
service.oauthClient.isAuthorized = async () => true;
|
||||||
window.localStorage.setItem('marco:osm_user_display_name', 'CachedName');
|
window.localStorage.setItem('marco:osm_user_display_name', 'CachedName');
|
||||||
|
|
||||||
@@ -68,6 +69,7 @@ module('Unit | Service | osm-auth', function (hooks) {
|
|||||||
test('it fetches user info when logged in but no cached name', async function (assert) {
|
test('it fetches user info when logged in but no cached name', async function (assert) {
|
||||||
let service = this.owner.factoryFor('service:osm-auth').create();
|
let service = this.owner.factoryFor('service:osm-auth').create();
|
||||||
|
|
||||||
|
service.oauthClient.ready = Promise.resolve();
|
||||||
service.oauthClient.isAuthorized = async () => true;
|
service.oauthClient.isAuthorized = async () => true;
|
||||||
service.oauthClient.getTokens = async () => ({ accessToken: 'fake-token' });
|
service.oauthClient.getTokens = async () => ({ accessToken: 'fake-token' });
|
||||||
// Ensure localStorage is empty for this key
|
// Ensure localStorage is empty for this key
|
||||||
|
|||||||
Reference in New Issue
Block a user