From 8d7a53b888e86fad25fa0f3573e7644d997dfcbf Mon Sep 17 00:00:00 2001 From: Benjamin Nussbaum Date: Thu, 8 May 2025 22:49:25 +0200 Subject: [PATCH] feat(#20): preference: assign mirror device id to auto joined devices --- app/Models/User.php | 1 + ...dd_assign_new_device_id_to_users_table.php | 29 +++++++++ .../components/layouts/app/header.blade.php | 2 +- .../components/settings/layout.blade.php | 1 + .../livewire/settings/preferences.blade.php | 63 +++++++++++++++++++ routes/api.php | 2 + routes/web.php | 3 +- tests/Feature/Api/DeviceEndpointsTest.php | 39 ++++++++++++ 8 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 database/migrations/2025_05_08_225241_add_assign_new_device_id_to_users_table.php create mode 100644 resources/views/livewire/settings/preferences.blade.php diff --git a/app/Models/User.php b/app/Models/User.php index 949cafa..5528c2c 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -25,6 +25,7 @@ class User extends Authenticatable // implements MustVerifyEmail 'email', 'password', 'assign_new_devices', + 'assign_new_device_id' ]; /** diff --git a/database/migrations/2025_05_08_225241_add_assign_new_device_id_to_users_table.php b/database/migrations/2025_05_08_225241_add_assign_new_device_id_to_users_table.php new file mode 100644 index 0000000..242b368 --- /dev/null +++ b/database/migrations/2025_05_08_225241_add_assign_new_device_id_to_users_table.php @@ -0,0 +1,29 @@ +foreignId('assign_new_device_id')->nullable()->constrained('devices')->nullOnDelete(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropForeign(['assign_new_device_id']); + $table->dropColumn('assign_new_device_id'); + }); + } +}; diff --git a/resources/views/components/layouts/app/header.blade.php b/resources/views/components/layouts/app/header.blade.php index 6f405e3..4589601 100644 --- a/resources/views/components/layouts/app/header.blade.php +++ b/resources/views/components/layouts/app/header.blade.php @@ -66,7 +66,7 @@ - Settings + Settings diff --git a/resources/views/components/settings/layout.blade.php b/resources/views/components/settings/layout.blade.php index 1c2a9b7..4d4c9fd 100644 --- a/resources/views/components/settings/layout.blade.php +++ b/resources/views/components/settings/layout.blade.php @@ -1,6 +1,7 @@
+ Preferences Profile Password Appearance diff --git a/resources/views/livewire/settings/preferences.blade.php b/resources/views/livewire/settings/preferences.blade.php new file mode 100644 index 0000000..5c89a96 --- /dev/null +++ b/resources/views/livewire/settings/preferences.blade.php @@ -0,0 +1,63 @@ +assign_new_device_id = Auth::user()->assign_new_device_id; + } + + public function updatePreferences(): void + { + $validated = $this->validate([ + 'assign_new_device_id' => [ + 'nullable', + Rule::exists('devices', 'id')->where(function ($query) { + $query->where('user_id', Auth::id()) + ->whereNull('mirror_device_id'); + }), + ], + ]); + + Auth::user()->update($validated); + + $this->dispatch('profile-updated'); + } +}; ?> + +
+ @include('partials.settings-heading') + + +
+ + None + @foreach(auth()->user()->devices->where('mirror_device_id', null) as $device) + + {{ $device->name }} ({{ $device->friendly_id }}) + + @endforeach + + +
+
+ {{ __('Save') }} +
+ + + {{ __('Saved.') }} + +
+
+ +
+
diff --git a/routes/api.php b/routes/api.php index 1a36c21..dae2e11 100644 --- a/routes/api.php +++ b/routes/api.php @@ -28,6 +28,7 @@ Route::get('/display', function (Request $request) { 'name' => "{$auto_assign_user->name}'s TRMNL", 'friendly_id' => Str::random(6), 'default_refresh_interval' => 900, + 'mirror_device_id' => $auto_assign_user->assign_new_device_id, ]); } else { return response()->json([ @@ -135,6 +136,7 @@ Route::get('/setup', function (Request $request) { 'name' => "{$auto_assign_user->name}'s TRMNL", 'friendly_id' => Str::random(6), 'default_refresh_interval' => 900, + 'mirror_device_id' => $auto_assign_user->assign_new_device_id, ]); } else { return response()->json([ diff --git a/routes/web.php b/routes/web.php index 65d9912..c9210da 100644 --- a/routes/web.php +++ b/routes/web.php @@ -8,7 +8,8 @@ Route::get('/', function () { })->name('home'); Route::middleware(['auth'])->group(function () { - Route::redirect('settings', 'settings/profile'); + Route::redirect('settings', 'settings/preferences'); + Volt::route('settings/preferences', 'settings.preferences')->name('settings.preferences'); Volt::route('settings/profile', 'settings.profile')->name('settings.profile'); Volt::route('settings/password', 'settings.password')->name('settings.password'); Volt::route('settings/appearance', 'settings.appearance')->name('settings.appearance'); diff --git a/tests/Feature/Api/DeviceEndpointsTest.php b/tests/Feature/Api/DeviceEndpointsTest.php index 06db54b..395151c 100644 --- a/tests/Feature/Api/DeviceEndpointsTest.php +++ b/tests/Feature/Api/DeviceEndpointsTest.php @@ -105,6 +105,45 @@ test('new device is auto-assigned to user with auto-assign enabled', function () ->api_key->toBe('new-device-key'); }); +test('new device is auto-assigned and mirrors specified device', function () { + // Create a source device that will be mirrored + $sourceDevice = Device::factory()->create([ + 'mac_address' => 'AA:BB:CC:DD:EE:FF', + 'api_key' => 'source-api-key', + 'current_screen_image' => 'source-image', + ]); + + // Create user with auto-assign enabled and mirror device set + $user = User::factory()->create([ + 'assign_new_devices' => true, + 'assign_new_device_id' => $sourceDevice->id, + ]); + + // Make request from new device + $response = $this->withHeaders([ + 'id' => '00:11:22:33:44:55', + 'access-token' => 'new-device-key', + 'rssi' => -70, + 'battery_voltage' => 3.8, + 'fw-version' => '1.0.0', + ])->get('/api/display'); + + $response->assertOk(); + + // Verify the new device was created and mirrors the source device + $newDevice = Device::where('mac_address', '00:11:22:33:44:55')->first(); + expect($newDevice) + ->not->toBeNull() + ->user_id->toBe($user->id) + ->api_key->toBe('new-device-key') + ->mirror_device_id->toBe($sourceDevice->id); + + // Verify the response contains the source device's image + $response->assertJson([ + 'filename' => 'source-image.bmp', + ]); +}); + test('device setup endpoint returns correct data', function () { $device = Device::factory()->create([ 'mac_address' => '00:11:22:33:44:55',