Merge branch 'main' into await_warnings

This commit is contained in:
vishnukvmd 2023-12-27 22:05:28 +05:30
commit e24617cb96
8 changed files with 202 additions and 57 deletions

View file

@ -105,3 +105,7 @@ If you're interested in helping out with translation, please visit our [Crowdin
Follow us on [Twitter](https://twitter.com/enteio), join [r/enteio](https://reddit.com/r/enteio) or hang out on our [Discord](https://ente.io/discord) to get regular updates, connect with other customers, and discuss your ideas. Follow us on [Twitter](https://twitter.com/enteio), join [r/enteio](https://reddit.com/r/enteio) or hang out on our [Discord](https://ente.io/discord) to get regular updates, connect with other customers, and discuss your ideas.
An important part of our journey is to build better software by consistently listening to community feedback. Please feel free to [share your thoughts](mailto:feedback@ente.io) with us at any time. An important part of our journey is to build better software by consistently listening to community feedback. Please feel free to [share your thoughts](mailto:feedback@ente.io) with us at any time.
## 🙇 Attributions
- [Simple Maps](https://simplemaps.com/data/world-cities)

View file

@ -27,6 +27,7 @@ import 'package:photos/services/favorites_service.dart';
import 'package:photos/services/ignored_files_service.dart'; import 'package:photos/services/ignored_files_service.dart';
import 'package:photos/services/memories_service.dart'; import 'package:photos/services/memories_service.dart';
import 'package:photos/services/search_service.dart'; import 'package:photos/services/search_service.dart';
import "package:photos/services/semantic_search/semantic_search_service.dart";
import 'package:photos/services/sync_service.dart'; import 'package:photos/services/sync_service.dart';
import 'package:photos/utils/crypto_util.dart'; import 'package:photos/utils/crypto_util.dart';
import 'package:photos/utils/file_uploader.dart'; import 'package:photos/utils/file_uploader.dart';
@ -157,7 +158,9 @@ class Configuration {
_cachedToken = null; _cachedToken = null;
_secretKey = null; _secretKey = null;
await FilesDB.instance.clearTable(); await FilesDB.instance.clearTable();
await ObjectBox.instance.clearTable(); SemanticSearchService.instance.hasInitialized
? await ObjectBox.instance.clearTable()
: null;
await CollectionsDB.instance.clearTable(); await CollectionsDB.instance.clearTable();
await MemoriesDB.instance.clearTable(); await MemoriesDB.instance.clearTable();
await PublicKeysDB.instance.clearTable(); await PublicKeysDB.instance.clearTable();

View file

@ -61,6 +61,8 @@ const defaultRadiusValues = <double>[1, 2, 10, 20, 40, 80, 200, 400, 1200];
const defaultRadiusValue = 40.0; const defaultRadiusValue = 40.0;
const defaultCityRadius = 10.0;
const galleryGridSpacing = 2.0; const galleryGridSpacing = 2.0;
const searchSectionLimit = 7; const searchSectionLimit = 7;

View file

@ -10,18 +10,26 @@ import "package:photos/models/local_entity_data.dart";
import "package:photos/models/location/location.dart"; import "package:photos/models/location/location.dart";
import 'package:photos/models/location_tag/location_tag.dart'; import 'package:photos/models/location_tag/location_tag.dart';
import "package:photos/services/entity_service.dart"; import "package:photos/services/entity_service.dart";
import "package:photos/services/feature_flag_service.dart";
import "package:photos/services/remote_assets_service.dart";
import "package:shared_preferences/shared_preferences.dart"; import "package:shared_preferences/shared_preferences.dart";
class LocationService { class LocationService {
late SharedPreferences prefs; late SharedPreferences prefs;
final Logger _logger = Logger((LocationService).toString()); final Logger _logger = Logger((LocationService).toString());
final List<City> _cities = [];
LocationService._privateConstructor(); LocationService._privateConstructor();
static final LocationService instance = LocationService._privateConstructor(); static final LocationService instance = LocationService._privateConstructor();
static const kCitiesRemotePath = "https://assets.ente.io/world_cities.json";
void init(SharedPreferences preferences) { void init(SharedPreferences preferences) {
prefs = preferences; prefs = preferences;
if (FeatureFlagService.instance.isInternalUserOrDebugBuild()) {
_loadCities();
}
} }
Future<Iterable<LocalEntity<LocationTag>>> _getStoredLocationTags() async { Future<Iterable<LocalEntity<LocationTag>>> _getStoredLocationTags() async {
@ -31,6 +39,10 @@ class LocationService {
); );
} }
List<City> getAllCities() {
return _cities;
}
Future<Iterable<LocalEntity<LocationTag>>> getLocationTags() { Future<Iterable<LocalEntity<LocationTag>>> getLocationTags() {
return _getStoredLocationTags(); return _getStoredLocationTags();
} }
@ -203,6 +215,52 @@ class LocationService {
rethrow; rethrow;
} }
} }
Future<void> _loadCities() async {
try {
final data =
await RemoteAssetsService.instance.getAsset(kCitiesRemotePath);
final citiesJson = json.decode(await data.readAsString());
final List<dynamic> jsonData = citiesJson['data'];
final cities =
jsonData.map<City>((jsonItem) => City.fromMap(jsonItem)).toList();
_cities.clear();
_cities.addAll(cities);
_logger.info("Loaded cities");
} catch (e, s) {
_logger.severe("Failed to load cities", e, s);
}
}
}
class City {
final String city;
final String country;
final double lat;
final double lng;
City({
required this.city,
required this.country,
required this.lat,
required this.lng,
});
factory City.fromMap(Map<String, dynamic> map) {
return City(
city: map['city'] ?? '',
country: map['country'] ?? '',
lat: map['lat']?.toDouble() ?? 0.0,
lng: map['lng']?.toDouble() ?? 0.0,
);
}
factory City.fromJson(String source) => City.fromMap(json.decode(source));
@override
String toString() {
return 'City(city: $city, country: $country, lat: $lat, lng: $lng)';
}
} }
class GPSData { class GPSData {

View file

@ -0,0 +1,57 @@
import "dart:io";
import "package:logging/logging.dart";
import "package:path_provider/path_provider.dart";
import "package:photos/core/network/network.dart";
class RemoteAssetsService {
static final _logger = Logger("RemoteAssetsService");
RemoteAssetsService._privateConstructor();
static final RemoteAssetsService instance =
RemoteAssetsService._privateConstructor();
Future<File> getAsset(String remotePath) async {
final path = await _getLocalPath(remotePath);
final file = File(path);
if (await file.exists()) {
_logger.info("Returning cached file for $remotePath");
return file;
} else {
final tempFile = File(path + ".temp");
await _downloadFile(remotePath, tempFile.path);
await tempFile.rename(path);
return File(path);
}
}
Future<String> _getLocalPath(String remotePath) async {
return (await getTemporaryDirectory()).path +
"/assets/" +
_urlToFileName(remotePath);
}
String _urlToFileName(String url) {
// Remove the protocol part (http:// or https://)
String fileName = url
.replaceAll(RegExp(r'https?://'), '')
// Replace all non-alphanumeric characters except for underscores and periods with an underscore
.replaceAll(RegExp(r'[^\w\.]'), '_');
// Optionally, you might want to trim the resulting string to a certain length
// Replace periods with underscores for better readability, if desired
fileName = fileName.replaceAll('.', '_');
return fileName;
}
Future<void> _downloadFile(String url, String savePath) async {
_logger.info("Downloading " + url);
final existingFile = File(savePath);
if (await existingFile.exists()) {
await existingFile.delete();
}
await NetworkClient.instance.getDio().download(url, savePath);
}
}

View file

@ -3,6 +3,7 @@ import "dart:math";
import "package:flutter/cupertino.dart"; import "package:flutter/cupertino.dart";
import "package:intl/intl.dart"; import "package:intl/intl.dart";
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import "package:photos/core/constants.dart";
import 'package:photos/core/event_bus.dart'; import 'package:photos/core/event_bus.dart';
import 'package:photos/data/holidays.dart'; import 'package:photos/data/holidays.dart';
import 'package:photos/data/months.dart'; import 'package:photos/data/months.dart';
@ -17,6 +18,7 @@ import "package:photos/models/file/extensions/file_props.dart";
import 'package:photos/models/file/file.dart'; import 'package:photos/models/file/file.dart';
import 'package:photos/models/file/file_type.dart'; import 'package:photos/models/file/file_type.dart';
import "package:photos/models/local_entity_data.dart"; import "package:photos/models/local_entity_data.dart";
import "package:photos/models/location/location.dart";
import "package:photos/models/location_tag/location_tag.dart"; import "package:photos/models/location_tag/location_tag.dart";
import 'package:photos/models/search/album_search_result.dart'; import 'package:photos/models/search/album_search_result.dart';
import 'package:photos/models/search/generic_search_result.dart'; import 'package:photos/models/search/generic_search_result.dart';
@ -681,6 +683,52 @@ class SearchService {
return searchResults; return searchResults;
} }
Future<List<GenericSearchResult>> getCityResults(String query) async {
final startTime = DateTime.now().microsecondsSinceEpoch;
final List<GenericSearchResult> searchResults = [];
final cities = LocationService.instance.getAllCities();
final matchingCities = <City>[];
final queryLower = query.toLowerCase();
for (City city in cities) {
if (city.city.toLowerCase().startsWith(queryLower)) {
matchingCities.add(city);
}
}
final files = await getAllFiles();
final Map<City, List<EnteFile>> results = {};
for (final city in matchingCities) {
final List<EnteFile> matchingFiles = [];
final cityLocation = Location(latitude: city.lat, longitude: city.lng);
for (final file in files) {
if (file.hasLocation) {
if (LocationService.instance.isFileInsideLocationTag(
cityLocation,
file.location!,
defaultCityRadius,
)) {
matchingFiles.add(file);
}
}
}
if (matchingFiles.isNotEmpty) {
results[city] = matchingFiles;
}
}
for (final entry in results.entries) {
searchResults.add(
GenericSearchResult(
ResultType.location,
entry.key.city,
entry.value,
),
);
}
final endTime = DateTime.now().microsecondsSinceEpoch;
_logger
.info("Time taken " + ((endTime - startTime) / 1000).toString() + "ms");
return searchResults;
}
Future<List<GenericSearchResult>> getAllLocationTags(int? limit) async { Future<List<GenericSearchResult>> getAllLocationTags(int? limit) async {
try { try {
final Map<LocalEntity<LocationTag>, List<EnteFile>> tagToItemsMap = {}; final Map<LocalEntity<LocationTag>, List<EnteFile>> tagToItemsMap = {};

View file

@ -46,6 +46,8 @@ class SemanticSearchService {
Future<List<EnteFile>>? _ongoingRequest; Future<List<EnteFile>>? _ongoingRequest;
PendingQuery? _nextQuery; PendingQuery? _nextQuery;
get hasInitialized => _hasInitialized;
Future<void> init() async { Future<void> init() async {
if (!LocalSettings.instance.hasEnabledMagicSearch()) { if (!LocalSettings.instance.hasEnabledMagicSearch()) {
return; return;

View file

@ -278,7 +278,7 @@ class SearchWidgetState extends State<SearchWidget> {
String query, String query,
) { ) {
int resultCount = 0; int resultCount = 0;
final maxResultCount = _isYearValid(query) ? 11 : 10; final maxResultCount = _isYearValid(query) ? 12 : 11;
final streamController = StreamController<List<SearchResult>>(); final streamController = StreamController<List<SearchResult>>();
if (query.isEmpty) { if (query.isEmpty) {
@ -286,113 +286,84 @@ class SearchWidgetState extends State<SearchWidget> {
streamController.close(); streamController.close();
return streamController.stream; return streamController.stream;
} }
void onResultsReceived(List<SearchResult> results) {
streamController.sink.add(results);
resultCount++;
if (resultCount == maxResultCount) {
streamController.close();
}
}
if (_isYearValid(query)) { if (_isYearValid(query)) {
_searchService.getYearSearchResults(query).then((yearSearchResults) { _searchService.getYearSearchResults(query).then((yearSearchResults) {
streamController.sink.add(yearSearchResults); onResultsReceived(yearSearchResults);
resultCount++;
if (resultCount == maxResultCount) {
streamController.close();
}
}); });
} }
_searchService.getHolidaySearchResults(context, query).then( _searchService.getHolidaySearchResults(context, query).then(
(holidayResults) { (holidayResults) {
streamController.sink.add(holidayResults); onResultsReceived(holidayResults);
resultCount++;
if (resultCount == maxResultCount) {
streamController.close();
}
}, },
); );
_searchService.getFileTypeResults(context, query).then( _searchService.getFileTypeResults(context, query).then(
(fileTypeSearchResults) { (fileTypeSearchResults) {
streamController.sink.add(fileTypeSearchResults); onResultsReceived(fileTypeSearchResults);
resultCount++;
if (resultCount == maxResultCount) {
streamController.close();
}
}, },
); );
_searchService.getCaptionAndNameResults(query).then( _searchService.getCaptionAndNameResults(query).then(
(captionAndDisplayNameResult) { (captionAndDisplayNameResult) {
streamController.sink.add(captionAndDisplayNameResult); onResultsReceived(captionAndDisplayNameResult);
resultCount++;
if (resultCount == maxResultCount) {
streamController.close();
}
}, },
); );
_searchService.getFileExtensionResults(query).then( _searchService.getFileExtensionResults(query).then(
(fileExtnResult) { (fileExtnResult) {
streamController.sink.add(fileExtnResult); onResultsReceived(fileExtnResult);
resultCount++;
if (resultCount == maxResultCount) {
streamController.close();
}
}, },
); );
_searchService.getLocationResults(query).then( _searchService.getLocationResults(query).then(
(locationResult) { (locationResult) {
streamController.sink.add(locationResult); onResultsReceived(locationResult);
resultCount++; },
if (resultCount == maxResultCount) { );
streamController.close();
} _searchService.getCityResults(query).then(
(results) {
onResultsReceived(results);
}, },
); );
_searchService.getCollectionSearchResults(query).then( _searchService.getCollectionSearchResults(query).then(
(collectionResults) { (collectionResults) {
streamController.sink.add(collectionResults); onResultsReceived(collectionResults);
resultCount++;
if (resultCount == maxResultCount) {
streamController.close();
}
}, },
); );
_searchService.getMonthSearchResults(context, query).then( _searchService.getMonthSearchResults(context, query).then(
(monthResults) { (monthResults) {
streamController.sink.add(monthResults); onResultsReceived(monthResults);
resultCount++;
if (resultCount == maxResultCount) {
streamController.close();
}
}, },
); );
_searchService.getDateResults(context, query).then( _searchService.getDateResults(context, query).then(
(possibleEvents) { (possibleEvents) {
streamController.sink.add(possibleEvents); onResultsReceived(possibleEvents);
resultCount++;
if (resultCount == maxResultCount) {
streamController.close();
}
}, },
); );
_searchService.getMagicSearchResults(context, query).then( _searchService.getMagicSearchResults(context, query).then(
(magicResults) { (magicResults) {
streamController.sink.add(magicResults); onResultsReceived(magicResults);
resultCount++;
if (resultCount == maxResultCount) {
streamController.close();
}
}, },
); );
_searchService.getContactSearchResults(query).then( _searchService.getContactSearchResults(query).then(
(contactResults) { (contactResults) {
streamController.sink.add(contactResults); onResultsReceived(contactResults);
resultCount++;
if (resultCount == maxResultCount) {
streamController.close();
}
}, },
); );