Skip to content

Commit 4dcb220

Browse files
committed
refactor: reworked settings, added settings service
1 parent c1af837 commit 4dcb220

File tree

12 files changed

+92
-178
lines changed

12 files changed

+92
-178
lines changed

app/lib/src/logic/composition_root.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ Future<DependenciesContainer> createDependenciesContainer(
5858

5959
// Get package info.
6060
final packageInfo = await PackageInfo.fromPlatform();
61-
62-
final settingsContainer = await SettingsContainer.create(sharedPreferences);
61+
final settingsContainer = await SettingsContainer.create(sharedPreferences: sharedPreferences);
6362

6463
return DependenciesContainer(
6564
logger: logger,

core/common/lib/common.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export 'src/extensions/inherited_extension.dart';
22
export 'src/extensions/string_extension.dart';
3-
export 'src/mutex/mutext.dart';
3+
export 'src/mutex/mutex_lock.dart';
44
export 'src/persisted_column/persisted_column.dart';
55
export 'src/persisted_column/shared_preferences_column.dart';
66
export 'src/utils/jsonmap_codec.dart';
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import 'dart:collection';
44
/// A simple mutex implementation using a queue of completers.
55
/// This allows for synchronizing access to a critical section of code,
66
/// ensuring that only one task can execute the critical section at a time.
7-
class Mutext {
8-
Mutext();
7+
class MutexLock {
8+
MutexLock();
99

1010
final _queue = DoubleLinkedQueue<Completer<void>>();
1111

feature/home/lib/src/presentation/widget/home_screen.dart

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,9 @@ class _HomeScreenState extends State<HomeScreen> {
1818
}
1919

2020
void _onSeedColorChanged(Color color) {
21-
SettingsScope.of(context).settingsBloc.add(
22-
SettingsEventUpdate(
23-
onUpdate: (settings) => settings.copyWith(
24-
general: settings.general.copyWith(seedColor: color),
25-
),
21+
SettingsScope.of(context).settingsService.update(
22+
(settings) => settings.copyWith(
23+
general: settings.general.copyWith(seedColor: color),
2624
),
2725
);
2826
}

feature/settings/lib/settings.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
export 'src/application/settings_service.dart';
12
export 'src/domain/model/general.dart';
23
export 'src/domain/model/settings.dart';
34
export 'src/domain/repositories/settings_repository.dart';
45
export 'src/injection.dart';
5-
export 'src/presentation/bloc/settings_bloc.dart';
66
export 'src/presentation/settings_builder.dart';
77
export 'src/presentation/settings_scope.dart';
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import 'dart:async';
2+
3+
import 'package:common/common.dart';
4+
import 'package:settings/settings.dart';
5+
6+
abstract interface class SettingsService {
7+
/// Emits settings whenever they change.
8+
Stream<Settings> get stream;
9+
10+
/// Current in-memory settings.
11+
Settings get current;
12+
13+
/// Update settings transactionally and persist them.
14+
Future<void> update(Settings Function(Settings current) transform);
15+
}
16+
17+
final class SettingsServiceImpl implements SettingsService {
18+
SettingsServiceImpl._(this._repository, this._current);
19+
20+
/// Create a new [SettingsServiceImpl] with the given [repository].
21+
static Future<SettingsServiceImpl> create({required SettingsRepository repository}) async {
22+
final current = await repository.read();
23+
return SettingsServiceImpl._(repository, current);
24+
}
25+
26+
final SettingsRepository _repository;
27+
final _controller = StreamController<Settings>.broadcast();
28+
final _mutex = MutexLock();
29+
Settings _current;
30+
31+
@override
32+
Stream<Settings> get stream => _controller.stream;
33+
34+
@override
35+
Settings get current => _current;
36+
37+
@override
38+
Future<void> update(Settings Function(Settings) transform) => _mutex.runLocked(() async {
39+
final updated = transform(current);
40+
await _repository.save(updated);
41+
_current = updated;
42+
_controller.add(updated);
43+
});
44+
}

feature/settings/lib/src/data/mappers/general_settings_codec.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ class GeneralSettingsCodec extends JsonMapCodec<GeneralSettings> {
1616
final themeMode = input['themeMode'] as String?;
1717
final seedColor = input['seedColor'] as Map<String, Object?>?;
1818

19-
const defaults = GeneralSettings();
20-
2119
Color? seedColorValue;
2220

2321
if (seedColor case {
@@ -29,6 +27,8 @@ class GeneralSettingsCodec extends JsonMapCodec<GeneralSettings> {
2927
seedColorValue = Color.from(alpha: a, red: r, green: g, blue: b);
3028
}
3129

30+
const defaults = GeneralSettings();
31+
3232
return GeneralSettings(
3333
themeMode: themeMode != null ? ThemeModeVO.values.byName(themeMode) : defaults.themeMode,
3434
seedColor: seedColorValue ?? defaults.seedColor,
Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,20 @@
11
import 'dart:async';
22

3-
import 'package:common/common.dart';
43
import 'package:settings/settings.dart';
54
import 'package:settings/src/data/datasources/settings_local_datasource.dart';
65

76
final class SettingsRepositoryImpl implements SettingsRepository {
8-
SettingsRepositoryImpl({
9-
required this.localDatasource,
10-
required this.current,
11-
});
12-
13-
final _controller = StreamController<Settings>.broadcast();
14-
15-
static Future<SettingsRepositoryImpl> init(SettingsLocalDatasource localDatasource) async {
16-
final currentSettings = await localDatasource.read();
17-
18-
return SettingsRepositoryImpl(
19-
localDatasource: localDatasource,
20-
current: currentSettings,
21-
);
22-
}
7+
const SettingsRepositoryImpl({required this.localDatasource});
238

249
final SettingsLocalDatasource localDatasource;
25-
final _mutex = Mutext();
26-
27-
@override
28-
Future<Settings> save(Settings Function(Settings settings) fn) => _mutex.runLocked(() async {
29-
final newSettings = fn(current);
30-
31-
await localDatasource.save(newSettings);
32-
current = newSettings;
33-
_controller.add(current);
34-
35-
return current;
36-
});
3710

3811
@override
39-
Stream<Settings> watch() => _controller.stream;
12+
Future<void> save(Settings settings) async {
13+
await localDatasource.save(settings);
14+
}
4015

4116
@override
42-
Settings current;
17+
Future<Settings> read() async {
18+
return await localDatasource.read();
19+
}
4320
}
Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import 'package:settings/settings.dart';
22

33
abstract interface class SettingsRepository {
4-
Settings get current;
5-
6-
Stream<Settings> watch();
7-
8-
Future<Settings> save(Settings Function(Settings settings) fn);
4+
Future<void> save(Settings settings);
5+
Future<Settings> read();
96
}
Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,27 @@
1-
import 'package:settings/settings.dart';
1+
import 'package:settings/src/application/settings_service.dart';
22
import 'package:settings/src/data/datasources/settings_local_datasource.dart';
33
import 'package:settings/src/data/repositories/settings_repository_impl.dart';
44
import 'package:shared_preferences/shared_preferences.dart';
55

6-
Future<SettingsRepository> _createSettingsRepository(SharedPreferencesAsync sharedPreferences) {
7-
return SettingsRepositoryImpl.init(
8-
SettingsLocalDatasourceSharedPreferences(sharedPreferences: sharedPreferences),
9-
);
10-
}
11-
126
/// Container with global settings state.
137
class SettingsContainer {
14-
const SettingsContainer({
15-
required this.settingsRepository,
16-
required this.settingsBloc,
17-
});
18-
19-
final SettingsRepository settingsRepository;
20-
final SettingsBloc settingsBloc;
8+
const SettingsContainer._(this.settingsService);
219

22-
static Future<SettingsContainer> create(SharedPreferencesAsync sharedPreferences) async {
23-
final settingsRepository = await _createSettingsRepository(sharedPreferences);
10+
/// Service for managing settings.
11+
final SettingsService settingsService;
2412

25-
return SettingsContainer(
26-
settingsRepository: settingsRepository,
27-
settingsBloc: SettingsBloc(settingsRepository: settingsRepository),
13+
/// Create a new [SettingsContainer] with the given [sharedPreferences].
14+
static Future<SettingsContainer> create({
15+
required SharedPreferencesAsync sharedPreferences,
16+
}) async {
17+
final settingsRepository = SettingsRepositoryImpl(
18+
localDatasource: SettingsLocalDatasourceSharedPreferences(
19+
sharedPreferences: sharedPreferences,
20+
),
2821
);
22+
23+
final settingsService = await SettingsServiceImpl.create(repository: settingsRepository);
24+
25+
return SettingsContainer._(settingsService);
2926
}
3027
}

0 commit comments

Comments
 (0)