mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:17:11 -05:00 
			
		
		
		
	Merge branch 'main' of github.com:immich-app/immich
This commit is contained in:
		
						commit
						6acfac9064
					
				
							
								
								
									
										53
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										53
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							@ -39,3 +39,56 @@ jobs:
 | 
			
		||||
 | 
			
		||||
      - name: Run tests
 | 
			
		||||
        run: cd web && npm ci && npm run check:all
 | 
			
		||||
 | 
			
		||||
  mobile-unit-tests:
 | 
			
		||||
    name: Run mobile unit tests
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - name: Setup Flutter SDK
 | 
			
		||||
        uses: subosito/flutter-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          channel: 'stable'
 | 
			
		||||
          flutter-version: '3.3.10'
 | 
			
		||||
      - name: Run tests
 | 
			
		||||
        working-directory: ./mobile
 | 
			
		||||
        run: flutter test
 | 
			
		||||
 | 
			
		||||
  mobile-integration-tests:
 | 
			
		||||
    name: Run mobile end-to-end integration tests
 | 
			
		||||
    runs-on: macos-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - uses: actions/setup-java@v3
 | 
			
		||||
        with:
 | 
			
		||||
          distribution: 'adopt'
 | 
			
		||||
          java-version: '11'
 | 
			
		||||
      - name: Cache android SDK
 | 
			
		||||
        uses: actions/cache@v3
 | 
			
		||||
        id: android-sdk
 | 
			
		||||
        with:
 | 
			
		||||
          key: android-sdk
 | 
			
		||||
          path: |
 | 
			
		||||
            /usr/local/lib/android/
 | 
			
		||||
            ~/.android
 | 
			
		||||
      - name: Setup Android SDK
 | 
			
		||||
        if: steps.android-sdk.outputs.cache-hit != 'true'
 | 
			
		||||
        uses: android-actions/setup-android@v2
 | 
			
		||||
      - name: Setup Flutter SDK
 | 
			
		||||
        uses: subosito/flutter-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          channel: 'stable'
 | 
			
		||||
          flutter-version: '3.3.10'
 | 
			
		||||
      - name: Run integration tests
 | 
			
		||||
        uses: reactivecircus/android-emulator-runner@v2.27.0
 | 
			
		||||
        with:
 | 
			
		||||
          working-directory: ./mobile
 | 
			
		||||
          api-level: 29
 | 
			
		||||
          arch: x86_64
 | 
			
		||||
          profile: pixel
 | 
			
		||||
          target: default
 | 
			
		||||
          emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim
 | 
			
		||||
          disable-linux-hw-accel: false
 | 
			
		||||
          script: |
 | 
			
		||||
            flutter pub get
 | 
			
		||||
            flutter test integration_test
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										44
									
								
								.github/workflows/test_mobile.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										44
									
								
								.github/workflows/test_mobile.yml
									
									
									
									
										vendored
									
									
								
							@ -1,44 +0,0 @@
 | 
			
		||||
name: Flutter Integration Tests
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [ "main" ]
 | 
			
		||||
  pull_request:
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  build:
 | 
			
		||||
    runs-on: macos-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - uses: actions/setup-java@v3
 | 
			
		||||
        with:
 | 
			
		||||
          distribution: 'adopt'
 | 
			
		||||
          java-version: '11'
 | 
			
		||||
      - name: Cache android SDK
 | 
			
		||||
        uses: actions/cache@v3
 | 
			
		||||
        id: android-sdk
 | 
			
		||||
        with:
 | 
			
		||||
          key: android-sdk
 | 
			
		||||
          path: |
 | 
			
		||||
            /usr/local/lib/android/
 | 
			
		||||
            ~/.android
 | 
			
		||||
      - name: Setup Android SDK
 | 
			
		||||
        if: steps.android-sdk.outputs.cache-hit != 'true'
 | 
			
		||||
        uses: android-actions/setup-android@v2
 | 
			
		||||
      - name: Setup Flutter SDK
 | 
			
		||||
        uses: subosito/flutter-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          channel: 'stable'
 | 
			
		||||
      - name: Run integration tests
 | 
			
		||||
        uses: reactivecircus/android-emulator-runner@v2.27.0
 | 
			
		||||
        with:
 | 
			
		||||
          working-directory: ./mobile
 | 
			
		||||
          api-level: 29
 | 
			
		||||
          arch: x86_64
 | 
			
		||||
          profile: pixel
 | 
			
		||||
          target: default
 | 
			
		||||
          emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim
 | 
			
		||||
          disable-linux-hw-accel: false
 | 
			
		||||
          script: |
 | 
			
		||||
            flutter pub get
 | 
			
		||||
            flutter test integration_test
 | 
			
		||||
