diff --git a/app/Http/Controllers/UserAppSettingsController.php b/app/Http/Controllers/UserAppSettingsController.php new file mode 100644 index 000000000..b18c97931 --- /dev/null +++ b/app/Http/Controllers/UserAppSettingsController.php @@ -0,0 +1,48 @@ +middleware('auth'); + } + + public function get(Request $request) + { + abort_if(!$request->user(), 403); + + $settings = UserAppSettings::whereUserId($request->user()->id)->first(); + + if(!$settings) { + return [ + 'id' => (string) $request->user()->profile_id, + 'username' => $request->user()->username, + 'updated_at' => null, + 'common' => AccountAppSettingsService::default(), + ]; + } + + return new UserAppSettingsResource($settings); + } + + public function store(StoreUserAppSettings $request) + { + $res = UserAppSettings::updateOrCreate([ + 'user_id' => $request->user()->id, + ],[ + 'profile_id' => $request->user()->profile_id, + 'common' => $request->common, + ] + ); + + return new UserAppSettingsResource($res); + } +} diff --git a/app/Http/Requests/StoreUserAppSettings.php b/app/Http/Requests/StoreUserAppSettings.php new file mode 100644 index 000000000..424ef4931 --- /dev/null +++ b/app/Http/Requests/StoreUserAppSettings.php @@ -0,0 +1,82 @@ +user() || $this->user()->status) { + return false; + } + + return true; + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules(): array + { + return [ + 'common' => 'required|array', + 'common.timelines.show_public' => 'required|boolean', + 'common.timelines.show_network' => 'required|boolean', + 'common.timelines.hide_likes_shares' => 'required|boolean', + 'common.media.hide_public_behind_cw' => 'required|boolean', + 'common.media.always_show_cw' => 'required|boolean', + 'common.media.show_alt_text' => 'required|boolean', + 'common.appearance.links_use_in_app_browser' => 'required|boolean', + 'common.appearance.theme' => 'required|string|in:light,dark,system', + ]; + } + /** + * Prepare inputs for validation. + * + * @return void + */ + protected function prepareForValidation() + { + $this->merge([ + 'common' => array_merge( + $this->input('common'), + [ + 'timelines' => [ + 'show_public' => $this->toBoolean($this->input('common.timelines.show_public')), + 'show_network' => $this->toBoolean($this->input('common.timelines.show_network')), + 'hide_likes_shares' => $this->toBoolean($this->input('common.timelines.hide_likes_shares')) + ], + + 'media' => [ + 'hide_public_behind_cw' => $this->toBoolean($this->input('common.media.hide_public_behind_cw')), + 'always_show_cw' => $this->toBoolean($this->input('common.media.always_show_cw')), + 'show_alt_text' => $this->toBoolean($this->input('common.media.show_alt_text')), + ], + + 'appearance' => [ + 'links_use_in_app_browser' => $this->toBoolean($this->input('common.appearance.links_use_in_app_browser')), + 'theme' => $this->input('common.appearance.theme'), + ] + ] + ) + ]); + } + + /** + * Convert to boolean + * + * @param $booleable + * @return boolean + */ + private function toBoolean($booleable) + { + return filter_var($booleable, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } +} diff --git a/app/Http/Resources/UserAppSettingsResource.php b/app/Http/Resources/UserAppSettingsResource.php new file mode 100644 index 000000000..814a8f55c --- /dev/null +++ b/app/Http/Resources/UserAppSettingsResource.php @@ -0,0 +1,27 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => (string) $this->profile_id, + 'username' => $request->user()->username, + 'updated_at' => str_replace('+00:00', 'Z', $this->updated_at->format(DATE_RFC3339_EXTENDED)), + 'common' => $this->common, + ]; + } +} diff --git a/app/Models/UserAppSettings.php b/app/Models/UserAppSettings.php new file mode 100644 index 000000000..aa0f7f351 --- /dev/null +++ b/app/Models/UserAppSettings.php @@ -0,0 +1,30 @@ + 'json', + 'custom' => 'json', + 'common.timelines.show_public' => 'boolean', + 'common.timelines.show_network' => 'boolean', + 'common.timelines.hide_likes_shares' => 'boolean', + 'common.media.hide_public_behind_cw' => 'boolean', + 'common.media.always_show_cw' => 'boolean', + 'common.media.show_alt_text' => 'boolean', + ]; + + public function user() + { + return $this->belongsTo(User::class); + } +} diff --git a/app/Services/Account/AccountAppSettingsService.php b/app/Services/Account/AccountAppSettingsService.php new file mode 100644 index 000000000..a8107faac --- /dev/null +++ b/app/Services/Account/AccountAppSettingsService.php @@ -0,0 +1,43 @@ + [ + // Show public timeline feed + 'show_public' => false, + + // Show network timeline feed + 'show_network' => false, + + // Hide likes and share counts + 'hide_likes_shares' => false, + ], + + 'media' => [ + // Hide media on Public/Network timelines behind CW + 'hide_public_behind_cw' => true, + + // Always show media with CW + 'always_show_cw' => false, + + // Show alt text if present below media + 'show_alt_text' => false, + ], + + 'appearance' => [ + // Use in-app browser when opening links + 'links_use_in_app_browser' => true, + + // App theme, can be 'light', 'dark' or 'system' + 'theme' => 'system', + ] + ]; + } +} diff --git a/database/migrations/2023_05_29_072206_create_user_app_settings_table.php b/database/migrations/2023_05_29_072206_create_user_app_settings_table.php new file mode 100644 index 000000000..813ae4603 --- /dev/null +++ b/database/migrations/2023_05_29_072206_create_user_app_settings_table.php @@ -0,0 +1,31 @@ +id(); + $table->unsignedInteger('user_id')->unique()->index(); + $table->bigInteger('profile_id')->unsigned()->unique()->index(); + $table->json('common')->nullable(); + $table->json('custom')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('user_app_settings'); + } +}; diff --git a/routes/api.php b/routes/api.php index a0b8b5155..ba883073e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -117,6 +117,9 @@ Route::group(['prefix' => 'api'], function() use($middleware) { Route::get('two-factor', 'Api\ApiV1Dot1Controller@accountTwoFactor')->middleware($middleware); Route::get('emails-from-pixelfed', 'Api\ApiV1Dot1Controller@accountEmailsFromPixelfed')->middleware($middleware); Route::get('apps-and-applications', 'Api\ApiV1Dot1Controller@accountApps')->middleware($middleware); + + Route::get('app/settings', 'UserAppSettingsController@get')->middleware($middleware); + Route::post('app/settings', 'UserAppSettingsController@store')->middleware($middleware); }); Route::group(['prefix' => 'collections'], function () use($middleware) {