mirror of
https://github.com/localsend/localsend.git
synced 2026-06-23 04:10:07 +00:00
refactor: migrate to dart_mappable
This commit is contained in:
+1
-1
@@ -7,7 +7,7 @@
|
||||
|
||||
*.g.dart
|
||||
*.gen.dart
|
||||
*.freezed.dart
|
||||
*.mapper.dart
|
||||
.refena_inspector/
|
||||
|
||||
/secrets
|
||||
|
||||
@@ -16,3 +16,10 @@ targets:
|
||||
- 'bn'
|
||||
description: |
|
||||
"LocalSend" is a file sharing app that allows you to send files to other devices on the same network.
|
||||
dart_mappable_builder:
|
||||
options:
|
||||
renameMethods:
|
||||
fromJson: deserialize
|
||||
toJson: serialize
|
||||
fromMap: fromJson
|
||||
toMap: toJson
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:localsend_app/constants.dart';
|
||||
import 'package:localsend_app/gen/strings.g.dart';
|
||||
import 'package:localsend_app/model/dto/file_dto.dart';
|
||||
import 'package:localsend_app/pages/home_page.dart';
|
||||
import 'package:localsend_app/provider/animation_provider.dart';
|
||||
import 'package:localsend_app/provider/dio_provider.dart';
|
||||
@@ -46,6 +48,8 @@ Future<(PersistenceService, bool)> preInit(List<String> args) async {
|
||||
}
|
||||
});
|
||||
|
||||
MapperContainer.globals.use(const FileDtoMapper());
|
||||
|
||||
final persistenceService = await PersistenceService.initialize();
|
||||
|
||||
// Register default plural resolver
|
||||
|
||||
@@ -1,22 +1,30 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:localsend_app/model/file_type.dart';
|
||||
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
||||
|
||||
part 'cross_file.freezed.dart';
|
||||
part 'cross_file.mapper.dart';
|
||||
|
||||
/// Common file model to avoid any third party libraries in the core logic.
|
||||
/// This model is used during the file selection phase.
|
||||
@freezed
|
||||
class CrossFile with _$CrossFile {
|
||||
const factory CrossFile({
|
||||
required String name,
|
||||
required FileType fileType,
|
||||
required int size,
|
||||
required Uint8List? thumbnail,
|
||||
required AssetEntity? asset, // for thumbnails
|
||||
required String? path,
|
||||
required List<int>? bytes, // if type message, then UTF-8 encoded
|
||||
}) = _CrossFile;
|
||||
@MappableClass()
|
||||
class CrossFile with CrossFileMappable {
|
||||
final String name;
|
||||
final FileType fileType;
|
||||
final int size;
|
||||
final Uint8List? thumbnail;
|
||||
final AssetEntity? asset; // for thumbnails
|
||||
final String? path;
|
||||
final List<int>? bytes; // if type message, then UTF-8 encoded
|
||||
|
||||
const CrossFile({
|
||||
required this.name,
|
||||
required this.fileType,
|
||||
required this.size,
|
||||
required this.thumbnail,
|
||||
required this.asset,
|
||||
required this.path,
|
||||
required this.bytes,
|
||||
});
|
||||
}
|
||||
|
||||
+26
-15
@@ -1,8 +1,9 @@
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'device.freezed.dart';
|
||||
part 'device.mapper.dart';
|
||||
|
||||
@MappableEnum(defaultValue: DeviceType.desktop)
|
||||
enum DeviceType {
|
||||
mobile(Icons.smartphone),
|
||||
desktop(Icons.computer),
|
||||
@@ -17,17 +18,27 @@ enum DeviceType {
|
||||
|
||||
/// Internal device model.
|
||||
/// It gets not serialized.
|
||||
@freezed
|
||||
class Device with _$Device {
|
||||
const factory Device({
|
||||
required String ip,
|
||||
required String version,
|
||||
required int port,
|
||||
required bool https,
|
||||
required String fingerprint,
|
||||
required String alias,
|
||||
required String? deviceModel,
|
||||
required DeviceType deviceType,
|
||||
required bool download,
|
||||
}) = _Device;
|
||||
@MappableClass()
|
||||
class Device with DeviceMappable {
|
||||
final String ip;
|
||||
final String version;
|
||||
final int port;
|
||||
final bool https;
|
||||
final String fingerprint;
|
||||
final String alias;
|
||||
final String? deviceModel;
|
||||
final DeviceType deviceType;
|
||||
final bool download;
|
||||
|
||||
const Device({
|
||||
required this.ip,
|
||||
required this.version,
|
||||
required this.port,
|
||||
required this.https,
|
||||
required this.fingerprint,
|
||||
required this.alias,
|
||||
required this.deviceModel,
|
||||
required this.deviceType,
|
||||
required this.download,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:localsend_app/model/file_type.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
|
||||
/// The file DTO that is sent between server and client.
|
||||
/// Custom implementation of freezed & json_serializable to handle legacy enums.
|
||||
/// The copyWith method is not implemented.
|
||||
class FileDto {
|
||||
final String id; // unique inside session
|
||||
final String fileName;
|
||||
@@ -29,10 +28,6 @@ class FileDto {
|
||||
|
||||
String lookupMime() => lookupMimeType(fileName) ?? 'application/octet-stream';
|
||||
|
||||
factory FileDto.fromJson(Map<String, Object?> json) => _parseFileDto(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _fileDtoToJson(this);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
@@ -50,49 +45,54 @@ class FileDto {
|
||||
int get hashCode => Object.hash(id, fileName, size, fileType, hash, preview, legacy);
|
||||
}
|
||||
|
||||
/// This deserializer handles both legacy and mime types.
|
||||
FileDto _parseFileDto(Map<String, Object?> json) {
|
||||
final String rawFileType = json['fileType'] as String;
|
||||
final FileType fileType;
|
||||
if (rawFileType.contains('/')) {
|
||||
// parse mime
|
||||
if (rawFileType.startsWith('image/')) {
|
||||
fileType = FileType.image;
|
||||
} else if (rawFileType.startsWith('video/')) {
|
||||
fileType = FileType.video;
|
||||
} else if (rawFileType == 'application/pdf') {
|
||||
fileType = FileType.pdf;
|
||||
} else if (rawFileType.startsWith('text/')) {
|
||||
fileType = FileType.text;
|
||||
} else if (rawFileType == 'application/vnd.android.package-archive') {
|
||||
fileType = FileType.apk;
|
||||
class FileDtoMapper extends SimpleMapper<FileDto> {
|
||||
const FileDtoMapper();
|
||||
|
||||
@override
|
||||
FileDto decode(dynamic value) {
|
||||
final map = value as Map<String, dynamic>;
|
||||
final String rawFileType = map['fileType'] as String;
|
||||
final FileType fileType;
|
||||
if (rawFileType.contains('/')) {
|
||||
// parse mime
|
||||
if (rawFileType.startsWith('image/')) {
|
||||
fileType = FileType.image;
|
||||
} else if (rawFileType.startsWith('video/')) {
|
||||
fileType = FileType.video;
|
||||
} else if (rawFileType == 'application/pdf') {
|
||||
fileType = FileType.pdf;
|
||||
} else if (rawFileType.startsWith('text/')) {
|
||||
fileType = FileType.text;
|
||||
} else if (rawFileType == 'application/vnd.android.package-archive') {
|
||||
fileType = FileType.apk;
|
||||
} else {
|
||||
fileType = FileType.other;
|
||||
}
|
||||
} else {
|
||||
fileType = FileType.other;
|
||||
// parse legacy enum to internal internal enum
|
||||
fileType = FileType.values.firstWhereOrNull((e) => e.name == rawFileType) ?? FileType.other;
|
||||
}
|
||||
} else {
|
||||
// parse legacy enum to internal internal enum
|
||||
fileType = FileType.values.firstWhereOrNull((e) => e.name == rawFileType) ?? FileType.other;
|
||||
|
||||
return FileDto(
|
||||
id: map['id'] as String,
|
||||
fileName: map['fileName'] as String,
|
||||
size: map['size'] as int,
|
||||
fileType: fileType,
|
||||
hash: map['hash'] as String?,
|
||||
preview: map['preview'] as String?,
|
||||
legacy: false,
|
||||
);
|
||||
}
|
||||
|
||||
return FileDto(
|
||||
id: json['id'] as String,
|
||||
fileName: json['fileName'] as String,
|
||||
size: json['size'] as int,
|
||||
fileType: fileType,
|
||||
hash: json['hash'] as String?,
|
||||
preview: json['preview'] as String?,
|
||||
legacy: false,
|
||||
);
|
||||
}
|
||||
|
||||
/// This serializer checks the legacy flag and serializes the file type accordingly.
|
||||
Map<String, dynamic> _fileDtoToJson(FileDto instance) {
|
||||
return {
|
||||
'id': instance.id,
|
||||
'fileName': instance.fileName,
|
||||
'size': instance.size,
|
||||
'fileType': instance.legacy ? instance.fileType.name : instance.lookupMime(),
|
||||
if (instance.hash != null) 'hash': instance.hash,
|
||||
if (instance.preview != null) 'preview': instance.preview,
|
||||
};
|
||||
@override
|
||||
dynamic encode(FileDto self) {
|
||||
return {
|
||||
'id': self.id,
|
||||
'fileName': self.fileName,
|
||||
'size': self.size,
|
||||
'fileType': self.legacy ? self.fileType.name : self.lookupMime(),
|
||||
if (self.hash != null) 'hash': self.hash,
|
||||
if (self.preview != null) 'preview': self.preview,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:localsend_app/constants.dart';
|
||||
import 'package:localsend_app/model/device.dart';
|
||||
|
||||
part 'info_dto.freezed.dart';
|
||||
part 'info_dto.g.dart';
|
||||
part 'info_dto.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class InfoDto with _$InfoDto {
|
||||
const factory InfoDto({
|
||||
required String alias,
|
||||
required String? version, // v2, format: major.minor
|
||||
required String? deviceModel,
|
||||
@JsonKey(unknownEnumValue: DeviceType.desktop) // ignore: invalid_annotation_target
|
||||
required DeviceType? deviceType,
|
||||
required String? fingerprint, // v2
|
||||
required bool? download, // v2
|
||||
}) = _InfoDto;
|
||||
@MappableClass()
|
||||
class InfoDto with InfoDtoMappable {
|
||||
final String alias;
|
||||
final String? version; // v2, format: major.minor
|
||||
final String? deviceModel;
|
||||
final DeviceType? deviceType;
|
||||
final String? fingerprint; // v2
|
||||
final bool? download; // v2
|
||||
|
||||
factory InfoDto.fromJson(Map<String, Object?> json) => _$InfoDtoFromJson(json);
|
||||
const InfoDto({
|
||||
required this.alias,
|
||||
required this.version,
|
||||
required this.deviceModel,
|
||||
required this.deviceType,
|
||||
required this.fingerprint,
|
||||
required this.download,
|
||||
});
|
||||
|
||||
static const fromJson = InfoDtoMapper.fromJson;
|
||||
}
|
||||
|
||||
extension InfoToDeviceExt on InfoDto {
|
||||
|
||||
@@ -1,29 +1,36 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:localsend_app/constants.dart';
|
||||
import 'package:localsend_app/model/device.dart';
|
||||
import 'package:localsend_app/model/dto/multicast_dto.dart';
|
||||
|
||||
part 'info_register_dto.freezed.dart';
|
||||
part 'info_register_dto.g.dart';
|
||||
part 'info_register_dto.mapper.dart';
|
||||
|
||||
/// Used only for /prepare-upload to be compatible with v1.
|
||||
/// The [fingerprint] does not exist in v1, so it is nullable here.
|
||||
/// TODO: replace with [RegisterDto] when v1 compatibility is removed
|
||||
@freezed
|
||||
class InfoRegisterDto with _$InfoRegisterDto {
|
||||
const factory InfoRegisterDto({
|
||||
required String alias,
|
||||
required String? version, // v2, format: major.minor
|
||||
required String? deviceModel,
|
||||
@JsonKey(unknownEnumValue: DeviceType.desktop) // ignore: invalid_annotation_target
|
||||
required DeviceType? deviceType,
|
||||
required String? fingerprint,
|
||||
required int? port, // v2
|
||||
required ProtocolType? protocol, // v2
|
||||
required bool? download, // v2
|
||||
}) = _InfoRegisterDto;
|
||||
@MappableClass()
|
||||
class InfoRegisterDto with InfoRegisterDtoMappable {
|
||||
final String alias;
|
||||
final String? version; // v2, format: major.minor
|
||||
final String? deviceModel;
|
||||
final DeviceType? deviceType;
|
||||
final String? fingerprint;
|
||||
final int? port; // v2
|
||||
final ProtocolType? protocol; // v2
|
||||
final bool? download; // v2
|
||||
|
||||
factory InfoRegisterDto.fromJson(Map<String, Object?> json) => _$InfoRegisterDtoFromJson(json);
|
||||
const InfoRegisterDto({
|
||||
required this.alias,
|
||||
required this.version,
|
||||
required this.deviceModel,
|
||||
required this.deviceType,
|
||||
required this.fingerprint,
|
||||
required this.port,
|
||||
required this.protocol,
|
||||
required this.download,
|
||||
});
|
||||
|
||||
static const fromJson = InfoRegisterDtoMapper.fromJson;
|
||||
}
|
||||
|
||||
extension RegisterDtoExt on InfoRegisterDto {
|
||||
|
||||
@@ -1,28 +1,39 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:localsend_app/constants.dart';
|
||||
import 'package:localsend_app/model/device.dart';
|
||||
|
||||
part 'multicast_dto.freezed.dart';
|
||||
part 'multicast_dto.g.dart';
|
||||
part 'multicast_dto.mapper.dart';
|
||||
|
||||
@MappableEnum(defaultValue: ProtocolType.https)
|
||||
enum ProtocolType { http, https }
|
||||
|
||||
@freezed
|
||||
class MulticastDto with _$MulticastDto {
|
||||
const factory MulticastDto({
|
||||
required String alias,
|
||||
required String? version, // v2, format: major.minor
|
||||
required String? deviceModel,
|
||||
required DeviceType? deviceType, // nullable since v2
|
||||
required String fingerprint,
|
||||
required int? port, // v2
|
||||
required ProtocolType? protocol, // v2
|
||||
required bool? download, // v2
|
||||
required bool? announcement, // v1
|
||||
required bool? announce, // v2
|
||||
}) = _MulticastDto;
|
||||
@MappableClass()
|
||||
class MulticastDto with MulticastDtoMappable {
|
||||
final String alias;
|
||||
final String? version; // v2, format: major.minor
|
||||
final String? deviceModel;
|
||||
final DeviceType? deviceType; // nullable since v2
|
||||
final String fingerprint;
|
||||
final int? port; // v2
|
||||
final ProtocolType? protocol; // v2
|
||||
final bool? download; // v2
|
||||
final bool? announcement; // v1
|
||||
final bool? announce; // v2
|
||||
|
||||
factory MulticastDto.fromJson(Map<String, Object?> json) => _$MulticastDtoFromJson(json);
|
||||
const MulticastDto({
|
||||
required this.alias,
|
||||
required this.version,
|
||||
required this.deviceModel,
|
||||
required this.deviceType,
|
||||
required this.fingerprint,
|
||||
required this.port,
|
||||
required this.protocol,
|
||||
required this.download,
|
||||
required this.announcement,
|
||||
required this.announce,
|
||||
});
|
||||
|
||||
static const fromJson = MulticastDtoMapper.fromJson;
|
||||
}
|
||||
|
||||
extension InfoToDeviceExt on MulticastDto {
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:localsend_app/model/dto/file_dto.dart';
|
||||
import 'package:localsend_app/model/dto/info_register_dto.dart';
|
||||
|
||||
part 'prepare_upload_request_dto.freezed.dart';
|
||||
part 'prepare_upload_request_dto.g.dart';
|
||||
part 'prepare_upload_request_dto.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class PrepareUploadRequestDto with _$PrepareUploadRequestDto {
|
||||
const factory PrepareUploadRequestDto({
|
||||
required InfoRegisterDto info,
|
||||
required Map<String, FileDto> files,
|
||||
}) = _PrepareUploadRequestDto;
|
||||
@MappableClass()
|
||||
class PrepareUploadRequestDto with PrepareUploadRequestDtoMappable {
|
||||
final InfoRegisterDto info;
|
||||
final Map<String, FileDto> files;
|
||||
|
||||
factory PrepareUploadRequestDto.fromJson(Map<String, Object?> json) => _$PrepareUploadRequestDtoFromJson(json);
|
||||
const PrepareUploadRequestDto({
|
||||
required this.info,
|
||||
required this.files,
|
||||
});
|
||||
|
||||
static const fromJson = PrepareUploadRequestDtoMapper.fromJson;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
part 'prepare_upload_response_dto.freezed.dart';
|
||||
part 'prepare_upload_response_dto.g.dart';
|
||||
part 'prepare_upload_response_dto.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class PrepareUploadResponseDto with _$PrepareUploadResponseDto {
|
||||
const factory PrepareUploadResponseDto({
|
||||
required String sessionId,
|
||||
required Map<String, String> files,
|
||||
}) = _PrepareUploadResponseDto;
|
||||
@MappableClass()
|
||||
class PrepareUploadResponseDto with PrepareUploadResponseDtoMappable {
|
||||
final String sessionId;
|
||||
final Map<String, String> files;
|
||||
|
||||
factory PrepareUploadResponseDto.fromJson(Map<String, Object?> json) => _$PrepareUploadResponseDtoFromJson(json);
|
||||
const PrepareUploadResponseDto({
|
||||
required this.sessionId,
|
||||
required this.files,
|
||||
});
|
||||
|
||||
static const fromJson = PrepareUploadResponseDtoMapper.fromJson;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:localsend_app/model/dto/file_dto.dart';
|
||||
import 'package:localsend_app/model/dto/info_dto.dart';
|
||||
|
||||
part 'receive_request_response_dto.freezed.dart';
|
||||
part 'receive_request_response_dto.g.dart';
|
||||
part 'receive_request_response_dto.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class ReceiveRequestResponseDto with _$ReceiveRequestResponseDto {
|
||||
const factory ReceiveRequestResponseDto({
|
||||
required InfoDto info,
|
||||
required String sessionId,
|
||||
required Map<String, FileDto> files,
|
||||
}) = _ReceiveRequestResponseDto;
|
||||
@MappableClass()
|
||||
class ReceiveRequestResponseDto with ReceiveRequestResponseDtoMappable {
|
||||
final InfoDto info;
|
||||
final String sessionId;
|
||||
final Map<String, FileDto> files;
|
||||
|
||||
factory ReceiveRequestResponseDto.fromJson(Map<String, Object?> json) => _$ReceiveRequestResponseDtoFromJson(json);
|
||||
const ReceiveRequestResponseDto({
|
||||
required this.info,
|
||||
required this.sessionId,
|
||||
required this.files,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:localsend_app/constants.dart';
|
||||
import 'package:localsend_app/model/device.dart';
|
||||
import 'package:localsend_app/model/dto/multicast_dto.dart';
|
||||
|
||||
part 'register_dto.freezed.dart';
|
||||
part 'register_dto.g.dart';
|
||||
part 'register_dto.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class RegisterDto with _$RegisterDto {
|
||||
const factory RegisterDto({
|
||||
required String alias,
|
||||
required String? version, // v2, format: major.minor
|
||||
required String? deviceModel,
|
||||
@JsonKey(unknownEnumValue: DeviceType.desktop) // ignore: invalid_annotation_target
|
||||
required DeviceType? deviceType,
|
||||
required String fingerprint,
|
||||
required int? port, // v2
|
||||
required ProtocolType? protocol, // v2
|
||||
required bool? download, // v2
|
||||
}) = _RegisterDto;
|
||||
@MappableClass()
|
||||
class RegisterDto with RegisterDtoMappable {
|
||||
final String alias;
|
||||
final String? version; // v2, format: major.minor
|
||||
final String? deviceModel;
|
||||
final DeviceType? deviceType;
|
||||
final String fingerprint;
|
||||
final int? port; // v2
|
||||
final ProtocolType? protocol; // v2
|
||||
final bool? download; // v2
|
||||
|
||||
factory RegisterDto.fromJson(Map<String, Object?> json) => _$RegisterDtoFromJson(json);
|
||||
const RegisterDto({
|
||||
required this.alias,
|
||||
required this.version,
|
||||
required this.deviceModel,
|
||||
required this.deviceType,
|
||||
required this.fingerprint,
|
||||
required this.port,
|
||||
required this.protocol,
|
||||
required this.download,
|
||||
});
|
||||
|
||||
static const fromJson = RegisterDtoMapper.fromJson;
|
||||
}
|
||||
|
||||
extension RegisterDtoExt on RegisterDto {
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
part 'file_type.mapper.dart';
|
||||
|
||||
/// Categorization of one file.
|
||||
/// We use this information for a better UX.
|
||||
@MappableEnum(defaultValue: FileType.other)
|
||||
enum FileType {
|
||||
image(Icons.image),
|
||||
video(Icons.movie),
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
part 'log_entry.freezed.dart';
|
||||
part 'log_entry.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class LogEntry with _$LogEntry {
|
||||
const factory LogEntry({
|
||||
required DateTime timestamp,
|
||||
required String log,
|
||||
}) = _LogEntry;
|
||||
@MappableClass()
|
||||
class LogEntry with LogEntryMappable {
|
||||
final DateTime timestamp;
|
||||
final String log;
|
||||
|
||||
const LogEntry({
|
||||
required this.timestamp,
|
||||
required this.log,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
part 'stored_security_context.freezed.dart';
|
||||
part 'stored_security_context.g.dart';
|
||||
part 'stored_security_context.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class StoredSecurityContext with _$StoredSecurityContext {
|
||||
const factory StoredSecurityContext({
|
||||
required String privateKey,
|
||||
required String publicKey,
|
||||
required String certificate,
|
||||
required String certificateHash,
|
||||
}) = _StoredSecurityContext;
|
||||
@MappableClass()
|
||||
class StoredSecurityContext with StoredSecurityContextMappable {
|
||||
final String privateKey;
|
||||
final String publicKey;
|
||||
final String certificate;
|
||||
final String certificateHash;
|
||||
|
||||
factory StoredSecurityContext.fromJson(Map<String, Object?> json) => _$StoredSecurityContextFromJson(json);
|
||||
const StoredSecurityContext({
|
||||
required this.privateKey,
|
||||
required this.publicKey,
|
||||
required this.certificate,
|
||||
required this.certificateHash,
|
||||
});
|
||||
|
||||
static const fromJson = StoredSecurityContextMapper.fromJson;
|
||||
}
|
||||
|
||||
@@ -1,27 +1,31 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:localsend_app/gen/strings.g.dart';
|
||||
import 'package:localsend_app/model/file_type.dart';
|
||||
|
||||
part 'receive_history_entry.freezed.dart';
|
||||
part 'receive_history_entry.g.dart';
|
||||
part 'receive_history_entry.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class ReceiveHistoryEntry with _$ReceiveHistoryEntry {
|
||||
const ReceiveHistoryEntry._(); // allow custom getters
|
||||
@MappableClass()
|
||||
class ReceiveHistoryEntry with ReceiveHistoryEntryMappable {
|
||||
final String id;
|
||||
final String fileName;
|
||||
final FileType fileType;
|
||||
final String? path;
|
||||
final bool savedToGallery;
|
||||
final int fileSize;
|
||||
final String senderAlias;
|
||||
final DateTime timestamp;
|
||||
|
||||
const factory ReceiveHistoryEntry({
|
||||
required String id,
|
||||
required String fileName,
|
||||
required FileType fileType,
|
||||
required String? path,
|
||||
required bool savedToGallery,
|
||||
required int fileSize,
|
||||
required String senderAlias,
|
||||
required DateTime timestamp,
|
||||
}) = _ReceiveHistoryEntry;
|
||||
|
||||
factory ReceiveHistoryEntry.fromJson(Map<String, Object?> json) => _$ReceiveHistoryEntryFromJson(json);
|
||||
const ReceiveHistoryEntry({
|
||||
required this.id,
|
||||
required this.fileName,
|
||||
required this.fileType,
|
||||
required this.path,
|
||||
required this.savedToGallery,
|
||||
required this.fileSize,
|
||||
required this.senderAlias,
|
||||
required this.timestamp,
|
||||
});
|
||||
|
||||
/// Format string using the intl package.
|
||||
/// Because the raw timestamp is saved in UTC, we need to transform it to local time zone first.
|
||||
@@ -29,4 +33,6 @@ class ReceiveHistoryEntry with _$ReceiveHistoryEntry {
|
||||
final localTimestamp = timestamp.toLocal();
|
||||
return '${DateFormat.yMd(LocaleSettings.currentLocale.languageTag).format(localTimestamp)} ${DateFormat.jm(LocaleSettings.currentLocale.languageTag).format(localTimestamp)}';
|
||||
}
|
||||
|
||||
static const fromJson = ReceiveHistoryEntryMapper.fromJson;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:localsend_app/model/device.dart';
|
||||
|
||||
part 'nearby_devices_state.freezed.dart';
|
||||
part 'nearby_devices_state.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class NearbyDevicesState with _$NearbyDevicesState {
|
||||
const factory NearbyDevicesState({
|
||||
required Set<String> runningIps, // list of local ips
|
||||
required Map<String, Device> devices, // ip -> device
|
||||
}) = _NearbyDevicesState;
|
||||
@MappableClass()
|
||||
class NearbyDevicesState with NearbyDevicesStateMappable {
|
||||
final Set<String> runningIps; // list of local ips
|
||||
final Map<String, Device> devices; // ip -> device
|
||||
|
||||
const NearbyDevicesState({
|
||||
required this.runningIps,
|
||||
required this.devices,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
part 'network_state.freezed.dart';
|
||||
part 'network_state.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class NetworkState with _$NetworkState {
|
||||
const factory NetworkState({
|
||||
required List<String> localIps,
|
||||
required bool initialized,
|
||||
}) = _NetworkState;
|
||||
@MappableClass()
|
||||
class NetworkState with NetworkStateMappable {
|
||||
final List<String> localIps;
|
||||
final bool initialized;
|
||||
|
||||
const NetworkState({
|
||||
required this.localIps,
|
||||
required this.initialized,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,23 +1,34 @@
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:localsend_app/model/device.dart';
|
||||
import 'package:localsend_app/model/session_status.dart';
|
||||
import 'package:localsend_app/model/state/send/sending_file.dart';
|
||||
|
||||
part 'send_session_state.freezed.dart';
|
||||
part 'send_session_state.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class SendSessionState with _$SendSessionState {
|
||||
const factory SendSessionState({
|
||||
required String sessionId,
|
||||
required String? remoteSessionId, // v2
|
||||
required bool background,
|
||||
required SessionStatus status,
|
||||
required Device target,
|
||||
required Map<String, SendingFile> files, // file id as key
|
||||
required int? startTime,
|
||||
required int? endTime,
|
||||
required CancelToken? cancelToken,
|
||||
required String? errorMessage,
|
||||
}) = _SendSessionState;
|
||||
@MappableClass()
|
||||
class SendSessionState with SendSessionStateMappable {
|
||||
final String sessionId;
|
||||
final String? remoteSessionId; // v2
|
||||
final bool background;
|
||||
final SessionStatus status;
|
||||
final Device target;
|
||||
final Map<String, SendingFile> files; // file id as key
|
||||
final int? startTime;
|
||||
final int? endTime;
|
||||
final CancelToken? cancelToken;
|
||||
final String? errorMessage;
|
||||
|
||||
const SendSessionState({
|
||||
required this.sessionId,
|
||||
required this.remoteSessionId,
|
||||
required this.background,
|
||||
required this.status,
|
||||
required this.target,
|
||||
required this.files,
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
required this.cancelToken,
|
||||
required this.errorMessage,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:localsend_app/model/dto/file_dto.dart';
|
||||
import 'package:localsend_app/model/file_status.dart';
|
||||
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
||||
|
||||
part 'sending_file.freezed.dart';
|
||||
part 'sending_file.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class SendingFile with _$SendingFile {
|
||||
const factory SendingFile({
|
||||
required FileDto file,
|
||||
required FileStatus status,
|
||||
required String? token,
|
||||
required AssetEntity? asset, // for thumbnails
|
||||
required String? path, // android, iOS, desktop
|
||||
required List<int>? bytes, // web
|
||||
required String? errorMessage, // when status == failed
|
||||
}) = _SendingFile;
|
||||
@MappableClass()
|
||||
class SendingFile with SendingFileMappable {
|
||||
final FileDto file;
|
||||
final FileStatus status;
|
||||
final String? token;
|
||||
final AssetEntity? asset; // for thumbnails
|
||||
final String? path; // android, iOS, desktop
|
||||
final List<int>? bytes; // web
|
||||
final String? errorMessage; // when status == failed
|
||||
|
||||
const SendingFile({
|
||||
required this.file,
|
||||
required this.status,
|
||||
required this.token,
|
||||
required this.asset,
|
||||
required this.path,
|
||||
required this.bytes,
|
||||
required this.errorMessage,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:localsend_app/model/dto/file_dto.dart';
|
||||
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
||||
|
||||
part 'web_send_file.freezed.dart';
|
||||
part 'web_send_file.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class WebSendFile with _$WebSendFile {
|
||||
const factory WebSendFile({
|
||||
required FileDto file,
|
||||
required AssetEntity? asset, // for thumbnails
|
||||
required String? path, // android, iOS, desktop
|
||||
required List<int>? bytes, // web
|
||||
}) = _WebSendFile;
|
||||
@MappableClass()
|
||||
class WebSendFile with WebSendFileMappable {
|
||||
final FileDto file;
|
||||
final AssetEntity? asset; // for thumbnails
|
||||
final String? path; // android, iOS, desktop
|
||||
final List<int>? bytes; // web
|
||||
|
||||
const WebSendFile({
|
||||
required this.file,
|
||||
required this.asset,
|
||||
required this.path,
|
||||
required this.bytes,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
part 'web_send_session.freezed.dart';
|
||||
part 'web_send_session.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class WebSendSession with _$WebSendSession {
|
||||
const factory WebSendSession({
|
||||
required String sessionId,
|
||||
required StreamController<bool>? responseHandler, // used to accept or reject incoming requests
|
||||
required String ip,
|
||||
required String deviceInfo, // parsed from userAgent
|
||||
}) = _WebSendSession;
|
||||
@MappableClass()
|
||||
class WebSendSession with WebSendSessionMappable {
|
||||
final String sessionId;
|
||||
final StreamController<bool>? responseHandler; // used to accept or reject incoming requests
|
||||
final String ip;
|
||||
final String deviceInfo; // parsed from userAgent
|
||||
|
||||
const WebSendSession({
|
||||
required this.sessionId,
|
||||
required this.responseHandler,
|
||||
required this.ip,
|
||||
required this.deviceInfo,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:localsend_app/model/state/send/web/web_send_file.dart';
|
||||
import 'package:localsend_app/model/state/send/web/web_send_session.dart';
|
||||
|
||||
part 'web_send_state.freezed.dart';
|
||||
part 'web_send_state.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class WebSendState with _$WebSendState {
|
||||
const factory WebSendState({
|
||||
required Map<String, WebSendSession> sessions, // session id -> session data, also includes incoming requests
|
||||
required Map<String, WebSendFile> files, // file id as key
|
||||
}) = _WebSendState;
|
||||
@MappableClass()
|
||||
class WebSendState with WebSendStateMappable {
|
||||
final Map<String, WebSendSession> sessions; // session id -> session data, also includes incoming requests
|
||||
final Map<String, WebSendFile> files; // file id as key
|
||||
|
||||
const WebSendState({
|
||||
required this.sessions,
|
||||
required this.files,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,31 +1,36 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:localsend_app/model/device.dart';
|
||||
import 'package:localsend_app/model/file_type.dart';
|
||||
import 'package:localsend_app/model/session_status.dart';
|
||||
import 'package:localsend_app/model/state/server/receiving_file.dart';
|
||||
|
||||
part 'receive_session_state.freezed.dart';
|
||||
part 'receive_session_state.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class ReceiveSessionState with _$ReceiveSessionState {
|
||||
const ReceiveSessionState._(); // allow custom getters
|
||||
@MappableClass()
|
||||
class ReceiveSessionState with ReceiveSessionStateMappable {
|
||||
final String sessionId;
|
||||
final SessionStatus status;
|
||||
final Device sender;
|
||||
final Map<String, ReceivingFile> files; // file id as key
|
||||
final int? startTime;
|
||||
final int? endTime;
|
||||
final String destinationDirectory;
|
||||
final bool saveToGallery;
|
||||
final StreamController<Map<String, String>?>? responseHandler;
|
||||
|
||||
const factory ReceiveSessionState({
|
||||
required String sessionId,
|
||||
required SessionStatus status,
|
||||
required Device sender,
|
||||
required Map<String, ReceivingFile> files,
|
||||
required int? startTime,
|
||||
required int? endTime,
|
||||
required String destinationDirectory,
|
||||
required bool saveToGallery,
|
||||
|
||||
// use this to accept / decline the request, empty map == decline
|
||||
// FileId -> File Name
|
||||
required StreamController<Map<String, String>?>? responseHandler,
|
||||
}) = _ReceiveSessionState;
|
||||
const ReceiveSessionState({
|
||||
required this.sessionId,
|
||||
required this.status,
|
||||
required this.sender,
|
||||
required this.files,
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
required this.destinationDirectory,
|
||||
required this.saveToGallery,
|
||||
required this.responseHandler,
|
||||
});
|
||||
|
||||
/// Returns the message of this request if this is a "message request".
|
||||
/// Message requests must contain a single text file with preview included.
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:localsend_app/model/dto/file_dto.dart';
|
||||
import 'package:localsend_app/model/file_status.dart';
|
||||
|
||||
part 'receiving_file.freezed.dart';
|
||||
part 'receiving_file.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class ReceivingFile with _$ReceivingFile {
|
||||
const factory ReceivingFile({
|
||||
required FileDto file,
|
||||
required FileStatus status,
|
||||
required String? token,
|
||||
required String? desiredName, // not null when accepted
|
||||
required String? path, // when finished
|
||||
required bool savedToGallery, // when finished
|
||||
required String? errorMessage, // when status == failed
|
||||
}) = _ReceivingFile;
|
||||
@MappableClass()
|
||||
class ReceivingFile with ReceivingFileMappable {
|
||||
final FileDto file;
|
||||
final FileStatus status;
|
||||
final String? token;
|
||||
final String? desiredName; // not null when accepted
|
||||
final String? path; // when finished
|
||||
final bool savedToGallery; // when finished
|
||||
final String? errorMessage; // when status == failed
|
||||
|
||||
const ReceivingFile({
|
||||
required this.file,
|
||||
required this.status,
|
||||
required this.token,
|
||||
required this.desiredName,
|
||||
required this.path,
|
||||
required this.savedToGallery,
|
||||
required this.errorMessage,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:localsend_app/model/state/send/web/web_send_state.dart';
|
||||
import 'package:localsend_app/model/state/server/receive_session_state.dart';
|
||||
|
||||
part 'server_state.freezed.dart';
|
||||
part 'server_state.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class ServerState with _$ServerState {
|
||||
const factory ServerState({
|
||||
required HttpServer httpServer,
|
||||
required String alias,
|
||||
required int port,
|
||||
required bool https,
|
||||
required ReceiveSessionState? session,
|
||||
required WebSendState? webSendState,
|
||||
}) = _ServerState;
|
||||
@MappableClass()
|
||||
class ServerState with ServerStateMappable {
|
||||
final HttpServer httpServer;
|
||||
final String alias;
|
||||
final int port;
|
||||
final bool https;
|
||||
final ReceiveSessionState? session;
|
||||
final WebSendState? webSendState;
|
||||
|
||||
const ServerState({
|
||||
required this.httpServer,
|
||||
required this.alias,
|
||||
required this.port,
|
||||
required this.https,
|
||||
required this.session,
|
||||
required this.webSendState,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,34 +1,55 @@
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:localsend_app/gen/strings.g.dart';
|
||||
import 'package:localsend_app/model/device.dart';
|
||||
import 'package:localsend_app/model/persistence/color_mode.dart';
|
||||
import 'package:localsend_app/model/send_mode.dart';
|
||||
|
||||
part 'settings_state.freezed.dart';
|
||||
part 'settings_state.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class SettingsState with _$SettingsState {
|
||||
const factory SettingsState({
|
||||
required String showToken, // the token to show / maximize the window because only one instance is allowed
|
||||
required String alias,
|
||||
required ThemeMode theme,
|
||||
required ColorMode colorMode,
|
||||
required AppLocale? locale,
|
||||
required int port,
|
||||
required String multicastGroup,
|
||||
required String? destination, // null = default
|
||||
required bool saveToGallery, // only Android, iOS
|
||||
required bool saveToHistory,
|
||||
required bool quickSave, // automatically accept file requests
|
||||
required bool minimizeToTray, // minimize to tray instead of exiting the app
|
||||
required bool launchAtStartup, // Tracks if the option is enabled on Linux
|
||||
required bool autoStartLaunchMinimized, // start hidden in tray (only available when launchAtStartup is true)
|
||||
required bool https,
|
||||
required SendMode sendMode,
|
||||
required bool saveWindowPlacement,
|
||||
required bool enableAnimations,
|
||||
required DeviceType? deviceType,
|
||||
required String? deviceModel,
|
||||
}) = _SettingsState;
|
||||
@MappableClass()
|
||||
class SettingsState with SettingsStateMappable {
|
||||
final String showToken; // the token to show / maximize the window because only one instance is allowed
|
||||
final String alias;
|
||||
final ThemeMode theme;
|
||||
final ColorMode colorMode;
|
||||
final AppLocale? locale;
|
||||
final int port;
|
||||
final String multicastGroup;
|
||||
final String? destination; // null = default
|
||||
final bool saveToGallery; // only Android, iOS
|
||||
final bool saveToHistory;
|
||||
final bool quickSave; // automatically accept file requests
|
||||
final bool minimizeToTray; // minimize to tray instead of exiting the app
|
||||
final bool launchAtStartup; // Tracks if the option is enabled on Linux
|
||||
final bool autoStartLaunchMinimized; // start hidden in tray (only available when launchAtStartup is true)
|
||||
final bool https;
|
||||
final SendMode sendMode;
|
||||
final bool saveWindowPlacement;
|
||||
final bool enableAnimations;
|
||||
final DeviceType? deviceType;
|
||||
final String? deviceModel;
|
||||
|
||||
const SettingsState({
|
||||
required this.showToken,
|
||||
required this.alias,
|
||||
required this.theme,
|
||||
required this.colorMode,
|
||||
required this.locale,
|
||||
required this.port,
|
||||
required this.multicastGroup,
|
||||
required this.destination,
|
||||
required this.saveToGallery,
|
||||
required this.saveToHistory,
|
||||
required this.quickSave,
|
||||
required this.minimizeToTray,
|
||||
required this.launchAtStartup,
|
||||
required this.autoStartLaunchMinimized,
|
||||
required this.https,
|
||||
required this.sendMode,
|
||||
required this.saveWindowPlacement,
|
||||
required this.enableAnimations,
|
||||
required this.deviceType,
|
||||
required this.deviceModel,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
part 'apk_provider_param.freezed.dart';
|
||||
part 'apk_provider_param.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class ApkProviderParam with _$ApkProviderParam {
|
||||
const factory ApkProviderParam({
|
||||
required String query,
|
||||
required bool includeSystemApps,
|
||||
required bool onlyAppsWithLaunchIntent,
|
||||
}) = _ApkProviderParam;
|
||||
@MappableClass()
|
||||
class ApkProviderParam with ApkProviderParamMappable {
|
||||
final String query;
|
||||
final bool includeSystemApps;
|
||||
final bool onlyAppsWithLaunchIntent;
|
||||
|
||||
const ApkProviderParam({
|
||||
required this.query,
|
||||
required this.includeSystemApps,
|
||||
required this.onlyAppsWithLaunchIntent,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
part 'cached_apk_provider_param.freezed.dart';
|
||||
part 'cached_apk_provider_param.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class CachedApkProviderParam with _$CachedApkProviderParam {
|
||||
const factory CachedApkProviderParam({
|
||||
required bool includeSystemApps,
|
||||
required bool onlyAppsWithLaunchIntent,
|
||||
}) = _CachedApkProviderParam;
|
||||
@MappableClass()
|
||||
class CachedApkProviderParam with CachedApkProviderParamMappable {
|
||||
final bool includeSystemApps;
|
||||
final bool onlyAppsWithLaunchIntent;
|
||||
|
||||
const CachedApkProviderParam({
|
||||
required this.includeSystemApps,
|
||||
required this.onlyAppsWithLaunchIntent,
|
||||
});
|
||||
}
|
||||
|
||||
+35
-35
@@ -17,6 +17,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.13.0"
|
||||
ansicolor:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ansicolor
|
||||
sha256: "607f8fa9786f392043f169898923e6c59b4518242b68b8862eb8a8b7d9c30b4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -249,6 +257,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.2"
|
||||
dart_mappable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dart_mappable
|
||||
sha256: "708b01d81663f6dbfa14d6cd588ba7241ed24c9a346a40855148c3b82520c63c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.0"
|
||||
dart_mappable_builder:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: dart_mappable_builder
|
||||
sha256: ce10c4c19cb9071461703e6186bb50ff7ec806c99ef717cab9ed25099d09f8bd
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.0"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -501,22 +525,6 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
freezed:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: freezed
|
||||
sha256: "2df89855fe181baae3b6d714dc3c4317acf4fccd495a6f36e5e00f24144c6c3b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
freezed_annotation:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: freezed_annotation
|
||||
sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -710,21 +718,13 @@ packages:
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
json_annotation:
|
||||
dependency: "direct main"
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.1"
|
||||
json_serializable:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: json_serializable
|
||||
sha256: aa1f5a8912615733e0fdc7a02af03308933c93235bdc8d50d0b0c8a8ccb0b969
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.7.1"
|
||||
launch_at_startup:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1075,10 +1075,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: refena
|
||||
sha256: "50020d82c1b6f085b4af1ce8c81b20f02219e5efd67adc5567d63200290cfde8"
|
||||
sha256: e7f3cd6440342201274e325e94f4dd4dd68f92395132a0df5a006c58a2b55bdc
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.32.0"
|
||||
version: "0.32.1"
|
||||
refena_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1308,14 +1308,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.4"
|
||||
source_map_stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1445,6 +1437,14 @@ packages:
|
||||
url: "https://github.com/Tienisto/tray_manager.git"
|
||||
source: git
|
||||
version: "0.2.0"
|
||||
type_plus:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: type_plus
|
||||
sha256: "52af1140887d0ce0ea89c768dfde1b244cd531221c7f48c8c29b1d24ae8aed9a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
+2
-4
@@ -13,6 +13,7 @@ dependencies:
|
||||
basic_utils: 5.6.1
|
||||
collection: ^1.17.2 # allow newer versions, so it can compile with newer Flutter versions
|
||||
connectivity_plus: 4.0.2
|
||||
dart_mappable: 3.3.0
|
||||
desktop_drop: 0.4.3
|
||||
device_apps: 2.2.0
|
||||
device_info_plus: 9.0.3
|
||||
@@ -25,11 +26,9 @@ dependencies:
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
flutter_markdown: 0.6.17+1
|
||||
freezed_annotation: 2.4.1
|
||||
gal: 1.9.1
|
||||
image_picker: 1.0.4
|
||||
intl: ^0.18.0 # allow newer versions, so it can compile with newer Flutter versions
|
||||
json_annotation: 4.8.1
|
||||
launch_at_startup: 0.2.2
|
||||
logging: 1.2.0
|
||||
mime: 1.0.4
|
||||
@@ -68,10 +67,9 @@ dependencies:
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: 2.4.6
|
||||
dart_mappable_builder: 3.3.0
|
||||
flutter_gen_runner: 5.3.1
|
||||
flutter_lints: 2.0.2
|
||||
freezed: 2.4.1
|
||||
json_serializable: 6.7.1
|
||||
msix: 3.16.1
|
||||
refena_inspector: 0.3.0
|
||||
slang_build_runner: 3.23.0
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:localsend_app/model/device.dart';
|
||||
import 'package:localsend_app/model/dto/file_dto.dart';
|
||||
import 'package:localsend_app/model/dto/info_register_dto.dart';
|
||||
import 'package:localsend_app/model/dto/multicast_dto.dart';
|
||||
import 'package:localsend_app/model/dto/prepare_upload_request_dto.dart';
|
||||
import 'package:localsend_app/model/dto/prepare_upload_response_dto.dart';
|
||||
import 'package:localsend_app/model/file_type.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
MapperContainer.globals.use(const FileDtoMapper());
|
||||
|
||||
group('parse PrepareUploadRequestDto', () {
|
||||
test('should parse valid enums', () {
|
||||
final dto = {
|
||||
@@ -33,6 +35,17 @@ void main() {
|
||||
expect(parsed.files.values.first.fileType, FileType.image);
|
||||
});
|
||||
|
||||
test('Should fallback deviceType (simple)', () {
|
||||
final dto = {
|
||||
'alias': 'Nice Banana',
|
||||
'deviceModel': 'Samsung',
|
||||
'deviceType': 'invalidType',
|
||||
};
|
||||
|
||||
final parsed = InfoRegisterDto.fromJson(dto);
|
||||
expect(parsed.deviceType, DeviceType.desktop);
|
||||
});
|
||||
|
||||
test('should fallback deviceType', () {
|
||||
final dto = {
|
||||
'info': {
|
||||
@@ -161,7 +174,7 @@ void main() {
|
||||
),
|
||||
},
|
||||
);
|
||||
final serialized = _deepSerialize(dto);
|
||||
final serialized = dto.toJson();
|
||||
expect(serialized['info']['deviceType'], 'mobile');
|
||||
expect(serialized['files'].length, 2);
|
||||
expect(serialized['files']['some id']['fileType'], 'image');
|
||||
@@ -192,7 +205,7 @@ void main() {
|
||||
),
|
||||
},
|
||||
);
|
||||
final serialized = _deepSerialize(dto);
|
||||
final serialized = dto.toJson();
|
||||
|
||||
expect(serialized['info']['deviceType'], 'mobile');
|
||||
expect(serialized['files'].length, 2);
|
||||
@@ -200,10 +213,19 @@ void main() {
|
||||
expect(serialized['files']['some id 2']['fileType'], 'application/vnd.android.package-archive');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Deep serialize an object to a map.
|
||||
/// The toJson method only serializes the first level.
|
||||
Map<String, dynamic> _deepSerialize(Object object) {
|
||||
return jsonDecode(jsonEncode(object));
|
||||
test('PrepareUploadResponseDto', () {
|
||||
final parsed = PrepareUploadResponseDto.fromJson({
|
||||
'sessionId': 'some session id',
|
||||
'files': {
|
||||
'some id': 'some url',
|
||||
'some id 2': 'some url 2',
|
||||
},
|
||||
});
|
||||
|
||||
expect(parsed.sessionId, 'some session id');
|
||||
expect(parsed.files.length, 2);
|
||||
expect(parsed.files['some id'], 'some url');
|
||||
expect(parsed.files['some id 2'], 'some url 2');
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user