refreshBuildings method
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(),
),
);
});
});