Connect OSM account
This commit is contained in:
@@ -1,9 +1,13 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { action } from '@ember/object';
|
||||
import { service } from '@ember/service';
|
||||
import Icon from '#components/icon';
|
||||
import { on } from '@ember/modifier';
|
||||
|
||||
export default class UserMenuComponent extends Component {
|
||||
@service storage;
|
||||
@service osmAuth;
|
||||
|
||||
@action
|
||||
connectRS() {
|
||||
this.args.onClose();
|
||||
@@ -15,6 +19,17 @@ export default class UserMenuComponent extends Component {
|
||||
this.args.storage.disconnect();
|
||||
}
|
||||
|
||||
@action
|
||||
connectOsm() {
|
||||
this.args.onClose();
|
||||
this.osmAuth.login();
|
||||
}
|
||||
|
||||
@action
|
||||
disconnectOsm() {
|
||||
this.osmAuth.logout();
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="user-menu-popover">
|
||||
<ul class="account-list">
|
||||
@@ -40,22 +55,39 @@ export default class UserMenuComponent extends Component {
|
||||
</div>
|
||||
<div class="account-status">
|
||||
{{#if @storage.connected}}
|
||||
<strong>{{@storage.userAddress}}</strong>
|
||||
{{@storage.userAddress}}
|
||||
{{else}}
|
||||
Not connected
|
||||
{{/if}}
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="account-item disabled">
|
||||
<li class="account-item">
|
||||
<div class="account-header">
|
||||
<div class="account-info">
|
||||
<Icon @name="map" @size={{18}} />
|
||||
<span>OpenStreetMap</span>
|
||||
</div>
|
||||
{{#if this.osmAuth.isConnected}}
|
||||
<button
|
||||
class="btn-text text-danger"
|
||||
type="button"
|
||||
{{on "click" this.disconnectOsm}}
|
||||
>Disconnect</button>
|
||||
{{else}}
|
||||
<button
|
||||
class="btn-text text-primary"
|
||||
type="button"
|
||||
{{on "click" this.connectOsm}}
|
||||
>Connect</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="account-status">
|
||||
Coming soon
|
||||
{{#if this.osmAuth.isConnected}}
|
||||
{{this.osmAuth.userDisplayName}}
|
||||
{{else}}
|
||||
Not connected
|
||||
{{/if}}
|
||||
</div>
|
||||
</li>
|
||||
|
||||
|
||||
@@ -10,4 +10,7 @@ Router.map(function () {
|
||||
this.route('place', { path: '/place/:place_id' });
|
||||
this.route('place.new', { path: '/place/new' });
|
||||
this.route('search');
|
||||
this.route('oauth', function () {
|
||||
this.route('osm-callback', { path: '/osm/callback' });
|
||||
});
|
||||
});
|
||||
|
||||
17
app/routes/oauth/osm-callback.js
Normal file
17
app/routes/oauth/osm-callback.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { service } from '@ember/service';
|
||||
|
||||
export default class OauthOsmCallbackRoute extends Route {
|
||||
@service osmAuth;
|
||||
@service router;
|
||||
|
||||
async model() {
|
||||
try {
|
||||
await this.osmAuth.handleCallback();
|
||||
} catch (e) {
|
||||
console.error('Failed to handle OSM OAuth callback', e);
|
||||
} finally {
|
||||
this.router.transitionTo('index');
|
||||
}
|
||||
}
|
||||
}
|
||||
103
app/services/osm-auth.js
Normal file
103
app/services/osm-auth.js
Normal file
@@ -0,0 +1,103 @@
|
||||
import Service from '@ember/service';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { OAuth2AuthCodePkceClient } from 'oauth2-pkce';
|
||||
|
||||
class MarcoOsmAuthStorage {
|
||||
saveState(serializedState) {
|
||||
localStorage.setItem('marco:osm_auth_state', serializedState);
|
||||
}
|
||||
loadState() {
|
||||
return localStorage.getItem('marco:osm_auth_state');
|
||||
}
|
||||
}
|
||||
|
||||
export default class OsmAuthService extends Service {
|
||||
@tracked isConnected = false;
|
||||
@tracked userDisplayName = null;
|
||||
|
||||
oauthClient;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
const clientId =
|
||||
import.meta.env.VITE_OSM_CLIENT_ID || 'YOUR_CLIENT_ID_HERE';
|
||||
const oauthUrl =
|
||||
import.meta.env.VITE_OSM_OAUTH_URL || 'https://www.openstreetmap.org';
|
||||
|
||||
const redirectUrl =
|
||||
import.meta.env.VITE_OSM_REDIRECT_URI ||
|
||||
`${window.location.origin}/oauth/osm/callback`;
|
||||
|
||||
this.oauthClient = new OAuth2AuthCodePkceClient(
|
||||
{
|
||||
scopes: ['read_prefs', 'write_api'],
|
||||
authorizationUrl: `${oauthUrl}/oauth2/authorize`,
|
||||
tokenUrl: `${oauthUrl}/oauth2/token`,
|
||||
clientId: clientId,
|
||||
redirectUrl: redirectUrl,
|
||||
storeRefreshToken: true,
|
||||
},
|
||||
new MarcoOsmAuthStorage()
|
||||
);
|
||||
|
||||
this.restoreSession();
|
||||
}
|
||||
|
||||
async restoreSession() {
|
||||
const isAuthorized = await this.oauthClient.isAuthorized();
|
||||
if (isAuthorized) {
|
||||
this.isConnected = true;
|
||||
const storedName = localStorage.getItem('marco:osm_user_display_name');
|
||||
if (storedName) {
|
||||
this.userDisplayName = storedName;
|
||||
} else {
|
||||
await this.fetchUserInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async login() {
|
||||
await this.oauthClient.requestAuthorizationCode();
|
||||
}
|
||||
|
||||
async handleCallback() {
|
||||
await this.oauthClient.receiveCode();
|
||||
await this.oauthClient.getTokens();
|
||||
this.isConnected = true;
|
||||
await this.fetchUserInfo();
|
||||
}
|
||||
|
||||
async logout() {
|
||||
await this.oauthClient.reset();
|
||||
this.isConnected = false;
|
||||
this.userDisplayName = null;
|
||||
localStorage.removeItem('marco:osm_user_display_name');
|
||||
}
|
||||
|
||||
async fetchUserInfo() {
|
||||
try {
|
||||
const tokens = await this.oauthClient.getTokens();
|
||||
// eslint-disable-next-line warp-drive/no-external-request-patterns
|
||||
const response = await fetch(
|
||||
'https://api.openstreetmap.org/api/0.6/user/details.json',
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens.accessToken}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.debug('OSM data:', data);
|
||||
const displayName = data.user.display_name;
|
||||
this.userDisplayName = displayName;
|
||||
localStorage.setItem('marco:osm_user_display_name', displayName);
|
||||
} else {
|
||||
console.error('Failed to fetch OSM user info', response.status);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error fetching OSM user info', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,6 +246,7 @@ body {
|
||||
|
||||
.account-status {
|
||||
font-size: 0.85rem;
|
||||
font-weight: bold;
|
||||
color: #898989;
|
||||
margin-top: 0.35rem;
|
||||
margin-left: calc(18px + 0.75rem);
|
||||
|
||||
Reference in New Issue
Block a user