Skip to content

Commit

Permalink
websocket upload notification - closed #24 (#25)
Browse files Browse the repository at this point in the history
* Render when a new asset is uploaded from WebSocket notification
* Update Readme
  • Loading branch information
alextran1502 authored Feb 14, 2022
1 parent 7cc7fc0 commit c234c95
Show file tree
Hide file tree
Showing 23 changed files with 11,037 additions and 69 deletions.
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ dev:
docker-compose -f ./server/docker-compose.yml up

dev-update:
docker-compose -f ./server/docker-compose.yml up --build -V
docker-compose -f ./server/docker-compose.yml up --build -V

dev-scale:
docker-compose -f ./server/docker-compose.yml up --build -V --scale immich_server=3 --remove-orphans
18 changes: 7 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,13 @@ This project is under heavy development, there will be continous functions, feat

# Features

[x] Upload assets(videos/images)

[x] View assets

[x] Quick navigation with drag scroll bar

[x] Auto Backup

[x] Support HEIC/HEIF Backup

[x] Extract and display EXIF info
- Upload assets(videos/images).
- View assets.
- Quick navigation with drag scroll bar.
- Auto Backup.
- Support HEIC/HEIF Backup.
- Extract and display EXIF info.
- Real-time render from multi-device upload event.

# Development

Expand Down
15 changes: 10 additions & 5 deletions mobile/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/home/providers/asset.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/providers/app_state.provider.dart';
import 'package:immich_mobile/shared/providers/backup.provider.dart';
import 'package:immich_mobile/shared/providers/websocket.provider.dart';
import 'constants/hive_box.dart';
import 'package:google_fonts/google_fonts.dart';

