refreshBuildings method

Future<void> refreshBuildings()

Fetches building and layer catalogs atomically without deleting room cache.

Layers removed upstream are deleted with their dependent rooms. A changed layer catalog invalidates search completeness until the next search. Network errors propagate and leave the previous catalog intact.

Implementation

Future<void> refreshBuildings() => _coalesce('catalog', () async {
  final outlines = await _service.getBuildings();
  final layers = await _service.getLayers();
  if (outlines.isEmpty || layers.isEmpty) {
    throw const FormatException('Empty campus map catalog');
  }
  final previous = await _db.select(_db.campusMapFloors).get();
  final before = previous.map((floor) => floor.layerName).toSet();
  final after = layers.map((layer) => layer.name).toSet();
  final catalogChanged =
      before.length != after.length || !before.containsAll(after);
  final buildings = <String, CampusMapBuildingsCompanion>{};
  for (final outline in outlines) {
    final code = outline.code.isEmpty ? outline.id : outline.code;
    buildings[code] = CampusMapBuildingsCompanion.insert(
      code: code,
      name: outline.name.isEmpty ? code : outline.name,
      geometry: encodeCampusMapGeometry(_polygons(outline.polygons)),
    );
  }
  // Some published layers belong to buildings absent from the outline layer.
  // They stay selectable and searchable instead of silently disappearing.
  for (final layer in layers) {
    buildings.putIfAbsent(
      layer.buildingCode,
      () => CampusMapBuildingsCompanion.insert(
        code: layer.buildingCode,
        name: layer.buildingCode,
        geometry: '[]',
      ),
    );
  }
  await _db.transaction(() async {
    await _db.batch((batch) {
      batch.insertAllOnConflictUpdate(
        _db.campusMapBuildings,
        buildings.values.toList(),
      );
      batch.insertAllOnConflictUpdate(_db.campusMapFloors, [
        for (final layer in layers)
          CampusMapFloorsCompanion.insert(
            layerName: layer.name,
            building: layer.buildingCode,
            code: layer.floor,
          ),
      ]);
    });
    await (_db.delete(
      _db.campusMapFloors,
    )..where((floor) => floor.layerName.isNotIn(after))).go();
    await (_db.delete(
      _db.campusMapBuildings,
    )..where((building) => building.code.isNotIn(buildings.keys))).go();
    await _db
        .into(_db.campusMapSyncs)
        .insertOnConflictUpdate(
          CampusMapSyncsCompanion.insert(
            namespace: _namespace,
            catalogFetchedAt: Value(_now()),
            indexFetchedAt: catalogChanged
                ? const Value(null)
                : const Value.absent(),
          ),
        );
  });
});