142 lines
4.4 KiB
Dart
142 lines
4.4 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:http/http.dart' as http;
|
|
|
|
import '../../config/api_config.dart';
|
|
import '../../services/auth_service.dart';
|
|
|
|
class SpellingWordsApiException implements Exception {
|
|
SpellingWordsApiException(this.statusCode, this.body);
|
|
|
|
final int statusCode;
|
|
final String body;
|
|
|
|
@override
|
|
String toString() => 'SpellingWordsApiException($statusCode): $body';
|
|
}
|
|
|
|
/// HTTP client for the shared spelling word catalog.
|
|
class SpellingWordsRemoteStore {
|
|
SpellingWordsRemoteStore({http.Client? client})
|
|
: _client = client ?? http.Client();
|
|
|
|
final http.Client _client;
|
|
|
|
static const String _basePath = '/v1/me/spelling-words';
|
|
|
|
Future<List<String>> fetchWords() async {
|
|
final String? token = await AuthService.instance.getIdToken();
|
|
if (token == null) {
|
|
throw StateError('Not signed in');
|
|
}
|
|
|
|
final http.Response response = await _client.get(
|
|
Uri.parse('$apiBaseUrl$_basePath'),
|
|
headers: _authHeaders(token),
|
|
);
|
|
if (response.statusCode != 200) {
|
|
throw SpellingWordsApiException(response.statusCode, response.body);
|
|
}
|
|
|
|
final Map<String, dynamic> body =
|
|
jsonDecode(response.body) as Map<String, dynamic>;
|
|
final List<dynamic> words = body['words'] as List<dynamic>? ?? <dynamic>[];
|
|
return words.map((dynamic w) => w as String).toList(growable: false);
|
|
}
|
|
|
|
Future<List<String>> addWord(String word) async {
|
|
final String? token = await AuthService.instance.getIdToken();
|
|
if (token == null) {
|
|
throw StateError('Not signed in');
|
|
}
|
|
|
|
final http.Response response = await _client.post(
|
|
Uri.parse('$apiBaseUrl$_basePath'),
|
|
headers: _authHeaders(token),
|
|
body: jsonEncode(<String, dynamic>{'word': word}),
|
|
);
|
|
if (response.statusCode != 201) {
|
|
throw SpellingWordsApiException(response.statusCode, response.body);
|
|
}
|
|
|
|
final Map<String, dynamic> body =
|
|
jsonDecode(response.body) as Map<String, dynamic>;
|
|
final List<dynamic> words = body['words'] as List<dynamic>? ?? <dynamic>[];
|
|
return words.map((dynamic w) => w as String).toList(growable: false);
|
|
}
|
|
|
|
Future<({int added, int skipped})> importText(String text) async {
|
|
final String? token = await AuthService.instance.getIdToken();
|
|
if (token == null) {
|
|
throw StateError('Not signed in');
|
|
}
|
|
|
|
final http.Response response = await _client.post(
|
|
Uri.parse('$apiBaseUrl$_basePath/import'),
|
|
headers: <String, String>{
|
|
..._authHeaders(token),
|
|
'Content-Type': 'text/plain; charset=utf-8',
|
|
},
|
|
body: text,
|
|
);
|
|
if (response.statusCode != 200) {
|
|
throw SpellingWordsApiException(response.statusCode, response.body);
|
|
}
|
|
|
|
final Map<String, dynamic> body =
|
|
jsonDecode(response.body) as Map<String, dynamic>;
|
|
return (
|
|
added: (body['added'] as num?)?.toInt() ?? 0,
|
|
skipped: (body['skipped'] as num?)?.toInt() ?? 0,
|
|
);
|
|
}
|
|
|
|
Future<String> exportText() async {
|
|
final String? token = await AuthService.instance.getIdToken();
|
|
if (token == null) {
|
|
throw StateError('Not signed in');
|
|
}
|
|
|
|
final http.Response response = await _client.get(
|
|
Uri.parse('$apiBaseUrl$_basePath/export'),
|
|
headers: <String, String>{
|
|
'Authorization': 'Bearer $token',
|
|
'Accept': 'text/plain',
|
|
},
|
|
);
|
|
if (response.statusCode != 200) {
|
|
throw SpellingWordsApiException(response.statusCode, response.body);
|
|
}
|
|
return response.body;
|
|
}
|
|
|
|
Future<List<String>> deleteWord(String word) async {
|
|
final String? token = await AuthService.instance.getIdToken();
|
|
if (token == null) {
|
|
throw StateError('Not signed in');
|
|
}
|
|
|
|
final String encodedWord = Uri.encodeComponent(word);
|
|
final http.Response response = await _client.delete(
|
|
Uri.parse('$apiBaseUrl$_basePath/$encodedWord'),
|
|
headers: _authHeaders(token),
|
|
);
|
|
if (response.statusCode != 200) {
|
|
throw SpellingWordsApiException(response.statusCode, response.body);
|
|
}
|
|
|
|
final Map<String, dynamic> body =
|
|
jsonDecode(response.body) as Map<String, dynamic>;
|
|
final List<dynamic> words = body['words'] as List<dynamic>? ?? <dynamic>[];
|
|
return words.map((dynamic w) => w as String).toList(growable: false);
|
|
}
|
|
|
|
Map<String, String> _authHeaders(String token) {
|
|
return <String, String>{
|
|
'Authorization': 'Bearer $token',
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
};
|
|
}
|
|
}
|