mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	118 - Implement shared album feature (#124)
* New features - Share album. Users can now create albums to share with existing people on the network. - Owner can delete the album. - Owner can invite the additional users to the album. - Shared users and the owner can add additional assets to the album. * In the asset viewer, the user can swipe up to see detailed information and swip down to dismiss. * Several UI enhancements.
This commit is contained in:
		@@ -1,6 +1,8 @@
 | 
			
		||||
import 'package:auto_route/auto_route.dart';
 | 
			
		||||
import 'package:cached_network_image/cached_network_image.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_hooks/flutter_hooks.dart';
 | 
			
		||||
import 'package:flutter_swipe_detector/flutter_swipe_detector.dart';
 | 
			
		||||
import 'package:hive/hive.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:immich_mobile/constants/hive_box.dart';
 | 
			
		||||
@@ -35,6 +37,18 @@ class ImageViewerPage extends HookConsumerWidget {
 | 
			
		||||
      assetDetail = await _assetService.getAssetById(asset.id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    showInfo() {
 | 
			
		||||
      showModalBottomSheet(
 | 
			
		||||
        backgroundColor: Colors.black,
 | 
			
		||||
        barrierColor: Colors.transparent,
 | 
			
		||||
        isScrollControlled: false,
 | 
			
		||||
        context: context,
 | 
			
		||||
        builder: (context) {
 | 
			
		||||
          return ExifBottomSheet(assetDetail: assetDetail!);
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    useEffect(() {
 | 
			
		||||
      getAssetExif();
 | 
			
		||||
      return null;
 | 
			
		||||
@@ -44,79 +58,77 @@ class ImageViewerPage extends HookConsumerWidget {
 | 
			
		||||
      backgroundColor: Colors.black,
 | 
			
		||||
      appBar: TopControlAppBar(
 | 
			
		||||
        asset: asset,
 | 
			
		||||
        onMoreInfoPressed: () {
 | 
			
		||||
          showModalBottomSheet(
 | 
			
		||||
            backgroundColor: Colors.black,
 | 
			
		||||
            barrierColor: Colors.transparent,
 | 
			
		||||
            isScrollControlled: false,
 | 
			
		||||
            context: context,
 | 
			
		||||
            builder: (context) {
 | 
			
		||||
              return ExifBottomSheet(assetDetail: assetDetail!);
 | 
			
		||||
            },
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
        onMoreInfoPressed: showInfo,
 | 
			
		||||
        onDownloadPressed: () {
 | 
			
		||||
          ref.watch(imageViewerStateProvider.notifier).downloadAsset(asset, context);
 | 
			
		||||
        },
 | 
			
		||||
      ),
 | 
			
		||||
      body: SafeArea(
 | 
			
		||||
        child: Stack(
 | 
			
		||||
          children: [
 | 
			
		||||
            Center(
 | 
			
		||||
              child: Hero(
 | 
			
		||||
                tag: heroTag,
 | 
			
		||||
                child: CachedNetworkImage(
 | 
			
		||||
                  fit: BoxFit.cover,
 | 
			
		||||
                  imageUrl: imageUrl,
 | 
			
		||||
                  httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
 | 
			
		||||
                  fadeInDuration: const Duration(milliseconds: 250),
 | 
			
		||||
                  errorWidget: (context, url, error) => ConstrainedBox(
 | 
			
		||||
                    constraints: const BoxConstraints(maxWidth: 300),
 | 
			
		||||
                    child: Wrap(
 | 
			
		||||
                      spacing: 32,
 | 
			
		||||
                      runSpacing: 32,
 | 
			
		||||
                      alignment: WrapAlignment.center,
 | 
			
		||||
                      children: [
 | 
			
		||||
                        const Text(
 | 
			
		||||
                          "Failed To Render Image - Possibly Corrupted Data",
 | 
			
		||||
                          textAlign: TextAlign.center,
 | 
			
		||||
                          style: TextStyle(fontSize: 16, color: Colors.white),
 | 
			
		||||
                        ),
 | 
			
		||||
                        SingleChildScrollView(
 | 
			
		||||
                          child: Text(
 | 
			
		||||
                            error.toString(),
 | 
			
		||||
      body: SwipeDetector(
 | 
			
		||||
        onSwipeDown: (_) {
 | 
			
		||||
          AutoRouter.of(context).pop();
 | 
			
		||||
        },
 | 
			
		||||
        onSwipeUp: (_) {
 | 
			
		||||
          showInfo();
 | 
			
		||||
        },
 | 
			
		||||
        child: SafeArea(
 | 
			
		||||
          child: Stack(
 | 
			
		||||
            children: [
 | 
			
		||||
              Center(
 | 
			
		||||
                child: Hero(
 | 
			
		||||
                  tag: heroTag,
 | 
			
		||||
                  child: CachedNetworkImage(
 | 
			
		||||
                    fit: BoxFit.cover,
 | 
			
		||||
                    imageUrl: imageUrl,
 | 
			
		||||
                    httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
 | 
			
		||||
                    fadeInDuration: const Duration(milliseconds: 250),
 | 
			
		||||
                    errorWidget: (context, url, error) => ConstrainedBox(
 | 
			
		||||
                      constraints: const BoxConstraints(maxWidth: 300),
 | 
			
		||||
                      child: Wrap(
 | 
			
		||||
                        spacing: 32,
 | 
			
		||||
                        runSpacing: 32,
 | 
			
		||||
                        alignment: WrapAlignment.center,
 | 
			
		||||
                        children: [
 | 
			
		||||
                          const Text(
 | 
			
		||||
                            "Failed To Render Image - Possibly Corrupted Data",
 | 
			
		||||
                            textAlign: TextAlign.center,
 | 
			
		||||
                            style: TextStyle(fontSize: 12, color: Colors.grey[400]),
 | 
			
		||||
                            style: TextStyle(fontSize: 16, color: Colors.white),
 | 
			
		||||
                          ),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ],
 | 
			
		||||
                          SingleChildScrollView(
 | 
			
		||||
                            child: Text(
 | 
			
		||||
                              error.toString(),
 | 
			
		||||
                              textAlign: TextAlign.center,
 | 
			
		||||
                              style: TextStyle(fontSize: 12, color: Colors.grey[400]),
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                        ],
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                    placeholder: (context, url) {
 | 
			
		||||
                      return CachedNetworkImage(
 | 
			
		||||
                        cacheKey: thumbnailUrl,
 | 
			
		||||
                        fit: BoxFit.cover,
 | 
			
		||||
                        imageUrl: thumbnailUrl,
 | 
			
		||||
                        httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
 | 
			
		||||
                        placeholderFadeInDuration: const Duration(milliseconds: 0),
 | 
			
		||||
                        progressIndicatorBuilder: (context, url, downloadProgress) => Transform.scale(
 | 
			
		||||
                          scale: 0.2,
 | 
			
		||||
                          child: CircularProgressIndicator(value: downloadProgress.progress),
 | 
			
		||||
                        ),
 | 
			
		||||
                        errorWidget: (context, url, error) => Icon(
 | 
			
		||||
                          Icons.error,
 | 
			
		||||
                          color: Colors.grey[300],
 | 
			
		||||
                        ),
 | 
			
		||||
                      );
 | 
			
		||||
                    },
 | 
			
		||||
                  ),
 | 
			
		||||
                  placeholder: (context, url) {
 | 
			
		||||
                    return CachedNetworkImage(
 | 
			
		||||
                      cacheKey: thumbnailUrl,
 | 
			
		||||
                      fit: BoxFit.cover,
 | 
			
		||||
                      imageUrl: thumbnailUrl,
 | 
			
		||||
                      httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
 | 
			
		||||
                      placeholderFadeInDuration: const Duration(milliseconds: 0),
 | 
			
		||||
                      progressIndicatorBuilder: (context, url, downloadProgress) => Transform.scale(
 | 
			
		||||
                        scale: 0.2,
 | 
			
		||||
                        child: CircularProgressIndicator(value: downloadProgress.progress),
 | 
			
		||||
                      ),
 | 
			
		||||
                      errorWidget: (context, url, error) => Icon(
 | 
			
		||||
                        Icons.error,
 | 
			
		||||
                        color: Colors.grey[300],
 | 
			
		||||
                      ),
 | 
			
		||||
                    );
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            if (downloadAssetStatus == DownloadAssetStatus.loading)
 | 
			
		||||
              const Center(
 | 
			
		||||
                child: DownloadLoadingIndicator(),
 | 
			
		||||
              ),
 | 
			
		||||
          ],
 | 
			
		||||
              if (downloadAssetStatus == DownloadAssetStatus.loading)
 | 
			
		||||
                const Center(
 | 
			
		||||
                  child: DownloadLoadingIndicator(),
 | 
			
		||||
                ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user