pretty broken, icons missing, login not working. browser opens though and cams still appear on map

This commit is contained in:
stopflock
2025-07-19 14:11:02 -05:00
parent d3d1e4a7b2
commit 56518bab28
11 changed files with 689 additions and 154 deletions
+76
View File
@@ -0,0 +1,76 @@
import 'dart:convert';
import 'package:oauth2_client/oauth2_client.dart';
import 'package:oauth2_client/oauth2_helper.dart';
import 'package:http/http.dart' as http;
/// Handles OAuth2 PKCE login to OpenStreetMap and exposes
/// the stored access token & display name.
///
/// ─ Requirements ─
/// • Register an OAuth app at
/// https://www.openstreetmap.org/oauth2/applications
/// Redirect URI: flockmap://auth
/// • Put that client ID below (replace 'flockmap').
class AuthService {
static const _clientId = 'flockmap'; // ← replace with your ID
static const _redirect = 'flockmap://auth';
late final OAuth2Helper _helper;
String? _displayName; // cached after login
String? get displayName => _displayName;
AuthService() {
final client = OAuth2Client(
authorizeUrl: 'https://www.openstreetmap.org/oauth2/authorize',
tokenUrl: 'https://www.openstreetmap.org/oauth2/token',
redirectUri: _redirect,
customUriScheme: 'flockmap', // matches redirect scheme
);
_helper = OAuth2Helper(
client,
clientId: _clientId,
scopes: ['write_api'],
enablePKCE: true, // PKCE flow
// No custom token store needed: oauth2_client will
// autouse flutter_secure_storage when present.
);
}
/* ───────── Public helpers ───────── */
/// Returns `true` if a nonexpired token is stored.
Future<bool> isLoggedIn() async =>
(await _helper.getTokenFromStorage())?.isExpired() == false;
/// Launches browser login if necessary; caches display name.
Future<String?> login() async {
final token = await _helper.getToken();
if (token?.accessToken == null) return null;
_displayName = await _fetchUsername(token!.accessToken!);
return _displayName;
}
Future<void> logout() async {
await _helper.removeAllTokens();
_displayName = null;
}
/// Safely fetch current access token (or null).
Future<String?> getAccessToken() async =>
(await _helper.getTokenFromStorage())?.accessToken;
/* ───────── Internal ───────── */
Future<String?> _fetchUsername(String accessToken) async {
final resp = await http.get(
Uri.parse('https://api.openstreetmap.org/api/0.6/user/details.json'),
headers: {'Authorization': 'Bearer $accessToken'},
);
if (resp.statusCode != 200) return null;
return jsonDecode(resp.body)['user']?['display_name'];
}
}
+66
View File
@@ -0,0 +1,66 @@
import 'dart:async';
import 'package:http/http.dart' as http;
import '../models/pending_upload.dart';
class Uploader {
Uploader(this.accessToken, this.onSuccess);
final String accessToken;
final void Function() onSuccess;
Future<bool> upload(PendingUpload p) async {
try {
// 1. open changeset
final csXml = '''
<osm>
<changeset>
<tag k="created_by" v="FlockMap 0.5"/>
<tag k="comment" v="Add surveillance camera"/>
</changeset>
</osm>''';
final csResp = await _post('/api/0.6/changeset/create', csXml);
if (csResp.statusCode != 200) return false;
final csId = csResp.body;
// 2. create node
final nodeXml = '''
<osm>
<node changeset="$csId" lat="${p.coord.latitude}" lon="${p.coord.longitude}">
<tag k="man_made" v="surveillance"/>
<tag k="surveillance:type" v="ALPR"/>
<tag k="camera:type" v="fixed"/>
<tag k="direction" v="${p.direction.round()}"/>
</node>
</osm>''';
final nodeResp = await _put('/api/0.6/node/create', nodeXml);
if (nodeResp.statusCode != 200) return false;
// 3. close changeset
await _put('/api/0.6/changeset/$csId/close', '');
onSuccess();
return true;
} catch (_) {
return false;
}
}
Future<http.Response> _post(String path, String body) => http.post(
Uri.https('api.openstreetmap.org', path),
headers: _headers,
body: body,
);
Future<http.Response> _put(String path, String body) => http.put(
Uri.https('api.openstreetmap.org', path),
headers: _headers,
body: body,
);
Map<String, String> get _headers => {
'Authorization': 'Bearer $accessToken',
'Content-Type': 'text/xml',
};
}