@ -93,6 +93,9 @@ If you feel like this is the right cause and the app is something you are seeing
 | 
			
		||||
 | 
			
		||||
- [Monthly donation](https://github.com/sponsors/alextran1502) via GitHub Sponsors
 | 
			
		||||
- [One-time donation](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) via Github Sponsors
 | 
			
		||||
- [Librepay](https://liberapay.com/alex.tran1502/)
 | 
			
		||||
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
 | 
			
		||||
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
 | 
			
		||||
 | 
			
		||||
# Known Issues
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
---
 | 
			
		||||
sidebar_position: 99
 | 
			
		||||
sidebar_position: 70
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
# All-In-One [Community]
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
---
 | 
			
		||||
sidebar_position: 3
 | 
			
		||||
sidebar_position: 30
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
# Docker Compose [Recommended]
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								docs/docs/install/kubernetes.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								docs/docs/install/kubernetes.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
---
 | 
			
		||||
sidebar_position: 40
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
# Kubernetes
 | 
			
		||||
 | 
			
		||||
You can deploy Immich on Kubernetes using [the official Helm chart](https://github.com/immich-app/immich-charts/tree/main/charts/apps/immich). 
 | 
			
		||||
 | 
			
		||||
If you want examples of how other people run Immich on Kubernetes, using the official chart or otherwise, you can find them at https://nanne.dev/k8s-at-home-search/#/immich.
 | 
			
		||||
 | 
			
		||||
:::caution DNS in Alpine containers
 | 
			
		||||
Immich makes use of Alpine container images. These can encounter [a DNS resolution bug](https://stackoverflow.com/a/65593511) on Kubernetes clusters if the host 
 | 
			
		||||
nodes have a search domain set, like:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
$ cat /etc/resolv.conf
 | 
			
		||||
search home.lan
 | 
			
		||||
nameserver 192.168.1.1
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
When you encounter this bug, it will cause the immich-microservices to crash on startup because it cannot download 
 | 
			
		||||
the geocoder data. This can be solved in one of two ways: Either reconfigure your nodes to remove the searchdomain from 
 | 
			
		||||
`resolv.conf`, or set the `DISABLE_REVERSE_GEOCODING` environment variable for Immich to `true` to disable the geocoder.
 | 
			
		||||
:::
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
---
 | 
			
		||||
sidebar_position: 4
 | 
			
		||||
sidebar_position: 50
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
# Portainer
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
---
 | 
			
		||||
sidebar_position: 1
 | 
			
		||||
sidebar_position: 10
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
---
 | 
			
		||||
sidebar_position: 2
 | 
			
		||||
sidebar_position: 20
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
# Install Script [Experimental]
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
---
 | 
			
		||||
sidebar_position: 5
 | 
			
		||||
sidebar_position: 60
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
# Unraid
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,10 @@ If you feel like this is the right cause and the app is something you see yourse
 | 
			
		||||
 | 
			
		||||
- Monthly donation via [GitHub Sponsors](https://github.com/sponsors/alextran1502)
 | 
			
		||||
- One-time donation via [GitHub Sponsors](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502)
 | 
			
		||||
- [Librepay](https://liberapay.com/alex.tran1502/)
 | 
			
		||||
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
 | 
			
		||||
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Contributing
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -110,6 +110,8 @@
 | 
			
		||||
  "experimental_settings_title": "Experimental",
 | 
			
		||||
  "home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.",
 | 
			
		||||
  "home_page_add_to_album_success": "Added {added} assets to album {album}.",
 | 
			
		||||
  "home_page_building_timeline": "Building the timeline",
 | 
			
		||||
  "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).",
 | 
			
		||||
  "library_page_albums": "Albums",
 | 
			
		||||
  "library_page_new_album": "New album",
 | 
			
		||||
  "login_form_button_text": "Login",
 | 
			
		||||
 | 
			
		||||
@ -8,12 +8,11 @@ void main() async {
 | 
			
		||||
  await ImmichTestHelper.initialize();
 | 
			
		||||
 | 
			
		||||
  group("Login input validation test", () {
 | 
			
		||||
    immichWidgetTest("Test leading/trailing whitespace", (tester) async {
 | 
			
		||||
      await ImmichTestLoginHelper.waitForLoginScreen(tester);
 | 
			
		||||
      await ImmichTestLoginHelper.acknowledgeNewServerVersion(tester);
 | 
			
		||||
    immichWidgetTest("Test leading/trailing whitespace", (tester, helper) async {
 | 
			
		||||
      await helper.loginHelper.waitForLoginScreen();
 | 
			
		||||
      await helper.loginHelper.acknowledgeNewServerVersion();
 | 
			
		||||
 | 
			
		||||
      await ImmichTestLoginHelper.enterLoginCredentials(
 | 
			
		||||
        tester,
 | 
			
		||||
      await helper.loginHelper.enterCredentials(
 | 
			
		||||
        email: " demo@immich.app"
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
@ -21,8 +20,7 @@ void main() async {
 | 
			
		||||
 | 
			
		||||
      expect(find.text("login_form_err_leading_whitespace".tr()), findsOneWidget);
 | 
			
		||||
 | 
			
		||||
      await ImmichTestLoginHelper.enterLoginCredentials(
 | 
			
		||||
          tester,
 | 
			
		||||
      await helper.loginHelper.enterCredentials(
 | 
			
		||||
          email: "demo@immich.app "
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
@ -31,12 +29,11 @@ void main() async {
 | 
			
		||||
      expect(find.text("login_form_err_trailing_whitespace".tr()), findsOneWidget);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    immichWidgetTest("Test invalid email", (tester) async {
 | 
			
		||||
      await ImmichTestLoginHelper.waitForLoginScreen(tester);
 | 
			
		||||
      await ImmichTestLoginHelper.acknowledgeNewServerVersion(tester);
 | 
			
		||||
    immichWidgetTest("Test invalid email", (tester, helper) async {
 | 
			
		||||
      await helper.loginHelper.waitForLoginScreen();
 | 
			
		||||
      await helper.loginHelper.acknowledgeNewServerVersion();
 | 
			
		||||
 | 
			
		||||
      await ImmichTestLoginHelper.enterLoginCredentials(
 | 
			
		||||
          tester,
 | 
			
		||||
      await helper.loginHelper.enterCredentials(
 | 
			
		||||
          email: "demo.immich.app"
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										39
									
								
								mobile/integration_test/module_login/login_test.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								mobile/integration_test/module_login/login_test.dart
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
			
		||||
import 'dart:io';
 | 
			
		||||
 | 
			
		||||
import 'package:flutter_test/flutter_test.dart';
 | 
			
		||||
 | 
			
		||||
import '../test_utils/general_helper.dart';
 | 
			
		||||
import '../test_utils/login_helper.dart';
 | 
			
		||||
 | 
			
		||||
void main() async {
 | 
			
		||||
  await ImmichTestHelper.initialize();
 | 
			
		||||
 | 
			
		||||
  group("Login tests", () {
 | 
			
		||||
    immichWidgetTest("Test correct credentials", (tester, helper) async {
 | 
			
		||||
      await helper.loginHelper.waitForLoginScreen();
 | 
			
		||||
      await helper.loginHelper.acknowledgeNewServerVersion();
 | 
			
		||||
      await helper.loginHelper
 | 
			
		||||
          .enterCredentialsOf(LoginCredentials.testInstance);
 | 
			
		||||
      await helper.loginHelper.pressLoginButton();
 | 
			
		||||
      await helper.loginHelper.assertLoginSuccess();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    immichWidgetTest("Test login with wrong password", (tester, helper) async {
 | 
			
		||||
      await helper.loginHelper.waitForLoginScreen();
 | 
			
		||||
      await helper.loginHelper.acknowledgeNewServerVersion();
 | 
			
		||||
      await helper.loginHelper.enterCredentialsOf(
 | 
			
		||||
          LoginCredentials.testInstanceButWithWrongPassword);
 | 
			
		||||
      await helper.loginHelper.pressLoginButton();
 | 
			
		||||
      await helper.loginHelper.assertLoginFailed();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    immichWidgetTest("Test login with wrong server URL", (tester, helper) async {
 | 
			
		||||
      await helper.loginHelper.waitForLoginScreen();
 | 
			
		||||
      await helper.loginHelper.acknowledgeNewServerVersion();
 | 
			
		||||
      await helper.loginHelper.enterCredentialsOf(
 | 
			
		||||
          LoginCredentials.wrongInstanceUrl);
 | 
			
		||||
      await helper.loginHelper.pressLoginButton();
 | 
			
		||||
      await helper.loginHelper.assertLoginFailed();
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
@ -1,14 +1,28 @@
 | 
			
		||||
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
import 'package:flutter/foundation.dart';
 | 
			
		||||
import 'package:flutter_test/flutter_test.dart';
 | 
			
		||||
import 'package:hive/hive.dart';
 | 
			
		||||
import 'package:immich_mobile/main.dart';
 | 
			
		||||
import 'package:integration_test/integration_test.dart';
 | 
			
		||||
 | 
			
		||||
import 'package:meta/meta.dart';
 | 
			
		||||
import 'package:immich_mobile/main.dart' as app;
 | 
			
		||||
 | 
			
		||||
import 'login_helper.dart';
 | 
			
		||||
 | 
			
		||||
class ImmichTestHelper {
 | 
			
		||||
 | 
			
		||||
  final WidgetTester tester;
 | 
			
		||||
 | 
			
		||||
  ImmichTestHelper(this.tester);
 | 
			
		||||
 | 
			
		||||
  ImmichTestLoginHelper? _loginHelper;
 | 
			
		||||
 | 
			
		||||
  ImmichTestLoginHelper get loginHelper {
 | 
			
		||||
    _loginHelper ??= ImmichTestLoginHelper(tester);
 | 
			
		||||
    return _loginHelper!;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static Future<IntegrationTestWidgetsFlutterBinding> initialize() async {
 | 
			
		||||
    final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 | 
			
		||||
    binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
 | 
			
		||||
@ -32,9 +46,12 @@ class ImmichTestHelper {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void immichWidgetTest(String description, Future<void> Function(WidgetTester) test) {
 | 
			
		||||
  testWidgets(description, (widgetTester) async {
 | 
			
		||||
    await ImmichTestHelper.loadApp(widgetTester);
 | 
			
		||||
    await test(widgetTester);
 | 
			
		||||
  });
 | 
			
		||||
@isTest
 | 
			
		||||
void immichWidgetTest(String description, Future<void> Function(WidgetTester, ImmichTestHelper) test) {
 | 
			
		||||
 | 
			
		||||
    testWidgets(description, (widgetTester) async {
 | 
			
		||||
        await ImmichTestHelper.loadApp(widgetTester);
 | 
			
		||||
        await test(widgetTester, ImmichTestHelper(widgetTester));
 | 
			
		||||
    }, semanticsEnabled: false);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -1,10 +1,15 @@
 | 
			
		||||
import 'dart:async';
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_test/flutter_test.dart';
 | 
			
		||||
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
 | 
			
		||||
 | 
			
		||||
class ImmichTestLoginHelper {
 | 
			
		||||
  static Future<void> waitForLoginScreen(WidgetTester tester,
 | 
			
		||||
      {int timeoutSeconds = 20}) async {
 | 
			
		||||
  final WidgetTester tester;
 | 
			
		||||
 | 
			
		||||
  ImmichTestLoginHelper(this.tester);
 | 
			
		||||
 | 
			
		||||
  Future<void> waitForLoginScreen({int timeoutSeconds = 20}) async {
 | 
			
		||||
    for (var i = 0; i < timeoutSeconds; i++) {
 | 
			
		||||
      // Search for "IMMICH" test in the app bar
 | 
			
		||||
      final result = find.text("IMMICH");
 | 
			
		||||
@ -21,7 +26,7 @@ class ImmichTestLoginHelper {
 | 
			
		||||
    fail("Timeout while waiting for login screen");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static Future<bool> acknowledgeNewServerVersion(WidgetTester tester) async {
 | 
			
		||||
  Future<bool> acknowledgeNewServerVersion() async {
 | 
			
		||||
    final result = find.text("Acknowledge");
 | 
			
		||||
    if (!tester.any(result)) {
 | 
			
		||||
      return false;
 | 
			
		||||
@ -33,8 +38,7 @@ class ImmichTestLoginHelper {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static Future<void> enterLoginCredentials(
 | 
			
		||||
    WidgetTester tester, {
 | 
			
		||||
  Future<void> enterCredentials({
 | 
			
		||||
    String server = "",
 | 
			
		||||
    String email = "",
 | 
			
		||||
    String password = "",
 | 
			
		||||
@ -50,6 +54,70 @@ class ImmichTestLoginHelper {
 | 
			
		||||
    await tester.pump(const Duration(milliseconds: 500));
 | 
			
		||||
    await tester.enterText(loginForms.at(2), server);
 | 
			
		||||
 | 
			
		||||
    await tester.pump(const Duration(milliseconds: 500));
 | 
			
		||||
    await tester.testTextInput.receiveAction(TextInputAction.done);
 | 
			
		||||
    await tester.pumpAndSettle();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> enterCredentialsOf(LoginCredentials credentials) async {
 | 
			
		||||
    await enterCredentials(
 | 
			
		||||
      server: credentials.server,
 | 
			
		||||
      email: credentials.email,
 | 
			
		||||
      password: credentials.password,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> pressLoginButton() async {
 | 
			
		||||
    final button = find.textContaining("login_form_button_text".tr());
 | 
			
		||||
    await tester.tap(button);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> assertLoginSuccess({int timeoutSeconds = 15}) async {
 | 
			
		||||
    for (var i = 0; i < timeoutSeconds * 2; i++) {
 | 
			
		||||
      if (tester.any(find.text("home_page_building_timeline".tr()))) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      await tester.pump(const Duration(milliseconds: 500));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fail("Login failed.");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> assertLoginFailed({int timeoutSeconds = 15}) async {
 | 
			
		||||
    for (var i = 0; i < timeoutSeconds * 2; i++) {
 | 
			
		||||
      if (tester.any(find.text("login_form_failed_login".tr()))) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      await tester.pump(const Duration(milliseconds: 500));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fail("Timeout.");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum LoginCredentials {
 | 
			
		||||
  testInstance(
 | 
			
		||||
    "https://flutter-int-test.preview.immich.app",
 | 
			
		||||
    "demo@immich.app",
 | 
			
		||||
    "demo",
 | 
			
		||||
  ),
 | 
			
		||||
 | 
			
		||||
  testInstanceButWithWrongPassword(
 | 
			
		||||
    "https://flutter-int-test.preview.immich.app",
 | 
			
		||||
    "demo@immich.app",
 | 
			
		||||
    "wrong",
 | 
			
		||||
  ),
 | 
			
		||||
 | 
			
		||||
  wrongInstanceUrl(
 | 
			
		||||
  "https://does-not-exist.preview.immich.app",
 | 
			
		||||
  "demo@immich.app",
 | 
			
		||||
  "demo",
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const LoginCredentials(this.server, this.email, this.password);
 | 
			
		||||
 | 
			
		||||
  final String server;
 | 
			
		||||
  final String email;
 | 
			
		||||
  final String password;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ import 'dart:async';
 | 
			
		||||
 | 
			
		||||
import 'package:auto_route/auto_route.dart';
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
import 'package:flutter/foundation.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_hooks/flutter_hooks.dart';
 | 
			
		||||
import 'package:fluttertoast/fluttertoast.dart';
 | 
			
		||||
@ -52,7 +53,10 @@ class HomePage extends HookConsumerWidget {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return () {
 | 
			
		||||
          selectionEnabledHook.dispose();
 | 
			
		||||
          // This does not work in tests
 | 
			
		||||
          if (kReleaseMode) {
 | 
			
		||||
            selectionEnabledHook.dispose();
 | 
			
		||||
          }
 | 
			
		||||
        };
 | 
			
		||||
      },
 | 
			
		||||
      [],
 | 
			
		||||
@ -162,28 +166,28 @@ class HomePage extends HookConsumerWidget {
 | 
			
		||||
              Padding(
 | 
			
		||||
                padding: const EdgeInsets.only(top: 16.0),
 | 
			
		||||
                child: Text(
 | 
			
		||||
                  'Building the timeline',
 | 
			
		||||
                  'home_page_building_timeline',
 | 
			
		||||
                  style: TextStyle(
 | 
			
		||||
                    fontWeight: FontWeight.w600,
 | 
			
		||||
                    fontSize: 16,
 | 
			
		||||
                    color: Theme.of(context).primaryColor,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                ).tr(),
 | 
			
		||||
              ),
 | 
			
		||||
              AnimatedOpacity(
 | 
			
		||||
                duration: const Duration(milliseconds: 500),
 | 
			
		||||
                opacity: tipOneOpacity.value,
 | 
			
		||||
                child: const SizedBox(
 | 
			
		||||
                child: SizedBox(
 | 
			
		||||
                  width: 250,
 | 
			
		||||
                  child: Padding(
 | 
			
		||||
                    padding: EdgeInsets.only(top: 8.0),
 | 
			
		||||
                    child: Text(
 | 
			
		||||
                      'If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).',
 | 
			
		||||
                    padding: const EdgeInsets.only(top: 8.0),
 | 
			
		||||
                    child: const Text(
 | 
			
		||||
                      'home_page_first_time_notice',
 | 
			
		||||
                      textAlign: TextAlign.justify,
 | 
			
		||||
                      style: TextStyle(
 | 
			
		||||
                        fontSize: 12,
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                    ).tr(),
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              )
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user