frindlymap/lib/modules/app/src/friendly_app.dart

136 lines
4.6 KiB
Dart

import 'dart:async';
import 'package:friendly_map/modules/sharing/sharing.dart';
import 'package:friendly_map/modules/sync/sync.dart';
import 'app_deps.dart';
import 'card_service.dart';
import 'change_bus.dart';
import 'call_service.dart';
import 'chat_service.dart';
import 'contact_import_service.dart';
import 'event_service.dart';
import 'facts_service.dart';
import 'friend_service.dart';
import 'group_service.dart';
import 'kin_service.dart';
import 'layout_service.dart';
import 'location_service.dart';
import 'network_service.dart';
import 'profile_service.dart';
import 'recovery_service.dart';
import 'settings_service.dart';
import 'verify_service.dart';
import 'visibility_service.dart';
/// 앱 내부의 입구. 화면은 이 객체의 서비스들만 쓴다 (저장소·전송·암호를 직접 만지지 않는다).
///
/// 서비스는 서로를 모르고 이 객체가 재료를 나눠 준다. 서비스 하나를 빼거나 바꿔도 다른 서비스는 그대로다.
class FriendlyApp {
FriendlyApp(AppDeps deps)
: deps = deps,
bus = ChangeBus() {
final storage = deps.storage;
final clock = deps.clock;
sync = SyncEngine(
storage: storage,
transport: deps.transport,
cipher: deps.cipher,
identity: deps.identity,
clock: clock,
timeSource: deps.timeSource,
);
visibility = VisibilityService(storage, clock, bus);
final share = ShareService(storage, clock, filter: visibility.contentFor);
layout = LayoutService(storage, clock, bus);
profile = ProfileService(storage, clock, layout, bus);
cards = CardService(storage, clock, share, sync, layout, bus);
friends = FriendService(storage, clock, sync, share, deps.identity, bus, deps.lan);
chats = ChatService(storage, clock, share, sync, bus);
network = NetworkService(storage, clock, bus);
settings = SettingsService(storage, clock, deps.probe, bus);
recovery = RecoveryService(storage, clock, sync, bus);
verify = VerifyService(storage, clock, bus, deps.verifier);
location = LocationService(storage, clock, sync, visibility, bus, deps.location);
facts = FactsService(storage, clock, bus);
kin = KinService(storage, clock, facts, bus);
events = EventService(storage, clock, facts, bus, deps.notifier);
calls = CallService(storage, deps.callLog, deps.phone);
groups = GroupService(storage, clock, bus);
contacts = ContactImportService(storage, clock, friends, groups, bus, deps.contacts);
}
final AppDeps deps;
final ChangeBus bus;
late final SyncEngine sync;
late final LayoutService layout;
late final ProfileService profile;
late final CardService cards;
late final FriendService friends;
late final ChatService chats;
late final NetworkService network;
late final SettingsService settings;
late final VisibilityService visibility;
late final RecoveryService recovery;
late final LocationService location;
late final VerifyService verify;
late final FactsService facts;
late final KinService kin;
late final EventService events;
late final CallService calls;
late final GroupService groups;
late final ContactImportService contacts;
StreamSubscription<SyncEvent>? _events;
final Set<Future<void>> _tasks = {};
/// 화면이 듣는 변경 알림.
Stream<void> get changes => bus.changes;
/// 연결을 열고 동기화를 시작한다. 내 정보를 정한 뒤에 부른다.
Future<void> start() async {
_events ??= sync.events.listen((e) {
if (e.type == SyncEventType.hello) {
final f = friends.onHello(e);
_tasks.add(f);
unawaited(f.whenComplete(() => _tasks.remove(f)));
} else {
bus.notify();
}
});
await sync.start();
unawaited(events.reschedule());
final me = await profile.me();
final lan = deps.lan;
if (lan != null && me != null) {
await lan.start(myPhone: me.phone, myPort: lanPort);
}
bus.notify();
}
/// 직접 연결이 열어 둔 포트 (조립하는 쪽이 정해 준다).
int lanPort = 0;
/// 받은 봉투와 그에 따른 처리가 모두 끝날 때까지 기다린다 (시험·종료 직전에 쓴다).
Future<void> idle() async {
do {
await sync.idle();
if (_tasks.isNotEmpty) await Future.wait(_tasks.toList());
} while (_tasks.isNotEmpty);
}
/// 보관함에서 새 봉투를 가져오고 못 보낸 것을 다시 보낸다 (화면 당겨서 새로고침, 주기적 호출).
Future<void> refresh() async {
await sync.refresh();
bus.notify();
}
Future<void> dispose() async {
await _events?.cancel();
await deps.lan?.stop();
await sync.stop();
await deps.storage.close();
await bus.close();
}
}