Expand Down Expand Up @@ -36,20 +38,23 @@ class _ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserv
switch (state) {
case AppLifecycleState.resumed:
debugPrint("[APP STATE] resumed");
ref.read(appStateProvider.notifier).state = AppStateEnum.resumed;
ref.read(backupProvider.notifier).resumeBackup();
ref.watch(appStateProvider.notifier).state = AppStateEnum.resumed;
ref.watch(backupProvider.notifier).resumeBackup();
ref.watch(websocketProvider.notifier).connect();
ref.watch(assetProvider.notifier).getAllAsset();
break;
case AppLifecycleState.inactive:
debugPrint("[APP STATE] inactive");
ref.read(appStateProvider.notifier).state = AppStateEnum.inactive;
ref.watch(appStateProvider.notifier).state = AppStateEnum.inactive;
ref.watch(websocketProvider.notifier).disconnect();
break;
case AppLifecycleState.paused:
debugPrint("[APP STATE] paused");
ref.read(appStateProvider.notifier).state = AppStateEnum.paused;
ref.watch(appStateProvider.notifier).state = AppStateEnum.paused;
break;
case AppLifecycleState.detached:
debugPrint("[APP STATE] detached");
ref.read(appStateProvider.notifier).state = AppStateEnum.detached;
ref.watch(appStateProvider.notifier).state = AppStateEnum.detached;
break;
}
}
Expand Down
18 changes: 10 additions & 8 deletions mobile/lib/modules/home/providers/asset.provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import 'package:photo_manager/photo_manager.dart';
class AssetNotifier extends StateNotifier<List<ImmichAsset>> {
final AssetService _assetService = AssetService();
final DeviceInfoService _deviceInfoService = DeviceInfoService();
final Ref ref;

AssetNotifier() : super([]);
AssetNotifier(this.ref) : super([]);

getAllAsset() async {
List<ImmichAsset>? allAssets = await _assetService.getAllAsset();

if (allAssets != null) {
allAssets.sortByCompare<DateTime>((e) => DateTime.parse(e.createdAt), (a, b) => b.compareTo(a));
state = allAssets;
}
}
Expand All @@ -26,6 +26,10 @@ class AssetNotifier extends StateNotifier<List<ImmichAsset>> {
state = [];
}

onNewAssetUploaded(ImmichAsset newAsset) {
state = [...state, newAsset];
}

deleteAssets(Set<ImmichAsset> deleteAssets) async {
var deviceInfo = await _deviceInfoService.getDeviceInfo();
var deviceId = deviceInfo["deviceId"];
Expand All @@ -43,7 +47,6 @@ class AssetNotifier extends StateNotifier<List<ImmichAsset>> {
}

final List<String> result = await PhotoManager.editor.deleteWithIds(deleteIdList);
print(result);

// Delete asset on server
List<DeleteAssetResponse>? deleteAssetResult = await _assetService.deleteAssets(deleteAssets);
Expand All @@ -59,14 +62,13 @@ class AssetNotifier extends StateNotifier<List<ImmichAsset>> {
}
}

final currentLocalPageProvider = StateProvider<int>((ref) => 0);

final assetProvider = StateNotifierProvider<AssetNotifier, List<ImmichAsset>>((ref) {
return AssetNotifier();
return AssetNotifier(ref);
});

final assetGroupByDateTimeProvider = StateProvider((ref) {
var assetGroup = ref.watch(assetProvider);
var assets = ref.watch(assetProvider);

return assetGroup.groupListsBy((element) => DateFormat('y-MM-dd').format(DateTime.parse(element.createdAt)));
assets.sortByCompare<DateTime>((e) => DateTime.parse(e.createdAt), (a, b) => b.compareTo(a));
return assets.groupListsBy((element) => DateFormat('y-MM-dd').format(DateTime.parse(element.createdAt)));
});
2 changes: 2 additions & 0 deletions mobile/lib/modules/home/ui/profile_drawer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:immich_mobile/modules/home/providers/asset.provider.dart';
import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
import 'package:immich_mobile/shared/providers/backup.provider.dart';
import 'package:immich_mobile/shared/providers/websocket.provider.dart';

class ProfileDrawer extends ConsumerWidget {
const ProfileDrawer({Key? key}) : super(key: key);
Expand Down Expand Up @@ -60,6 +61,7 @@ class ProfileDrawer extends ConsumerWidget {
if (res) {
ref.watch(backupProvider.notifier).cancelBackup();
ref.watch(assetProvider.notifier).clearAllAsset();
ref.watch(websocketProvider.notifier).disconnect();
AutoRouter.of(context).popUntilRoot();
}
},
Expand Down
4 changes: 3 additions & 1 deletion mobile/lib/modules/home/views/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:immich_mobile/modules/home/ui/immich_sliver_appbar.dart';
import 'package:immich_mobile/modules/home/ui/monthly_title_text.dart';
import 'package:immich_mobile/modules/home/ui/profile_drawer.dart';
import 'package:immich_mobile/modules/home/providers/asset.provider.dart';
import 'package:immich_mobile/shared/providers/websocket.provider.dart';
import 'package:sliver_tools/sliver_tools.dart';

class HomePage extends HookConsumerWidget {
Expand All @@ -25,12 +26,13 @@ class HomePage extends HookConsumerWidget {
var homePageState = ref.watch(homePageStateProvider);

useEffect(() {
ref.read(websocketProvider.notifier).connect();
ref.read(assetProvider.notifier).getAllAsset();
return null;
}, []);

onPopBackFromBackupPage() {
ref.read(assetProvider.notifier).getAllAsset();
// ref.read(assetProvider.notifier).getAllAsset();
}

Widget _buildBody() {
Expand Down
113 changes: 113 additions & 0 deletions mobile/lib/shared/providers/websocket.provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import 'dart:convert';

import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/home/providers/asset.provider.dart';
import 'package:immich_mobile/shared/models/immich_asset.model.dart';
import 'package:socket_io_client/socket_io_client.dart';

import 'package:immich_mobile/constants/hive_box.dart';
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';

class WebscoketState {
final Socket? socket;
final bool isConnected;

WebscoketState({
this.socket,
required this.isConnected,
});

WebscoketState copyWith({
Socket? socket,
bool? isConnected,
}) {
return WebscoketState(
socket: socket ?? this.socket,
isConnected: isConnected ?? this.isConnected,
);
}

@override
String toString() => 'WebscoketState(socket: $socket, isConnected: $isConnected)';

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;

return other is WebscoketState && other.socket == socket && other.isConnected == isConnected;
}

@override
int get hashCode => socket.hashCode ^ isConnected.hashCode;
}

class WebsocketNotifier extends StateNotifier<WebscoketState> {
WebsocketNotifier(this.ref) : super(WebscoketState(socket: null, isConnected: false)) {
debugPrint("Init websocket instance");
}

final Ref ref;

connect() {
var authenticationState = ref.watch(authenticationProvider);

if (authenticationState.isAuthenticated) {
var accessToken = Hive.box(userInfoBox).get(accessTokenKey);
var endpoint = Hive.box(userInfoBox).get(serverEndpointKey);
try {
debugPrint("[WEBSOCKET] Attempting to connect to ws");
// Configure socket transports must be sepecified
Socket socket = io(
endpoint,
OptionBuilder()
.setTransports(['websocket'])
.enableReconnection()
.enableForceNew()
.enableForceNewConnection()
.enableAutoConnect()
.setExtraHeaders({"Authorization": "Bearer $accessToken"})
.build(),
);

socket.onConnect((_) {
debugPrint("[WEBSOCKET] Established Websocket Connection");
state = WebscoketState(isConnected: true, socket: socket);
});

socket.onDisconnect((_) {
debugPrint("[WEBSOCKET] Disconnect to Websocket Connection");
state = WebscoketState(isConnected: false, socket: null);
});

socket.on('error', (errorMessage) {
debugPrint("Webcoket Error - $errorMessage");
state = WebscoketState(isConnected: false, socket: null);
});

socket.on('on_upload_success', (data) {
var jsonString = jsonDecode(data.toString());
ImmichAsset newAsset = ImmichAsset.fromMap(jsonString);
ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset);
});
} catch (e) {
debugPrint("[WEBSOCKET] Catch Websocket Error - ${e.toString()}");
}
}
}

disconnect() {
debugPrint("[WEBSOCKET] Attempting to disconnect");
var socket = state.socket?.disconnect();
if (socket != null) {
if (socket.disconnected) {
state = WebscoketState(isConnected: false, socket: null);
}
}
}
}

final websocketProvider = StateNotifierProvider<WebsocketNotifier, WebscoketState>((ref) {
return WebsocketNotifier(ref);
});
14 changes: 14 additions & 0 deletions mobile/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.5"
socket_io_client:
dependency: "direct main"
description:
name: socket_io_client
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0-beta.4-nullsafety.0"
socket_io_common:
dependency: transitive
description:
name: socket_io_common
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
source_gen:
dependency: transitive
description:
Expand Down
1 change: 1 addition & 0 deletions mobile/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ dependencies:
sliver_tools: ^0.2.5
badges: ^2.0.2
photo_view: ^0.13.0
socket_io_client: ^2.0.0-beta.4-nullsafety.0

dev_dependencies:
flutter_test:
Expand Down
9 changes: 4 additions & 5 deletions server/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ version: '3.8'


services:
server:
container_name: immich_server
immich_server:
image: immich-server-dev:1.0.0
build:
context: .
target: development
dockerfile: ./Dockerfile
command: npm run start:dev
ports:
- "3000:3000"
expose:
- "3000"
volumes:
- .:/usr/src/app
- ${UPLOAD_LOCATION}:/usr/src/app/upload
Expand Down Expand Up @@ -60,7 +59,7 @@ services:
networks:
- immich_network
depends_on:
- server
- immich_server

networks:
immich_network:
Expand Down
Loading

0 comments on commit c234c95

Please sign in to comment.