mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
feat: Maplibre (#4294)
* maplibre on web, custom styles from server Actually use new vector tile server, custom style.json support multiple style files, light/dark mode cleanup, use new map everywhere send file directly instead of loading first better light/dark mode switching remove leaflet fix mapstyles dto, first draft of map settings delete and add styles fix delete default styles fix tests only allow one light and one dark style url revert config core changes fix server config store fix tests move axios fetches to repo fix package-lock fix tests * open api * add assets to docker container * web: use mapSettings color for style * style: add unique ids to map styles * mobile: use style json for vector / raster * do not use svelte-material-icons * add click events to markers, simplify asset detail map * improve map performance by using asset thumbnails for markers instead of original file * Remove custom attribution (by request) * mobile: update map attribution * style: map dark mode * style: map light mode * zoom level for state * styling * overflow gradient * Limit maxZoom to 14 * mobile: listen for mapStyle changes in MapThumbnail * mobile: update concurrency --------- Co-authored-by: shalong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: bo0tzz <git@bo0tzz.me> Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
parent
5423f1c25b
commit
a147dee4b6
63 changed files with 5457 additions and 9751 deletions
1
mobile/openapi/lib/api.dart
generated
1
mobile/openapi/lib/api.dart
generated
|
|
@ -116,6 +116,7 @@ part 'model/login_credential_dto.dart';
|
|||
part 'model/login_response_dto.dart';
|
||||
part 'model/logout_response_dto.dart';
|
||||
part 'model/map_marker_response_dto.dart';
|
||||
part 'model/map_theme.dart';
|
||||
part 'model/memory_lane_response_dto.dart';
|
||||
part 'model/merge_person_dto.dart';
|
||||
part 'model/model_type.dart';
|
||||
|
|
|
|||
49
mobile/openapi/lib/api/system_config_api.dart
generated
49
mobile/openapi/lib/api/system_config_api.dart
generated
|
|
@ -98,6 +98,55 @@ class SystemConfigApi {
|
|||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /system-config/map/style.json' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [MapTheme] theme (required):
|
||||
Future<Response> getMapStyleWithHttpInfo(MapTheme theme,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/system-config/map/style.json';
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
queryParams.addAll(_queryParams('', 'theme', theme));
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'GET',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [MapTheme] theme (required):
|
||||
Future<Object?> getMapStyle(MapTheme theme,) async {
|
||||
final response = await getMapStyleWithHttpInfo(theme,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'Object',) as Object;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /system-config/storage-template-options' operation and returns the [Response].
|
||||
Future<Response> getStorageTemplateOptionsWithHttpInfo() async {
|
||||
// ignore: prefer_const_declarations
|
||||
|
|
|
|||
2
mobile/openapi/lib/api_client.dart
generated
2
mobile/openapi/lib/api_client.dart
generated
|
|
@ -321,6 +321,8 @@ class ApiClient {
|
|||
return LogoutResponseDto.fromJson(value);
|
||||
case 'MapMarkerResponseDto':
|
||||
return MapMarkerResponseDto.fromJson(value);
|
||||
case 'MapTheme':
|
||||
return MapThemeTypeTransformer().decode(value);
|
||||
case 'MemoryLaneResponseDto':
|
||||
return MemoryLaneResponseDto.fromJson(value);
|
||||
case 'MergePersonDto':
|
||||
|
|
|
|||
3
mobile/openapi/lib/api_helper.dart
generated
3
mobile/openapi/lib/api_helper.dart
generated
|
|
@ -88,6 +88,9 @@ String parameterToString(dynamic value) {
|
|||
if (value is LibraryType) {
|
||||
return LibraryTypeTypeTransformer().encode(value).toString();
|
||||
}
|
||||
if (value is MapTheme) {
|
||||
return MapThemeTypeTransformer().encode(value).toString();
|
||||
}
|
||||
if (value is ModelType) {
|
||||
return ModelTypeTypeTransformer().encode(value).toString();
|
||||
}
|
||||
|
|
|
|||
85
mobile/openapi/lib/model/map_theme.dart
generated
Normal file
85
mobile/openapi/lib/model/map_theme.dart
generated
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.12
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
|
||||
class MapTheme {
|
||||
/// Instantiate a new enum with the provided [value].
|
||||
const MapTheme._(this.value);
|
||||
|
||||
/// The underlying value of this enum member.
|
||||
final String value;
|
||||
|
||||
@override
|
||||
String toString() => value;
|
||||
|
||||
String toJson() => value;
|
||||
|
||||
static const light = MapTheme._(r'light');
|
||||
static const dark = MapTheme._(r'dark');
|
||||
|
||||
/// List of all possible values in this [enum][MapTheme].
|
||||
static const values = <MapTheme>[
|
||||
light,
|
||||
dark,
|
||||
];
|
||||
|
||||
static MapTheme? fromJson(dynamic value) => MapThemeTypeTransformer().decode(value);
|
||||
|
||||
static List<MapTheme>? listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <MapTheme>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = MapTheme.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
}
|
||||
|
||||
/// Transformation class that can [encode] an instance of [MapTheme] to String,
|
||||
/// and [decode] dynamic data back to [MapTheme].
|
||||
class MapThemeTypeTransformer {
|
||||
factory MapThemeTypeTransformer() => _instance ??= const MapThemeTypeTransformer._();
|
||||
|
||||
const MapThemeTypeTransformer._();
|
||||
|
||||
String encode(MapTheme data) => data.value;
|
||||
|
||||
/// Decodes a [dynamic value][data] to a MapTheme.
|
||||
///
|
||||
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
|
||||
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
|
||||
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
|
||||
///
|
||||
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
|
||||
/// and users are still using an old app with the old code.
|
||||
MapTheme? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'light': return MapTheme.light;
|
||||
case r'dark': return MapTheme.dark;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Singleton [MapThemeTypeTransformer] instance.
|
||||
static MapThemeTypeTransformer? _instance;
|
||||
}
|
||||
|
||||
10
mobile/openapi/lib/model/server_config_dto.dart
generated
10
mobile/openapi/lib/model/server_config_dto.dart
generated
|
|
@ -15,7 +15,6 @@ class ServerConfigDto {
|
|||
ServerConfigDto({
|
||||
required this.isInitialized,
|
||||
required this.loginPageMessage,
|
||||
required this.mapTileUrl,
|
||||
required this.oauthButtonText,
|
||||
required this.trashDays,
|
||||
});
|
||||
|
|
@ -24,8 +23,6 @@ class ServerConfigDto {
|
|||
|
||||
String loginPageMessage;
|
||||
|
||||
String mapTileUrl;
|
||||
|
||||
String oauthButtonText;
|
||||
|
||||
int trashDays;
|
||||
|
|
@ -34,7 +31,6 @@ class ServerConfigDto {
|
|||
bool operator ==(Object other) => identical(this, other) || other is ServerConfigDto &&
|
||||
other.isInitialized == isInitialized &&
|
||||
other.loginPageMessage == loginPageMessage &&
|
||||
other.mapTileUrl == mapTileUrl &&
|
||||
other.oauthButtonText == oauthButtonText &&
|
||||
other.trashDays == trashDays;
|
||||
|
||||
|
|
@ -43,18 +39,16 @@ class ServerConfigDto {
|
|||
// ignore: unnecessary_parenthesis
|
||||
(isInitialized.hashCode) +
|
||||
(loginPageMessage.hashCode) +
|
||||
(mapTileUrl.hashCode) +
|
||||
(oauthButtonText.hashCode) +
|
||||
(trashDays.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'ServerConfigDto[isInitialized=$isInitialized, loginPageMessage=$loginPageMessage, mapTileUrl=$mapTileUrl, oauthButtonText=$oauthButtonText, trashDays=$trashDays]';
|
||||
String toString() => 'ServerConfigDto[isInitialized=$isInitialized, loginPageMessage=$loginPageMessage, oauthButtonText=$oauthButtonText, trashDays=$trashDays]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'isInitialized'] = this.isInitialized;
|
||||
json[r'loginPageMessage'] = this.loginPageMessage;
|
||||
json[r'mapTileUrl'] = this.mapTileUrl;
|
||||
json[r'oauthButtonText'] = this.oauthButtonText;
|
||||
json[r'trashDays'] = this.trashDays;
|
||||
return json;
|
||||
|
|
@ -70,7 +64,6 @@ class ServerConfigDto {
|
|||
return ServerConfigDto(
|
||||
isInitialized: mapValueOfType<bool>(json, r'isInitialized')!,
|
||||
loginPageMessage: mapValueOfType<String>(json, r'loginPageMessage')!,
|
||||
mapTileUrl: mapValueOfType<String>(json, r'mapTileUrl')!,
|
||||
oauthButtonText: mapValueOfType<String>(json, r'oauthButtonText')!,
|
||||
trashDays: mapValueOfType<int>(json, r'trashDays')!,
|
||||
);
|
||||
|
|
@ -122,7 +115,6 @@ class ServerConfigDto {
|
|||
static const requiredKeys = <String>{
|
||||
'isInitialized',
|
||||
'loginPageMessage',
|
||||
'mapTileUrl',
|
||||
'oauthButtonText',
|
||||
'trashDays',
|
||||
};
|
||||
|
|
|
|||
24
mobile/openapi/lib/model/system_config_map_dto.dart
generated
24
mobile/openapi/lib/model/system_config_map_dto.dart
generated
|
|
@ -13,32 +13,38 @@ part of openapi.api;
|
|||
class SystemConfigMapDto {
|
||||
/// Returns a new [SystemConfigMapDto] instance.
|
||||
SystemConfigMapDto({
|
||||
required this.darkStyle,
|
||||
required this.enabled,
|
||||
required this.tileUrl,
|
||||
required this.lightStyle,
|
||||
});
|
||||
|
||||
String darkStyle;
|
||||
|
||||
bool enabled;
|
||||
|
||||
String tileUrl;
|
||||
String lightStyle;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SystemConfigMapDto &&
|
||||
other.darkStyle == darkStyle &&
|
||||
other.enabled == enabled &&
|
||||
other.tileUrl == tileUrl;
|
||||
other.lightStyle == lightStyle;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(darkStyle.hashCode) +
|
||||
(enabled.hashCode) +
|
||||
(tileUrl.hashCode);
|
||||
(lightStyle.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SystemConfigMapDto[enabled=$enabled, tileUrl=$tileUrl]';
|
||||
String toString() => 'SystemConfigMapDto[darkStyle=$darkStyle, enabled=$enabled, lightStyle=$lightStyle]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'darkStyle'] = this.darkStyle;
|
||||
json[r'enabled'] = this.enabled;
|
||||
json[r'tileUrl'] = this.tileUrl;
|
||||
json[r'lightStyle'] = this.lightStyle;
|
||||
return json;
|
||||
}
|
||||
|
||||
|
|
@ -50,8 +56,9 @@ class SystemConfigMapDto {
|
|||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return SystemConfigMapDto(
|
||||
darkStyle: mapValueOfType<String>(json, r'darkStyle')!,
|
||||
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
||||
tileUrl: mapValueOfType<String>(json, r'tileUrl')!,
|
||||
lightStyle: mapValueOfType<String>(json, r'lightStyle')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
|
@ -99,8 +106,9 @@ class SystemConfigMapDto {
|
|||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'darkStyle',
|
||||
'enabled',
|
||||
'tileUrl',
|
||||
'lightStyle',
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue