Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion mobile/lib/core/network/dio_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,14 @@ Dio dio(Ref ref) {

dio.options.connectTimeout = const Duration(seconds: 10);
dio.options.receiveTimeout = const Duration(seconds: 10);
dio.options.headers = {
final customHeaders = ref.watch(customHeadersProvider).value ?? [];
final Map<String, String> headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
for (final h in customHeaders)
if (h.key.trim().isNotEmpty) h.key.trim(): h.value,
};
dio.options.headers = headers;

// Allow self-signed certificates when the user has enabled the setting
final allowSelfSigned =
Expand Down Expand Up @@ -95,6 +99,8 @@ Dio dio(Ref ref) {
refreshDio.options.headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
for (final h in customHeaders)
if (h.key.trim().isNotEmpty) h.key.trim(): h.value,
};
if (allowSelfSigned && serverUrl != null && serverUrl.isNotEmpty) {
refreshDio.httpClientAdapter =
Expand Down
2 changes: 1 addition & 1 deletion mobile/lib/core/network/dio_provider.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 68 additions & 1 deletion mobile/lib/core/network/server_config_provider.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'dart:convert';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
Expand All @@ -6,7 +7,8 @@ part 'server_config_provider.g.dart';

const _serverUrlKey = 'server_url';
const _allowSelfSignedCertKey = 'allow_self_signed_cert';

const _customHeadersKey = 'custom_headers';
const int maxCustomHeaders = 10;
@riverpod
class ServerConfig extends _$ServerConfig {
final _storage = const FlutterSecureStorage();
Expand Down Expand Up @@ -73,3 +75,68 @@ final allowSelfSignedCertProvider =
AsyncNotifierProvider<AllowSelfSignedCertNotifier, bool>(
AllowSelfSignedCertNotifier.new,
);
class CustomHeader {
final String key;
final String value;
const CustomHeader({required this.key, required this.value});
Map<String, String> toJson() => {'key': key, 'value': value};
factory CustomHeader.fromJson(Map<String, dynamic> json) =>
CustomHeader(key: json['key'] as String, value: json['value'] as String);
CustomHeader copyWith({String? key, String? value}) =>
CustomHeader(key: key ?? this.key, value: value ?? this.value);
}
class CustomHeadersNotifier extends AsyncNotifier<List<CustomHeader>> {
final _storage = const FlutterSecureStorage();
@override
Future<List<CustomHeader>> build() async {
try {
final raw = await _storage.read(key: _customHeadersKey);
if (raw == null) return [];
final list = jsonDecode(raw) as List<dynamic>;
return list
.cast<Map<String, dynamic>>()
.map(CustomHeader.fromJson)
.toList();
} catch (_) {
return [];
}
}
Future<void> _persist(List<CustomHeader> headers) async {
try {
final encoded = jsonEncode(headers.map((h) => h.toJson()).toList());
await _storage.write(key: _customHeadersKey, value: encoded);
} catch (_) {
// Ignore storage errors
}
}
Future<void> addHeader(CustomHeader header) async {
final current = state.value ?? [];
if (current.length >= maxCustomHeaders) return;
final updated = [...current, header];
state = AsyncData(updated);
await _persist(updated);
}
Future<void> updateHeader(int index, CustomHeader header) async {
final current = List<CustomHeader>.from(state.value ?? []);
if (index < 0 || index >= current.length) return;
current[index] = header;
state = AsyncData(current);
await _persist(current);
}
Future<void> removeHeader(int index) async {
final current = List<CustomHeader>.from(state.value ?? []);
if (index < 0 || index >= current.length) return;
current.removeAt(index);
state = AsyncData(current);
await _persist(current);
}
Future<void> setHeaders(List<CustomHeader> headers) async {
final limited = headers.take(maxCustomHeaders).toList();
state = AsyncData(limited);
await _persist(limited);
}
}
final customHeadersProvider =
AsyncNotifierProvider<CustomHeadersNotifier, List<CustomHeader>>(
CustomHeadersNotifier.new,
);
Loading