Skip to content
This repository has been archived by the owner on Jul 2, 2024. It is now read-only.

Commit

Permalink
Merge pull request #232 from blazern/news-feed-ui
Browse files Browse the repository at this point in the history
First implementation of News Feed UI
  • Loading branch information
blazern authored Apr 19, 2022
2 parents 42ec497 + 34ceeb4 commit e60db8b
Show file tree
Hide file tree
Showing 25 changed files with 1,032 additions and 92 deletions.
15 changes: 10 additions & 5 deletions lib/di.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'package:plante/model/shared_preferences_holder.dart';
import 'package:plante/model/user_params_controller.dart';
import 'package:plante/outside/backend/backend.dart';
import 'package:plante/outside/backend/mobile_app_config_manager.dart';
import 'package:plante/outside/backend/news/news_feed_manager.dart';
import 'package:plante/outside/backend/user_avatar_manager.dart';
import 'package:plante/outside/backend/user_params_auto_wiper.dart';
import 'package:plante/outside/backend/user_params_fetcher.dart';
Expand All @@ -27,8 +28,8 @@ import 'package:plante/outside/map/displayed_distance_units_manager.dart';
import 'package:plante/outside/map/extra_properties/map_extra_properties_cacher.dart';
import 'package:plante/outside/map/extra_properties/products_at_shops_extra_properties_manager.dart';
import 'package:plante/outside/map/osm/open_street_map.dart';
import 'package:plante/outside/map/osm/osm_cacher.dart';
import 'package:plante/outside/map/osm/osm_searcher.dart';
import 'package:plante/outside/map/osm/osm_territory_cacher.dart';
import 'package:plante/outside/map/roads_manager.dart';
import 'package:plante/outside/map/shops_manager.dart';
import 'package:plante/outside/map/user_address/caching_user_address_pieces_obtainer.dart';
Expand Down Expand Up @@ -161,13 +162,13 @@ void initDI() {
GetIt.I.registerSingleton<ViewedProductsStorage>(ViewedProductsStorage());
GetIt.I.registerSingleton<ContributedByUserProductsStorage>(
ContributedByUserProductsStorage());
GetIt.I.registerSingleton<OsmCacher>(OsmCacher());
GetIt.I.registerSingleton<OsmTerritoryCacher>(OsmTerritoryCacher());
GetIt.I.registerSingleton<ShopsManager>(ShopsManager(
GetIt.I.get<OpenStreetMap>(),
GetIt.I.get<Backend>(),
GetIt.I.get<ProductsObtainer>(),
GetIt.I.get<Analytics>(),
GetIt.I.get<OsmCacher>(),
GetIt.I.get<OsmTerritoryCacher>(),
GetIt.I.get<OffGeoHelper>(),
));
GetIt.I.registerSingleton<SuggestedProductsManager>(SuggestedProductsManager(
Expand All @@ -176,8 +177,8 @@ void initDI() {
GetIt.I.get<ProductsAtShopsExtraPropertiesManager>(),
GetIt.I.get<Settings>(),
));
GetIt.I.registerSingleton<RoadsManager>(
RoadsManager(GetIt.I.get<OpenStreetMap>(), GetIt.I.get<OsmCacher>()));
GetIt.I.registerSingleton<RoadsManager>(RoadsManager(
GetIt.I.get<OpenStreetMap>(), GetIt.I.get<OsmTerritoryCacher>()));
GetIt.I.registerSingleton<OsmSearcher>(
OsmSearcher(GetIt.I.get<OpenStreetMap>()));
GetIt.I.registerSingleton<DirectionsManager>(DirectionsManager());
Expand All @@ -202,4 +203,8 @@ void initDI() {
GetIt.I.registerSingleton(ShopsCreationManager(
GetIt.I.get<ShopsManager>(),
));
GetIt.I.registerSingleton(NewsFeedManager(
GetIt.I.get<Backend>(),
GetIt.I.get<LatestCameraPosStorage>(),
));
}
8 changes: 8 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,14 @@
"mod_reason_some_ingredient_is_possibly_non_vegan": "Product has ambiguous ingredients (ambiguous ingredients can be vegan or non-vegan depending on how they were manufactured)",
"mod_reason_some_of_product_series_have_non_vegan_ingredients": "Some of product series have non-vegan ingredients and some don't — please read ingredients list carefully!",
"mod_reason_tested_on_animals": "Animal tested product",
"news_feed_page_new_events_title": "New events nearby",
"@news_feed_page_new_events_title": {
"description": "Title of the News Feed Page's news list, shown above of all news"
},
"news_feed_page_label_for_new_product_at_shop": "New product",
"@news_feed_page_label_for_new_product_at_shop": {
"description": "Label displayed on news pieces about new products added to certain shops (1 news piece = 1 product added to 1 shop)"
},
"pick_existing_shop_page_title": "There are some stores nearby - maybe the new shop is already among them?",
"@pick_existing_shop_page_title": {
"description": "When the user wants to create a new store, we first check if there are any stores nearby, so that the user wouldn't create a duplicate of an existing store. If we find stores nearby, we open a page with a list of these stores, and ask the user if the store they wanted to create is among the stores we've found. The English title is long - hopefully translations to other languages won't be too much longer, because a user's attention span is short and they might not even read long sentences unfortunately."
Expand Down
4 changes: 3 additions & 1 deletion lib/outside/backend/news/news_feed_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class NewsFeedManager {
return Err(backendNewsRes.unwrapErr().toGeneral());
}
final backendNews = backendNewsRes.unwrap();
return Ok(backendNews.results.toList());
final results = backendNews.results.toList();
results.sort((a, b) => b.creationTimeSecs - a.creationTimeSecs);
return Ok(results);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ enum OsmCacherError {
TERRITORY_NOT_FOUND,
}

class OsmCacher extends DatabaseBase {
class OsmTerritoryCacher extends DatabaseBase {
final _dbCompleter = Completer<Database>();
Future<Database> get _db => _dbCompleter.future;

Expand All @@ -62,11 +62,11 @@ class OsmCacher extends DatabaseBase {
return _db;
}

OsmCacher() {
OsmTerritoryCacher() {
_initAsync();
}

OsmCacher.withDb(Database db) {
OsmTerritoryCacher.withDb(Database db) {
_initAsync(db);
}

Expand Down
4 changes: 2 additions & 2 deletions lib/outside/map/roads_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import 'package:plante/logging/log.dart';
import 'package:plante/model/coords_bounds.dart';
import 'package:plante/outside/map/osm/open_street_map.dart';
import 'package:plante/outside/map/osm/osm_cached_territory.dart';
import 'package:plante/outside/map/osm/osm_cacher.dart';
import 'package:plante/outside/map/osm/osm_overpass.dart';
import 'package:plante/outside/map/osm/osm_road.dart';
import 'package:plante/outside/map/osm/osm_territory_cacher.dart';

enum RoadsManagerError {
NETWORK,
Expand All @@ -24,7 +24,7 @@ class RoadsManager {
static final requestedRadios = kmToGrad(REQUESTED_RADIUS_KM);

final OpenStreetMap _osm;
final OsmCacher _cacher;
final OsmTerritoryCacher _cacher;

RoadsManager(this._osm, this._cacher);

Expand Down
75 changes: 59 additions & 16 deletions lib/outside/map/shops_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,28 @@ import 'package:plante/outside/backend/backend.dart';
import 'package:plante/outside/backend/product_at_shop_source.dart';
import 'package:plante/outside/backend/product_presence_vote_result.dart';
import 'package:plante/outside/map/osm/open_street_map.dart';
import 'package:plante/outside/map/osm/osm_cacher.dart';
import 'package:plante/outside/map/osm/osm_overpass.dart';
import 'package:plante/outside/map/osm/osm_shop.dart';
import 'package:plante/outside/map/osm/osm_territory_cacher.dart';
import 'package:plante/outside/map/osm/osm_uid.dart';
import 'package:plante/outside/map/shops_large_local_cache.dart';
import 'package:plante/outside/map/shops_large_local_cache_isolated.dart';
import 'package:plante/outside/map/shops_manager_backend_worker.dart';
import 'package:plante/outside/map/shops_manager_fetch_shops_helper.dart';
import 'package:plante/outside/map/shops_manager_shops_territories_fetcher.dart';
import 'package:plante/outside/map/shops_manager_types.dart';
import 'package:plante/outside/off/off_geo_helper.dart';
import 'package:plante/products/products_obtainer.dart';

/// NOTE: this class is a total mess among with both
/// [ShopsManagerFetchShopsHelper] and [ShopsManagerBackendWorker].
/// [ShopsManagerShopsTerritoriesFetcher] and [ShopsManagerBackendWorker].
/// They need a refactoring.
class ShopsManager {
static const DAYS_BEFORE_PERSISTENT_CACHE_IS_OLD = 30;
static const DAYS_BEFORE_PERSISTENT_CACHE_IS_ANCIENT = 90;
final Analytics _analytics;
final OsmCacher _osmCacher;
final OsmTerritoryCacher _osmTerritoriesCacher;
final OffGeoHelper _offGeoHelper;
late final ShopsManagerFetchShopsHelper _fetchShopsHelper;
late final ShopsManagerShopsTerritoriesFetcher _territoriesFetcher;
final _listeners = <ShopsManagerListener>[];
final OpenStreetMap _osm;
late final ShopsManagerBackendWorker _backendWorker;
Expand All @@ -53,11 +53,11 @@ class ShopsManager {

/// [largeCache] param is used in the Web Admin project.
ShopsManager(this._osm, Backend backend, ProductsObtainer productsObtainer,
this._analytics, this._osmCacher, this._offGeoHelper,
this._analytics, this._osmTerritoriesCacher, this._offGeoHelper,
{ShopsLargeLocalCache? largeCache})
: _backendWorker = ShopsManagerBackendWorker(backend, productsObtainer) {
_fetchShopsHelper =
ShopsManagerFetchShopsHelper(_backendWorker, _osmCacher);
_territoriesFetcher = ShopsManagerShopsTerritoriesFetcher(
_backendWorker, _osmTerritoriesCacher);
_initAsync(largeCache);
}

Expand Down Expand Up @@ -116,7 +116,7 @@ class ShopsManager {
if (await _loadShopsFromCache(bounds) != null) {
return true;
}
for (final territory in await _osmCacher.getCachedShops()) {
for (final territory in await _osmTerritoriesCacher.getCachedShops()) {
if (territory.bounds.containsBounds(bounds)) {
return true;
}
Expand Down Expand Up @@ -157,7 +157,7 @@ class ShopsManager {
return Ok(existingCache);
}

final shopsFetchResult = await _fetchShopsHelper.fetchShops(overpass,
final shopsFetchResult = await _territoriesFetcher.fetchShops(overpass,
viewPort: bounds,
osmBoundsSizesToRequest: [100, 91],
planteBoundsSizeToRequest: 90);
Expand Down Expand Up @@ -191,15 +191,16 @@ class ShopsManager {
}

void _ensureAllShopsArePresentInOsmCache(Iterable<Shop> shops) async {
for (final territory in await _osmCacher.getCachedShops()) {
for (final territory in await _osmTerritoriesCacher.getCachedShops()) {
final territoryShopsUids =
territory.entities.map((e) => e.osmUID).toSet();
final shopsToInsert = <OsmUID, OsmShop>{};
for (final shop in shops) {
if (territory.bounds.containsShop(shop) &&
!territoryShopsUids.contains(shop.osmUID)) {
shopsToInsert[shop.osmUID] = shop.osmShop;
unawaited(_osmCacher.addShopToCache(territory.id, shop.osmShop));
unawaited(
_osmTerritoriesCacher.addShopToCache(territory.id, shop.osmShop));
}
}
}
Expand Down Expand Up @@ -352,9 +353,10 @@ class ShopsManager {
_loadedAreas[loadedArea]!.add(shop.osmUID);
}
}
for (final territory in await _osmCacher.getCachedShops()) {
for (final territory in await _osmTerritoriesCacher.getCachedShops()) {
if (territory.bounds.containsShop(shop)) {
unawaited(_osmCacher.addShopToCache(territory.id, shop.osmShop));
unawaited(
_osmTerritoriesCacher.addShopToCache(territory.id, shop.osmShop));
}
}
_notifyListenersShopCreated(shop);
Expand Down Expand Up @@ -406,16 +408,57 @@ class ShopsManager {
}

Future<void> clearCache() async {
await _fetchShopsHelper.clearCache();
await _territoriesFetcher.clearCache();
await (await _slowCache).clear();
_loadedAreas.clear();
_rangesCache.clear();
_notifyListenersAboutChange();
}

Future<Result<Map<OsmUID, Shop>, ShopsManagerError>> fetchShopsByUIDs(
Iterable<OsmUID> uids) async {
final cache = await _slowCache;
final shopsFromCache = await cache.getShops(uids);

final obtainedUIDs = shopsFromCache.keys.toSet();
final notYetObtainedUIDs = uids.where((uid) => !obtainedUIDs.contains(uid));
if (notYetObtainedUIDs.isEmpty) {
return Ok(shopsFromCache);
}

return _osm.withOverpass((overpass) async {
final osmShopsRes =
await overpass.fetchShops(osmUIDs: notYetObtainedUIDs);
if (osmShopsRes.isErr) {
return Err(osmShopsRes.unwrapErr().convert());
}
final shopsRes = await inflateOsmShops(osmShopsRes.unwrap());
if (shopsRes.isErr) {
return Err(shopsRes.unwrapErr());
}
final shopsFromNetwork = shopsRes.unwrap();
await cache.addShops(shopsFromNetwork.values);
return Ok({
...shopsFromCache,
...shopsFromNetwork,
});
});
}
}

extension _BoundsExt on CoordsBounds {
extension on CoordsBounds {
bool containsShop(Shop shop) {
return contains(Coord(lat: shop.latitude, lon: shop.longitude));
}
}

extension on OpenStreetMapError {
ShopsManagerError convert() {
switch (this) {
case OpenStreetMapError.NETWORK:
return ShopsManagerError.NETWORK_ERROR;
case OpenStreetMapError.OTHER:
return ShopsManagerError.OTHER;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ import 'package:plante/logging/log.dart';
import 'package:plante/model/coords_bounds.dart';
import 'package:plante/outside/map/fetched_shops.dart';
import 'package:plante/outside/map/osm/osm_cached_territory.dart';
import 'package:plante/outside/map/osm/osm_cacher.dart';
import 'package:plante/outside/map/osm/osm_overpass.dart';
import 'package:plante/outside/map/osm/osm_shop.dart';
import 'package:plante/outside/map/osm/osm_territory_cacher.dart';
import 'package:plante/outside/map/shops_manager.dart';
import 'package:plante/outside/map/shops_manager_backend_worker.dart';
import 'package:plante/outside/map/shops_manager_types.dart';

// Extracted from ShopsManager logic of shops fetching
class ShopsManagerFetchShopsHelper {
// Extracted from ShopsManager logic of shops' territories fetching
class ShopsManagerShopsTerritoriesFetcher {
final ShopsManagerBackendWorker _shopsManagerBackendWorker;
final OsmCacher _osmCacher;
final OsmTerritoryCacher _osmCacher;

ShopsManagerFetchShopsHelper(
ShopsManagerShopsTerritoriesFetcher(
this._shopsManagerBackendWorker, this._osmCacher);

/// [osmBoundsSizesToRequest] - list of bounds sizes which will be requested
Expand Down
6 changes: 6 additions & 0 deletions lib/ui/base/text_styles.dart
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,12 @@ class TextStyles {
color: ColorsPlante.grey,
fontWeight: FontWeight.bold);

static const TextStyle newsTitle = TextStyle(
fontFamily: 'OpenSans',
fontWeight: FontWeight.bold,
fontSize: 18,
color: ColorsPlante.mainTextBlack);

static String get montserrat {
if (_useSafeFont()) {
return 'OpenSans';
Expand Down
Loading

0 comments on commit e60db8b

Please sign in to comment.