diff --git a/app/components/icon.gjs b/app/components/icon.gjs index b00dc5f..d043420 100644 --- a/app/components/icon.gjs +++ b/app/components/icon.gjs @@ -6,8 +6,10 @@ import activity from 'feather-icons/dist/icons/activity.svg?raw'; import bookmark from 'feather-icons/dist/icons/bookmark.svg?raw'; import clock from 'feather-icons/dist/icons/clock.svg?raw'; import edit from 'feather-icons/dist/icons/edit.svg?raw'; +import facebook from 'feather-icons/dist/icons/facebook.svg?raw'; import globe from 'feather-icons/dist/icons/globe.svg?raw'; import home from 'feather-icons/dist/icons/home.svg?raw'; +import instagram from 'feather-icons/dist/icons/instagram.svg?raw'; import logIn from 'feather-icons/dist/icons/log-in.svg?raw'; import logOut from 'feather-icons/dist/icons/log-out.svg?raw'; import map from 'feather-icons/dist/icons/map.svg?raw'; @@ -31,8 +33,10 @@ const ICONS = { bookmark, clock, edit, + facebook, globe, home, + instagram, 'log-in': logIn, 'log-out': logOut, map, diff --git a/app/components/place-details.gjs b/app/components/place-details.gjs index 2e92ae0..9685066 100644 --- a/app/components/place-details.gjs +++ b/app/components/place-details.gjs @@ -4,6 +4,7 @@ import { on } from '@ember/modifier'; import { htmlSafe } from '@ember/template'; import { humanizeOsmTag } from '../utils/format-text'; import { getLocalizedName, getPlaceType } from '../utils/osm'; +import { getSocialInfo } from '../utils/social-links'; import Icon from '../components/icon'; import PlaceEditForm from './place-edit-form'; @@ -159,6 +160,14 @@ export default class PlaceDetails extends Component { .join(', '); } + get facebook() { + return getSocialInfo(this.tags, 'facebook'); + } + + get instagram() { + return getSocialInfo(this.tags, 'instagram'); + } + get wikipedia() { const val = this.tags.wikipedia; if (!val) return null; @@ -292,6 +301,36 @@ export default class PlaceDetails extends Component {

{{/if}} + {{#if this.facebook}} +

+ + + + {{this.facebook.username}} + + +

+ {{/if}} + + {{#if this.instagram}} +

+ + + + {{this.instagram.username}} + + +

+ {{/if}} + {{#if this.wikipedia}}

diff --git a/app/utils/social-links.js b/app/utils/social-links.js new file mode 100644 index 0000000..6137c8d --- /dev/null +++ b/app/utils/social-links.js @@ -0,0 +1,52 @@ +// Helper to get value from multiple keys +const get = (tags, ...keys) => { + for (const k of keys) { + if (tags[k]) return tags[k]; + } + return null; +}; + +export function getSocialInfo(tags, platform) { + if (!tags) return null; + + const key = platform; + const domain = `${platform}.com`; + const val = get(tags, `contact:${key}`, key); + + if (!val) return null; + + // Check if it's a full URL + if (val.startsWith('http')) { + try { + const url = new URL(val); + + // Handle Facebook profile.php?id=... + if ( + platform === 'facebook' && + url.pathname === '/profile.php' && + url.searchParams.has('id') + ) { + return { + url: val, + username: url.searchParams.get('id'), + }; + } + + // Clean up pathname to get username + let username = url.pathname.replace(/^\/|\/$/g, ''); + return { + url: val, + username: username || val, // Fallback to full URL if path is empty + }; + } catch { + return { url: val, username: val }; + } + } + + // Assume it's a username + const username = val.replace(/^@/, ''); // Remove leading @ + return { + url: `https://${domain}/${username}`, + username: username, + }; +} diff --git a/tests/unit/utils/social-links-test.js b/tests/unit/utils/social-links-test.js new file mode 100644 index 0000000..22ffa1a --- /dev/null +++ b/tests/unit/utils/social-links-test.js @@ -0,0 +1,66 @@ +import { getSocialInfo } from 'marco/utils/social-links'; +import { module, test } from 'qunit'; + +module('Unit | Utility | social-links', function () { + test('it returns null if tags are missing', function (assert) { + let result = getSocialInfo({}, 'facebook'); + assert.strictEqual(result, null); + }); + + test('it returns null if specific platform tags are missing', function (assert) { + let result = getSocialInfo({ twitter: 'foo' }, 'facebook'); + assert.strictEqual(result, null); + }); + + test('it handles simple usernames', function (assert) { + let result = getSocialInfo({ facebook: 'foo' }, 'facebook'); + assert.deepEqual(result, { + url: 'https://facebook.com/foo', + username: 'foo', + }); + + result = getSocialInfo({ 'contact:instagram': '@bar' }, 'instagram'); + assert.deepEqual(result, { + url: 'https://instagram.com/bar', + username: 'bar', + }); + }); + + test('it handles full URLs', function (assert) { + let result = getSocialInfo( + { facebook: 'https://www.facebook.com/foo' }, + 'facebook' + ); + assert.deepEqual(result, { + url: 'https://www.facebook.com/foo', + username: 'foo', + }); + }); + + test('it handles Facebook profile.php URLs', function (assert) { + let result = getSocialInfo( + { facebook: 'https://www.facebook.com/profile.php?id=12345' }, + 'facebook' + ); + assert.deepEqual(result, { + url: 'https://www.facebook.com/profile.php?id=12345', + username: '12345', + }); + }); + + test('it falls back gracefully for malformed URLs', function (assert) { + let result = getSocialInfo({ facebook: 'http://' }, 'facebook'); + assert.deepEqual(result, { + url: 'http://', + username: 'http://', + }); + }); + + test('it prioritizes contact:tag over tag', function (assert) { + let result = getSocialInfo( + { 'contact:facebook': 'priority', facebook: 'fallback' }, + 'facebook' + ); + assert.strictEqual(result.username, 'priority'); + }); +});