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? _events; final Set> _tasks = {}; /// 화면이 듣는 변경 알림. Stream get changes => bus.changes; /// 연결을 열고 동기화를 시작한다. 내 정보를 정한 뒤에 부른다. Future 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 idle() async { do { await sync.idle(); if (_tasks.isNotEmpty) await Future.wait(_tasks.toList()); } while (_tasks.isNotEmpty); } /// 보관함에서 새 봉투를 가져오고 못 보낸 것을 다시 보낸다 (화면 당겨서 새로고침, 주기적 호출). Future refresh() async { await sync.refresh(); bus.notify(); } Future dispose() async { await _events?.cancel(); await deps.lan?.stop(); await sync.stop(); await deps.storage.close(); await bus.close(); } }