From 715ce1a368a1fbc0b6fbf3555d6d3229eeabfd6d Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Tue, 18 Nov 2025 21:45:13 +0800 Subject: [PATCH] :sparkles: File reference list --- lib/models/reference.dart | 23 +++ lib/models/reference.freezed.dart | 319 +++++++++++++++++++++++++++++ lib/models/reference.g.dart | 41 ++++ lib/pods/file_list.g.dart | 4 +- lib/pods/file_references.dart | 18 ++ lib/pods/file_references.g.dart | 153 ++++++++++++++ lib/screens/files/file_detail.dart | 74 +++++++ 7 files changed, 630 insertions(+), 2 deletions(-) create mode 100644 lib/models/reference.dart create mode 100644 lib/models/reference.freezed.dart create mode 100644 lib/models/reference.g.dart create mode 100644 lib/pods/file_references.dart create mode 100644 lib/pods/file_references.g.dart diff --git a/lib/models/reference.dart b/lib/models/reference.dart new file mode 100644 index 00000000..b7fa3868 --- /dev/null +++ b/lib/models/reference.dart @@ -0,0 +1,23 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:island/models/file.dart'; + +part 'reference.freezed.dart'; +part 'reference.g.dart'; + +@freezed +sealed class Reference with _$Reference { + const factory Reference({ + required String id, + @JsonKey(name: 'file_id') required String fileId, + SnCloudFile? file, + required String usage, + @JsonKey(name: 'resource_id') required String resourceId, + @JsonKey(name: 'expired_at') DateTime? expiredAt, + @JsonKey(name: 'created_at') required DateTime createdAt, + @JsonKey(name: 'updated_at') required DateTime updatedAt, + @JsonKey(name: 'deleted_at') DateTime? deletedAt, + }) = _Reference; + + factory Reference.fromJson(Map json) => + _$ReferenceFromJson(json); +} diff --git a/lib/models/reference.freezed.dart b/lib/models/reference.freezed.dart new file mode 100644 index 00000000..bfd3eb63 --- /dev/null +++ b/lib/models/reference.freezed.dart @@ -0,0 +1,319 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'reference.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$Reference { + + String get id;@JsonKey(name: 'file_id') String get fileId; SnCloudFile? get file; String get usage;@JsonKey(name: 'resource_id') String get resourceId;@JsonKey(name: 'expired_at') DateTime? get expiredAt;@JsonKey(name: 'created_at') DateTime get createdAt;@JsonKey(name: 'updated_at') DateTime get updatedAt;@JsonKey(name: 'deleted_at') DateTime? get deletedAt; +/// Create a copy of Reference +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ReferenceCopyWith get copyWith => _$ReferenceCopyWithImpl(this as Reference, _$identity); + + /// Serializes this Reference to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is Reference&&(identical(other.id, id) || other.id == id)&&(identical(other.fileId, fileId) || other.fileId == fileId)&&(identical(other.file, file) || other.file == file)&&(identical(other.usage, usage) || other.usage == usage)&&(identical(other.resourceId, resourceId) || other.resourceId == resourceId)&&(identical(other.expiredAt, expiredAt) || other.expiredAt == expiredAt)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,fileId,file,usage,resourceId,expiredAt,createdAt,updatedAt,deletedAt); + +@override +String toString() { + return 'Reference(id: $id, fileId: $fileId, file: $file, usage: $usage, resourceId: $resourceId, expiredAt: $expiredAt, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; +} + + +} + +/// @nodoc +abstract mixin class $ReferenceCopyWith<$Res> { + factory $ReferenceCopyWith(Reference value, $Res Function(Reference) _then) = _$ReferenceCopyWithImpl; +@useResult +$Res call({ + String id,@JsonKey(name: 'file_id') String fileId, SnCloudFile? file, String usage,@JsonKey(name: 'resource_id') String resourceId,@JsonKey(name: 'expired_at') DateTime? expiredAt,@JsonKey(name: 'created_at') DateTime createdAt,@JsonKey(name: 'updated_at') DateTime updatedAt,@JsonKey(name: 'deleted_at') DateTime? deletedAt +}); + + +$SnCloudFileCopyWith<$Res>? get file; + +} +/// @nodoc +class _$ReferenceCopyWithImpl<$Res> + implements $ReferenceCopyWith<$Res> { + _$ReferenceCopyWithImpl(this._self, this._then); + + final Reference _self; + final $Res Function(Reference) _then; + +/// Create a copy of Reference +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? fileId = null,Object? file = freezed,Object? usage = null,Object? resourceId = null,Object? expiredAt = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { + return _then(_self.copyWith( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,fileId: null == fileId ? _self.fileId : fileId // ignore: cast_nullable_to_non_nullable +as String,file: freezed == file ? _self.file : file // ignore: cast_nullable_to_non_nullable +as SnCloudFile?,usage: null == usage ? _self.usage : usage // ignore: cast_nullable_to_non_nullable +as String,resourceId: null == resourceId ? _self.resourceId : resourceId // ignore: cast_nullable_to_non_nullable +as String,expiredAt: freezed == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable +as DateTime?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable +as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable +as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable +as DateTime?, + )); +} +/// Create a copy of Reference +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$SnCloudFileCopyWith<$Res>? get file { + if (_self.file == null) { + return null; + } + + return $SnCloudFileCopyWith<$Res>(_self.file!, (value) { + return _then(_self.copyWith(file: value)); + }); +} +} + + +/// Adds pattern-matching-related methods to [Reference]. +extension ReferencePatterns on Reference { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _Reference value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _Reference() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _Reference value) $default,){ +final _that = this; +switch (_that) { +case _Reference(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _Reference value)? $default,){ +final _that = this; +switch (_that) { +case _Reference() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, @JsonKey(name: 'file_id') String fileId, SnCloudFile? file, String usage, @JsonKey(name: 'resource_id') String resourceId, @JsonKey(name: 'expired_at') DateTime? expiredAt, @JsonKey(name: 'created_at') DateTime createdAt, @JsonKey(name: 'updated_at') DateTime updatedAt, @JsonKey(name: 'deleted_at') DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _Reference() when $default != null: +return $default(_that.id,_that.fileId,_that.file,_that.usage,_that.resourceId,_that.expiredAt,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String id, @JsonKey(name: 'file_id') String fileId, SnCloudFile? file, String usage, @JsonKey(name: 'resource_id') String resourceId, @JsonKey(name: 'expired_at') DateTime? expiredAt, @JsonKey(name: 'created_at') DateTime createdAt, @JsonKey(name: 'updated_at') DateTime updatedAt, @JsonKey(name: 'deleted_at') DateTime? deletedAt) $default,) {final _that = this; +switch (_that) { +case _Reference(): +return $default(_that.id,_that.fileId,_that.file,_that.usage,_that.resourceId,_that.expiredAt,_that.createdAt,_that.updatedAt,_that.deletedAt);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, @JsonKey(name: 'file_id') String fileId, SnCloudFile? file, String usage, @JsonKey(name: 'resource_id') String resourceId, @JsonKey(name: 'expired_at') DateTime? expiredAt, @JsonKey(name: 'created_at') DateTime createdAt, @JsonKey(name: 'updated_at') DateTime updatedAt, @JsonKey(name: 'deleted_at') DateTime? deletedAt)? $default,) {final _that = this; +switch (_that) { +case _Reference() when $default != null: +return $default(_that.id,_that.fileId,_that.file,_that.usage,_that.resourceId,_that.expiredAt,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _Reference implements Reference { + const _Reference({required this.id, @JsonKey(name: 'file_id') required this.fileId, this.file, required this.usage, @JsonKey(name: 'resource_id') required this.resourceId, @JsonKey(name: 'expired_at') this.expiredAt, @JsonKey(name: 'created_at') required this.createdAt, @JsonKey(name: 'updated_at') required this.updatedAt, @JsonKey(name: 'deleted_at') this.deletedAt}); + factory _Reference.fromJson(Map json) => _$ReferenceFromJson(json); + +@override final String id; +@override@JsonKey(name: 'file_id') final String fileId; +@override final SnCloudFile? file; +@override final String usage; +@override@JsonKey(name: 'resource_id') final String resourceId; +@override@JsonKey(name: 'expired_at') final DateTime? expiredAt; +@override@JsonKey(name: 'created_at') final DateTime createdAt; +@override@JsonKey(name: 'updated_at') final DateTime updatedAt; +@override@JsonKey(name: 'deleted_at') final DateTime? deletedAt; + +/// Create a copy of Reference +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$ReferenceCopyWith<_Reference> get copyWith => __$ReferenceCopyWithImpl<_Reference>(this, _$identity); + +@override +Map toJson() { + return _$ReferenceToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Reference&&(identical(other.id, id) || other.id == id)&&(identical(other.fileId, fileId) || other.fileId == fileId)&&(identical(other.file, file) || other.file == file)&&(identical(other.usage, usage) || other.usage == usage)&&(identical(other.resourceId, resourceId) || other.resourceId == resourceId)&&(identical(other.expiredAt, expiredAt) || other.expiredAt == expiredAt)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,fileId,file,usage,resourceId,expiredAt,createdAt,updatedAt,deletedAt); + +@override +String toString() { + return 'Reference(id: $id, fileId: $fileId, file: $file, usage: $usage, resourceId: $resourceId, expiredAt: $expiredAt, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; +} + + +} + +/// @nodoc +abstract mixin class _$ReferenceCopyWith<$Res> implements $ReferenceCopyWith<$Res> { + factory _$ReferenceCopyWith(_Reference value, $Res Function(_Reference) _then) = __$ReferenceCopyWithImpl; +@override @useResult +$Res call({ + String id,@JsonKey(name: 'file_id') String fileId, SnCloudFile? file, String usage,@JsonKey(name: 'resource_id') String resourceId,@JsonKey(name: 'expired_at') DateTime? expiredAt,@JsonKey(name: 'created_at') DateTime createdAt,@JsonKey(name: 'updated_at') DateTime updatedAt,@JsonKey(name: 'deleted_at') DateTime? deletedAt +}); + + +@override $SnCloudFileCopyWith<$Res>? get file; + +} +/// @nodoc +class __$ReferenceCopyWithImpl<$Res> + implements _$ReferenceCopyWith<$Res> { + __$ReferenceCopyWithImpl(this._self, this._then); + + final _Reference _self; + final $Res Function(_Reference) _then; + +/// Create a copy of Reference +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? fileId = null,Object? file = freezed,Object? usage = null,Object? resourceId = null,Object? expiredAt = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { + return _then(_Reference( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,fileId: null == fileId ? _self.fileId : fileId // ignore: cast_nullable_to_non_nullable +as String,file: freezed == file ? _self.file : file // ignore: cast_nullable_to_non_nullable +as SnCloudFile?,usage: null == usage ? _self.usage : usage // ignore: cast_nullable_to_non_nullable +as String,resourceId: null == resourceId ? _self.resourceId : resourceId // ignore: cast_nullable_to_non_nullable +as String,expiredAt: freezed == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable +as DateTime?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable +as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable +as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable +as DateTime?, + )); +} + +/// Create a copy of Reference +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$SnCloudFileCopyWith<$Res>? get file { + if (_self.file == null) { + return null; + } + + return $SnCloudFileCopyWith<$Res>(_self.file!, (value) { + return _then(_self.copyWith(file: value)); + }); +} +} + +// dart format on diff --git a/lib/models/reference.g.dart b/lib/models/reference.g.dart new file mode 100644 index 00000000..96c5e2b6 --- /dev/null +++ b/lib/models/reference.g.dart @@ -0,0 +1,41 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'reference.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_Reference _$ReferenceFromJson(Map json) => _Reference( + id: json['id'] as String, + fileId: json['file_id'] as String, + file: + json['file'] == null + ? null + : SnCloudFile.fromJson(json['file'] as Map), + usage: json['usage'] as String, + resourceId: json['resource_id'] as String, + expiredAt: + json['expired_at'] == null + ? null + : DateTime.parse(json['expired_at'] as String), + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), + deletedAt: + json['deleted_at'] == null + ? null + : DateTime.parse(json['deleted_at'] as String), +); + +Map _$ReferenceToJson(_Reference instance) => + { + 'id': instance.id, + 'file_id': instance.fileId, + 'file': instance.file?.toJson(), + 'usage': instance.usage, + 'resource_id': instance.resourceId, + 'expired_at': instance.expiredAt?.toIso8601String(), + 'created_at': instance.createdAt.toIso8601String(), + 'updated_at': instance.updatedAt.toIso8601String(), + 'deleted_at': instance.deletedAt?.toIso8601String(), + }; diff --git a/lib/pods/file_list.g.dart b/lib/pods/file_list.g.dart index 1a69740c..e9263160 100644 --- a/lib/pods/file_list.g.dart +++ b/lib/pods/file_list.g.dart @@ -45,7 +45,7 @@ final billingQuotaProvider = // ignore: unused_element typedef BillingQuotaRef = AutoDisposeFutureProviderRef?>; String _$cloudFileListNotifierHash() => - r'5f2f80357cb31ac6473df5ac2101f9a462004f81'; + r'533dfa86f920b60cf7491fb4aeb95ece19e428af'; /// See also [CloudFileListNotifier]. @ProviderFor(CloudFileListNotifier) @@ -66,7 +66,7 @@ final cloudFileListNotifierProvider = AutoDisposeAsyncNotifierProvider< typedef _$CloudFileListNotifier = AutoDisposeAsyncNotifier>; String _$unindexedFileListNotifierHash() => - r'48fc92432a50a562190da5fe8ed0920d171b07b6'; + r'afa487d7b956b71b21ca1b073a01364a34ede1d5'; /// See also [UnindexedFileListNotifier]. @ProviderFor(UnindexedFileListNotifier) diff --git a/lib/pods/file_references.dart b/lib/pods/file_references.dart new file mode 100644 index 00000000..74fc887a --- /dev/null +++ b/lib/pods/file_references.dart @@ -0,0 +1,18 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:island/models/reference.dart'; +import 'package:island/pods/network.dart'; + +part 'file_references.g.dart'; + +@riverpod +Future> fileReferences( + FileReferencesRef ref, + String fileId, +) async { + final client = ref.read(apiClientProvider); + final response = await client.get('/drive/files/$fileId/references'); + final list = response.data as List; + return list + .map((json) => Reference.fromJson(json as Map)) + .toList(); +} diff --git a/lib/pods/file_references.g.dart b/lib/pods/file_references.g.dart new file mode 100644 index 00000000..ee3646ce --- /dev/null +++ b/lib/pods/file_references.g.dart @@ -0,0 +1,153 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'file_references.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$fileReferencesHash() => r'464562fbdc9452d8a5ffbd2d9d9343cdb43f1876'; + +/// Copied from Dart SDK +class _SystemHash { + _SystemHash._(); + + static int combine(int hash, int value) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + value); + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + // ignore: parameter_assignments + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +/// See also [fileReferences]. +@ProviderFor(fileReferences) +const fileReferencesProvider = FileReferencesFamily(); + +/// See also [fileReferences]. +class FileReferencesFamily extends Family>> { + /// See also [fileReferences]. + const FileReferencesFamily(); + + /// See also [fileReferences]. + FileReferencesProvider call(String fileId) { + return FileReferencesProvider(fileId); + } + + @override + FileReferencesProvider getProviderOverride( + covariant FileReferencesProvider provider, + ) { + return call(provider.fileId); + } + + static const Iterable? _dependencies = null; + + @override + Iterable? get dependencies => _dependencies; + + static const Iterable? _allTransitiveDependencies = null; + + @override + Iterable? get allTransitiveDependencies => + _allTransitiveDependencies; + + @override + String? get name => r'fileReferencesProvider'; +} + +/// See also [fileReferences]. +class FileReferencesProvider + extends AutoDisposeFutureProvider> { + /// See also [fileReferences]. + FileReferencesProvider(String fileId) + : this._internal( + (ref) => fileReferences(ref as FileReferencesRef, fileId), + from: fileReferencesProvider, + name: r'fileReferencesProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$fileReferencesHash, + dependencies: FileReferencesFamily._dependencies, + allTransitiveDependencies: + FileReferencesFamily._allTransitiveDependencies, + fileId: fileId, + ); + + FileReferencesProvider._internal( + super._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.debugGetCreateSourceHash, + required super.from, + required this.fileId, + }) : super.internal(); + + final String fileId; + + @override + Override overrideWith( + FutureOr> Function(FileReferencesRef provider) create, + ) { + return ProviderOverride( + origin: this, + override: FileReferencesProvider._internal( + (ref) => create(ref as FileReferencesRef), + from: from, + name: null, + dependencies: null, + allTransitiveDependencies: null, + debugGetCreateSourceHash: null, + fileId: fileId, + ), + ); + } + + @override + AutoDisposeFutureProviderElement> createElement() { + return _FileReferencesProviderElement(this); + } + + @override + bool operator ==(Object other) { + return other is FileReferencesProvider && other.fileId == fileId; + } + + @override + int get hashCode { + var hash = _SystemHash.combine(0, runtimeType.hashCode); + hash = _SystemHash.combine(hash, fileId.hashCode); + + return _SystemHash.finish(hash); + } +} + +@Deprecated('Will be removed in 3.0. Use Ref instead') +// ignore: unused_element +mixin FileReferencesRef on AutoDisposeFutureProviderRef> { + /// The parameter `fileId` of this provider. + String get fileId; +} + +class _FileReferencesProviderElement + extends AutoDisposeFutureProviderElement> + with FileReferencesRef { + _FileReferencesProviderElement(super.provider); + + @override + String get fileId => (origin as FileReferencesProvider).fileId; +} + +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/screens/files/file_detail.dart b/lib/screens/files/file_detail.dart index 69703c7a..93b8282c 100644 --- a/lib/screens/files/file_detail.dart +++ b/lib/screens/files/file_detail.dart @@ -6,19 +6,24 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gal/gal.dart'; import 'package:gap/gap.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/file.dart'; import 'package:island/pods/config.dart'; +import 'package:island/pods/file_references.dart'; import 'package:island/pods/network.dart'; import 'package:island/pods/upload_tasks.dart'; import 'package:island/models/drive_task.dart'; import 'package:island/services/responsive.dart'; +import 'package:island/services/time.dart'; import 'package:island/widgets/alert.dart'; import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/content/file_info_sheet.dart'; import 'package:island/widgets/content/file_viewer_contents.dart'; +import 'package:island/widgets/content/sheet.dart'; import 'package:path/path.dart' show extension; import 'package:path_provider/path_provider.dart'; +import 'package:styled_widget/styled_widget.dart'; class FileDetailScreen extends HookConsumerWidget { final SnCloudFile item; @@ -167,6 +172,24 @@ class FileDetailScreen extends HookConsumerWidget { break; } + // Add references button + actions.add( + IconButton( + icon: Icon(Icons.link), + onPressed: + () => showModalBottomSheet( + useRootNavigator: true, + context: context, + isScrollControlled: true, + builder: + (context) => SheetScaffold( + titleText: 'File References', + child: ReferencesList(fileId: item.id), + ), + ), + ), + ); + // Always add info button actions.add( IconButton(icon: Icon(Icons.info_outline), onPressed: showInfoSheet), @@ -275,3 +298,54 @@ class FileDetailScreen extends HookConsumerWidget { }; } } + +class ReferencesList extends ConsumerWidget { + const ReferencesList({super.key, required this.fileId}); + + final String fileId; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final asyncReferences = ref.watch(fileReferencesProvider(fileId)); + + return asyncReferences.when( + data: + (references) => ListView.builder( + itemCount: references.length, + itemBuilder: (context, index) { + final reference = references[index]; + return ListTile( + leading: const Icon(Icons.link), + title: Row( + spacing: 6, + children: [ + Text( + reference.usage, + style: GoogleFonts.robotoMono( + fontWeight: FontWeight.bold, + fontSize: 13, + ), + ), + Text( + reference.id, + style: GoogleFonts.robotoMono(fontSize: 13), + ), + ], + ), + subtitle: Row( + spacing: 8, + children: [ + Text(reference.createdAt.formatRelative(context)), + const VerticalDivider(width: 1, thickness: 1).height(12), + Text(reference.createdAt.formatSystem()), + ], + ), + ); + }, + ), + loading: () => const Center(child: CircularProgressIndicator()), + error: + (error, _) => Center(child: Text('Error loading references: $error')), + ); + } +}