mirror of
https://github.com/immich-app/immich.git
synced 2026-05-22 23:52:32 -04:00
fix(mobile): remove redundant iOS code
update code related to LocalAsset model and asset viewer
This commit is contained in:
@@ -61,7 +61,6 @@ import UIKit
|
||||
BackgroundWorkerFgHostApiSetup.setUp(binaryMessenger: engine.binaryMessenger, api: BackgroundWorkerApiImpl())
|
||||
ConnectivityApiSetup.setUp(binaryMessenger: engine.binaryMessenger, api: ConnectivityApiImpl())
|
||||
NetworkApiSetup.setUp(binaryMessenger: engine.binaryMessenger, api: NetworkApiImpl(viewController: controller))
|
||||
ViewIntentHostApiSetup.setUp(binaryMessenger: engine.binaryMessenger, api: ViewIntentApiImpl())
|
||||
}
|
||||
|
||||
public static func cancelPlugins(with engine: FlutterEngine) {
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
// Autogenerated from Pigeon (v26.0.2), do not edit directly.
|
||||
// See also: https://pub.dev/packages/pigeon
|
||||
|
||||
import Foundation
|
||||
|
||||
#if os(iOS)
|
||||
import Flutter
|
||||
#elseif os(macOS)
|
||||
import FlutterMacOS
|
||||
#else
|
||||
#error("Unsupported platform.")
|
||||
#endif
|
||||
|
||||
private func wrapResult(_ result: Any?) -> [Any?] {
|
||||
return [result]
|
||||
}
|
||||
|
||||
private func wrapError(_ error: Any) -> [Any?] {
|
||||
if let pigeonError = error as? PigeonError {
|
||||
return [
|
||||
pigeonError.code,
|
||||
pigeonError.message,
|
||||
pigeonError.details,
|
||||
]
|
||||
}
|
||||
if let flutterError = error as? FlutterError {
|
||||
return [
|
||||
flutterError.code,
|
||||
flutterError.message,
|
||||
flutterError.details,
|
||||
]
|
||||
}
|
||||
return [
|
||||
"\(error)",
|
||||
"\(type(of: error))",
|
||||
"Stacktrace: \(Thread.callStackSymbols)",
|
||||
]
|
||||
}
|
||||
|
||||
private func isNullish(_ value: Any?) -> Bool {
|
||||
return value is NSNull || value == nil
|
||||
}
|
||||
|
||||
private func nilOrValue<T>(_ value: Any?) -> T? {
|
||||
if value is NSNull { return nil }
|
||||
return value as! T?
|
||||
}
|
||||
|
||||
func deepEqualsViewIntent(_ lhs: Any?, _ rhs: Any?) -> Bool {
|
||||
let cleanLhs = nilOrValue(lhs) as Any?
|
||||
let cleanRhs = nilOrValue(rhs) as Any?
|
||||
switch (cleanLhs, cleanRhs) {
|
||||
case (nil, nil):
|
||||
return true
|
||||
|
||||
case (nil, _), (_, nil):
|
||||
return false
|
||||
|
||||
case is (Void, Void):
|
||||
return true
|
||||
|
||||
case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable):
|
||||
return cleanLhsHashable == cleanRhsHashable
|
||||
|
||||
case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]):
|
||||
guard cleanLhsArray.count == cleanRhsArray.count else { return false }
|
||||
for (index, element) in cleanLhsArray.enumerated() {
|
||||
if !deepEqualsViewIntent(element, cleanRhsArray[index]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]):
|
||||
guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false }
|
||||
for (key, cleanLhsValue) in cleanLhsDictionary {
|
||||
guard cleanRhsDictionary.index(forKey: key) != nil else { return false }
|
||||
if !deepEqualsViewIntent(cleanLhsValue, cleanRhsDictionary[key]!) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
default:
|
||||
// Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be untrue.
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func deepHashViewIntent(value: Any?, hasher: inout Hasher) {
|
||||
if let valueList = value as? [AnyHashable] {
|
||||
for item in valueList { deepHashViewIntent(value: item, hasher: &hasher) }
|
||||
return
|
||||
}
|
||||
|
||||
if let valueDict = value as? [AnyHashable: AnyHashable] {
|
||||
for key in valueDict.keys {
|
||||
hasher.combine(key)
|
||||
deepHashViewIntent(value: valueDict[key]!, hasher: &hasher)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if let hashableValue = value as? AnyHashable {
|
||||
hasher.combine(hashableValue.hashValue)
|
||||
}
|
||||
|
||||
return hasher.combine(String(describing: value))
|
||||
}
|
||||
|
||||
|
||||
|
||||
enum ViewIntentType: Int {
|
||||
case image = 0
|
||||
case video = 1
|
||||
}
|
||||
|
||||
/// Generated class from Pigeon that represents data sent in messages.
|
||||
struct ViewIntentPayload: Hashable {
|
||||
var path: String
|
||||
var type: ViewIntentType
|
||||
var mimeType: String
|
||||
var localAssetId: String? = nil
|
||||
|
||||
|
||||
// swift-format-ignore: AlwaysUseLowerCamelCase
|
||||
static func fromList(_ pigeonVar_list: [Any?]) -> ViewIntentPayload? {
|
||||
let path = pigeonVar_list[0] as! String
|
||||
let type = pigeonVar_list[1] as! ViewIntentType
|
||||
let mimeType = pigeonVar_list[2] as! String
|
||||
let localAssetId: String? = nilOrValue(pigeonVar_list[3])
|
||||
|
||||
return ViewIntentPayload(
|
||||
path: path,
|
||||
type: type,
|
||||
mimeType: mimeType,
|
||||
localAssetId: localAssetId
|
||||
)
|
||||
}
|
||||
func toList() -> [Any?] {
|
||||
return [
|
||||
path,
|
||||
type,
|
||||
mimeType,
|
||||
localAssetId,
|
||||
]
|
||||
}
|
||||
static func == (lhs: ViewIntentPayload, rhs: ViewIntentPayload) -> Bool {
|
||||
return deepEqualsViewIntent(lhs.toList(), rhs.toList()) }
|
||||
func hash(into hasher: inout Hasher) {
|
||||
deepHashViewIntent(value: toList(), hasher: &hasher)
|
||||
}
|
||||
}
|
||||
|
||||
private class ViewIntentPigeonCodecReader: FlutterStandardReader {
|
||||
override func readValue(ofType type: UInt8) -> Any? {
|
||||
switch type {
|
||||
case 129:
|
||||
let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?)
|
||||
if let enumResultAsInt = enumResultAsInt {
|
||||
return ViewIntentType(rawValue: enumResultAsInt)
|
||||
}
|
||||
return nil
|
||||
case 130:
|
||||
return ViewIntentPayload.fromList(self.readValue() as! [Any?])
|
||||
default:
|
||||
return super.readValue(ofType: type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ViewIntentPigeonCodecWriter: FlutterStandardWriter {
|
||||
override func writeValue(_ value: Any) {
|
||||
if let value = value as? ViewIntentType {
|
||||
super.writeByte(129)
|
||||
super.writeValue(value.rawValue)
|
||||
} else if let value = value as? ViewIntentPayload {
|
||||
super.writeByte(130)
|
||||
super.writeValue(value.toList())
|
||||
} else {
|
||||
super.writeValue(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ViewIntentPigeonCodecReaderWriter: FlutterStandardReaderWriter {
|
||||
override func reader(with data: Data) -> FlutterStandardReader {
|
||||
return ViewIntentPigeonCodecReader(data: data)
|
||||
}
|
||||
|
||||
override func writer(with data: NSMutableData) -> FlutterStandardWriter {
|
||||
return ViewIntentPigeonCodecWriter(data: data)
|
||||
}
|
||||
}
|
||||
|
||||
class ViewIntentPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable {
|
||||
static let shared = ViewIntentPigeonCodec(readerWriter: ViewIntentPigeonCodecReaderWriter())
|
||||
}
|
||||
|
||||
|
||||
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
|
||||
protocol ViewIntentHostApi {
|
||||
func consumeViewIntent(completion: @escaping (Result<ViewIntentPayload?, Error>) -> Void)
|
||||
}
|
||||
|
||||
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
|
||||
class ViewIntentHostApiSetup {
|
||||
static var codec: FlutterStandardMessageCodec { ViewIntentPigeonCodec.shared }
|
||||
/// Sets up an instance of `ViewIntentHostApi` to handle messages through the `binaryMessenger`.
|
||||
static func setUp(binaryMessenger: FlutterBinaryMessenger, api: ViewIntentHostApi?, messageChannelSuffix: String = "") {
|
||||
let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""
|
||||
let consumeViewIntentChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.ViewIntentHostApi.consumeViewIntent\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
consumeViewIntentChannel.setMessageHandler { _, reply in
|
||||
api.consumeViewIntent { result in
|
||||
switch result {
|
||||
case .success(let res):
|
||||
reply(wrapResult(res))
|
||||
case .failure(let error):
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
consumeViewIntentChannel.setMessageHandler(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
class ViewIntentApiImpl: ViewIntentHostApi {
|
||||
func consumeViewIntent(completion: @escaping (Result<ViewIntentPayload?, any Error>) -> Void) {
|
||||
completion(.success(nil))
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
||||
enum ViewIntentAttachmentType { image, video }
|
||||
@@ -7,9 +8,15 @@ enum ViewIntentAttachmentType { image, video }
|
||||
class ViewIntentAttachment {
|
||||
final String path;
|
||||
final ViewIntentAttachmentType type;
|
||||
final String mimeType;
|
||||
final String? localAssetId;
|
||||
|
||||
const ViewIntentAttachment({required this.path, required this.type, this.localAssetId});
|
||||
const ViewIntentAttachment({
|
||||
required this.path,
|
||||
required this.type,
|
||||
required this.mimeType,
|
||||
this.localAssetId,
|
||||
});
|
||||
|
||||
File get file => File(path);
|
||||
|
||||
@@ -18,4 +25,22 @@ class ViewIntentAttachment {
|
||||
bool get isImage => type == ViewIntentAttachmentType.image;
|
||||
|
||||
bool get isVideo => type == ViewIntentAttachmentType.video;
|
||||
|
||||
AssetPlaybackStyle get playbackStyle {
|
||||
if (isVideo) {
|
||||
return AssetPlaybackStyle.video;
|
||||
}
|
||||
|
||||
final normalizedMimeType = mimeType.toLowerCase();
|
||||
if (normalizedMimeType == 'image/gif' || normalizedMimeType == 'image/webp') {
|
||||
return AssetPlaybackStyle.imageAnimated;
|
||||
}
|
||||
|
||||
final normalizedPath = path.toLowerCase();
|
||||
if (normalizedPath.endsWith('.gif') || normalizedPath.endsWith('.webp')) {
|
||||
return AssetPlaybackStyle.imageAnimated;
|
||||
}
|
||||
|
||||
return AssetPlaybackStyle.image;
|
||||
}
|
||||
}
|
||||
|
||||
+36
-22
@@ -14,25 +14,33 @@ PlatformException _createConnectionError(String channelName) {
|
||||
message: 'Unable to establish connection on channel: "$channelName".',
|
||||
);
|
||||
}
|
||||
|
||||
bool _deepEquals(Object? a, Object? b) {
|
||||
if (a is List && b is List) {
|
||||
return a.length == b.length && a.indexed.every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]));
|
||||
return a.length == b.length &&
|
||||
a.indexed
|
||||
.every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]));
|
||||
}
|
||||
if (a is Map && b is Map) {
|
||||
return a.length == b.length &&
|
||||
a.entries.every(
|
||||
(MapEntry<Object?, Object?> entry) =>
|
||||
(b as Map<Object?, Object?>).containsKey(entry.key) && _deepEquals(entry.value, b[entry.key]),
|
||||
);
|
||||
return a.length == b.length && a.entries.every((MapEntry<Object?, Object?> entry) =>
|
||||
(b as Map<Object?, Object?>).containsKey(entry.key) &&
|
||||
_deepEquals(entry.value, b[entry.key]));
|
||||
}
|
||||
return a == b;
|
||||
}
|
||||
|
||||
enum ViewIntentType { image, video }
|
||||
|
||||
enum ViewIntentType {
|
||||
image,
|
||||
video,
|
||||
}
|
||||
|
||||
class ViewIntentPayload {
|
||||
ViewIntentPayload({required this.path, required this.type, required this.mimeType, this.localAssetId});
|
||||
ViewIntentPayload({
|
||||
required this.path,
|
||||
required this.type,
|
||||
required this.mimeType,
|
||||
this.localAssetId,
|
||||
});
|
||||
|
||||
String path;
|
||||
|
||||
@@ -43,12 +51,16 @@ class ViewIntentPayload {
|
||||
String? localAssetId;
|
||||
|
||||
List<Object?> _toList() {
|
||||
return <Object?>[path, type, mimeType, localAssetId];
|
||||
return <Object?>[
|
||||
path,
|
||||
type,
|
||||
mimeType,
|
||||
localAssetId,
|
||||
];
|
||||
}
|
||||
|
||||
Object encode() {
|
||||
return _toList();
|
||||
}
|
||||
return _toList(); }
|
||||
|
||||
static ViewIntentPayload decode(Object result) {
|
||||
result as List<Object?>;
|
||||
@@ -74,9 +86,11 @@ class ViewIntentPayload {
|
||||
|
||||
@override
|
||||
// ignore: avoid_equals_and_hash_code_on_mutable_classes
|
||||
int get hashCode => Object.hashAll(_toList());
|
||||
int get hashCode => Object.hashAll(_toList())
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
class _PigeonCodec extends StandardMessageCodec {
|
||||
const _PigeonCodec();
|
||||
@override
|
||||
@@ -84,10 +98,10 @@ class _PigeonCodec extends StandardMessageCodec {
|
||||
if (value is int) {
|
||||
buffer.putUint8(4);
|
||||
buffer.putInt64(value);
|
||||
} else if (value is ViewIntentType) {
|
||||
} else if (value is ViewIntentType) {
|
||||
buffer.putUint8(129);
|
||||
writeValue(buffer, value.index);
|
||||
} else if (value is ViewIntentPayload) {
|
||||
} else if (value is ViewIntentPayload) {
|
||||
buffer.putUint8(130);
|
||||
writeValue(buffer, value.encode());
|
||||
} else {
|
||||
@@ -98,10 +112,10 @@ class _PigeonCodec extends StandardMessageCodec {
|
||||
@override
|
||||
Object? readValueOfType(int type, ReadBuffer buffer) {
|
||||
switch (type) {
|
||||
case 129:
|
||||
case 129:
|
||||
final int? value = readValue(buffer) as int?;
|
||||
return value == null ? null : ViewIntentType.values[value];
|
||||
case 130:
|
||||
case 130:
|
||||
return ViewIntentPayload.decode(readValue(buffer)!);
|
||||
default:
|
||||
return super.readValueOfType(type, buffer);
|
||||
@@ -114,8 +128,8 @@ class ViewIntentHostApi {
|
||||
/// available for dependency injection. If it is left null, the default
|
||||
/// BinaryMessenger will be used which routes to the host platform.
|
||||
ViewIntentHostApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
|
||||
: pigeonVar_binaryMessenger = binaryMessenger,
|
||||
pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
|
||||
: pigeonVar_binaryMessenger = binaryMessenger,
|
||||
pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
|
||||
final BinaryMessenger? pigeonVar_binaryMessenger;
|
||||
|
||||
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
|
||||
@@ -123,15 +137,15 @@ class ViewIntentHostApi {
|
||||
final String pigeonVar_messageChannelSuffix;
|
||||
|
||||
Future<ViewIntentPayload?> consumeViewIntent() async {
|
||||
final String pigeonVar_channelName =
|
||||
'dev.flutter.pigeon.immich_mobile.ViewIntentHostApi.consumeViewIntent$pigeonVar_messageChannelSuffix';
|
||||
final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.ViewIntentHostApi.consumeViewIntent$pigeonVar_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
pigeonVar_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: pigeonVar_binaryMessenger,
|
||||
);
|
||||
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
|
||||
final List<Object?>? pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||
final List<Object?>? pigeonVar_replyList =
|
||||
await pigeonVar_sendFuture as List<Object?>?;
|
||||
if (pigeonVar_replyList == null) {
|
||||
throw _createConnectionError(pigeonVar_channelName);
|
||||
} else if (pigeonVar_replyList.length > 1) {
|
||||
|
||||
@@ -7,12 +7,9 @@ import 'package:immich_mobile/domain/services/timeline.service.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
|
||||
import 'package:immich_mobile/models/view_intent/view_intent_attachment.model.dart';
|
||||
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
|
||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
@@ -89,8 +86,8 @@ class ViewIntentHandler {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final fallbackAsset = _toViewIntentAsset(attachment);
|
||||
final checksum = localAssetId != null ? await _computeChecksum(localAssetId) : null;
|
||||
final fallbackAsset = _toViewIntentAsset(attachment).copyWith(checksum: checksum);
|
||||
_logger.fine('openAssetViewer for fallbackAsset');
|
||||
_openAssetViewer(fallbackAsset, _timelineFactory.fromAssets([fallbackAsset], TimelineOrigin.deepLink), 0);
|
||||
}
|
||||
@@ -135,13 +132,9 @@ class ViewIntentHandler {
|
||||
void _openAssetViewer(BaseAsset asset, TimelineService timelineService, int initialIndex) {
|
||||
_ref.read(assetViewerProvider.notifier).reset();
|
||||
_ref.read(assetViewerProvider.notifier).setAsset(asset);
|
||||
_ref.read(currentAssetNotifier.notifier).setAsset(asset);
|
||||
if (asset.isVideo || asset.isMotionPhoto) {
|
||||
_ref.read(videoPlaybackValueProvider.notifier).reset();
|
||||
_ref.read(videoPlayerControlsProvider.notifier).pause();
|
||||
}
|
||||
|
||||
if (asset.isVideo) {
|
||||
_ref.read(assetViewerProvider.notifier).setControls(false);
|
||||
_ref.read(assetViewerProvider.notifier).setControls(true);
|
||||
}
|
||||
|
||||
_router.push(AssetViewerRoute(initialIndex: initialIndex, timelineService: timelineService));
|
||||
@@ -163,13 +156,14 @@ class ViewIntentHandler {
|
||||
final now = DateTime.now();
|
||||
|
||||
return LocalAsset(
|
||||
id: attachment.path,
|
||||
id: attachment.localAssetId ?? '',
|
||||
name: attachment.fileName,
|
||||
checksum: null,
|
||||
type: attachment.isVideo ? AssetType.video : AssetType.image,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
isEdited: false,
|
||||
playbackStyle: attachment.playbackStyle,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ class ViewHandlerRepository {
|
||||
return ViewIntentAttachment(
|
||||
path: result.path,
|
||||
type: result.type == ViewIntentType.image ? ViewIntentAttachmentType.image : ViewIntentAttachmentType.video,
|
||||
mimeType: result.mimeType,
|
||||
localAssetId: result.localAssetId,
|
||||
);
|
||||
} catch (_) {
|
||||
|
||||
@@ -3,8 +3,6 @@ import 'package:pigeon/pigeon.dart';
|
||||
@ConfigurePigeon(
|
||||
PigeonOptions(
|
||||
dartOut: 'lib/platform/view_intent_api.g.dart',
|
||||
swiftOut: 'ios/Runner/ViewIntent/ViewIntent.g.swift',
|
||||
swiftOptions: SwiftOptions(includeErrorClass: false),
|
||||
kotlinOut: 'android/app/src/main/kotlin/app/alextran/immich/viewintent/ViewIntent.g.kt',
|
||||
kotlinOptions: KotlinOptions(package: 'app.alextran.immich.viewintent'),
|
||||
dartOptions: DartOptions(),
|
||||
|
||||
@@ -13,6 +13,7 @@ void main() {
|
||||
const attachment = ViewIntentAttachment(
|
||||
path: '/tmp/file.jpg',
|
||||
type: ViewIntentAttachmentType.image,
|
||||
mimeType: 'image/jpeg',
|
||||
localAssetId: '42',
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user