160 lines
5.8 KiB
Dart
160 lines
5.8 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 'block_service.dart';
|
|
import 'card_service.dart';
|
|
import 'career_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 'relate_service.dart';
|
|
import 'settings_service.dart';
|
|
import 'verify_service.dart';
|
|
import 'visibility_service.dart';
|
|
import 'work_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);
|
|
relate = RelateService(groups, kin, network, bus);
|
|
work = WorkService(storage, clock, share, chats, groups, bus, deps.instant);
|
|
career = CareerService(storage, clock, groups, relate, network, bus);
|
|
blocks = BlockService(storage, clock, friends, bus);
|
|
}
|
|
|
|
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;
|
|
late final RelateService relate;
|
|
late final WorkService work;
|
|
late final CareerService career;
|
|
late final BlockService blocks;
|
|
|
|
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 = blocks.isBlocked(e.phone ?? '').then((no) async {
|
|
if (!no) await friends.onHello(e);
|
|
});
|
|
_tasks.add(f);
|
|
unawaited(f.whenComplete(() => _tasks.remove(f)));
|
|
} else {
|
|
// 받은 대화에 업무 표시·긴급 호출이 있으면 업무 쪽이 처리한다.
|
|
final id = e.refId;
|
|
// 받은 회사 명함의 회사가 바뀌었으면 이직 알림을 만든다.
|
|
if ((e.type == SyncEventType.message || e.type == SyncEventType.card) && id != null) {
|
|
final f = (e.type == SyncEventType.message ? work.onMessage(id) : career.onCard(id)).then((_) {}).catchError((Object _) {});
|
|
_tasks.add(f);
|
|
unawaited(f.whenComplete(() => _tasks.remove(f)));
|
|
}
|
|
bus.notify();
|
|
}
|
|
});
|
|
await career.seed();
|
|
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();
|
|
}
|
|
}
|