From 5d145dc52eb74c78b0d4409563c04ffed0418c72 Mon Sep 17 00:00:00 2001 From: Spitfire Date: Tue, 6 Feb 2024 15:33:44 -0600 Subject: [PATCH 1/2] Feature: Upload Image --- app/Livewire/Roadmap/Form.php | 15 ++ app/Models/Feature.php | 6 + app/Models/FeatureFile.php | 25 +++ config/livewire.php | 159 ++++++++++++++++++ ...2024_02_06_162941_create_feature_files.php | 31 ++++ .../views/livewire/roadmap/form.blade.php | 7 + 6 files changed, 243 insertions(+) create mode 100644 app/Models/FeatureFile.php create mode 100644 config/livewire.php create mode 100644 database/migrations/2024_02_06_162941_create_feature_files.php diff --git a/app/Livewire/Roadmap/Form.php b/app/Livewire/Roadmap/Form.php index 8a4219d809..aeaa16e9d3 100644 --- a/app/Livewire/Roadmap/Form.php +++ b/app/Livewire/Roadmap/Form.php @@ -4,11 +4,18 @@ use App\Enums\FeatureStatus; use App\Models\Feature; +use App\Models\FeatureFile; use Livewire\Attributes\Validate; use Livewire\Component; +use Livewire\WithFileUploads; class Form extends Component { + use WithFileUploads; + + #[Validate('image|nullable|max:3072')] // 3MB Max + public $file; + #[Validate('required|min:5')] public string $title = ''; @@ -31,6 +38,14 @@ public function save() $feat->status_id = FeatureStatus::Draft; $feat->save(); + if ($this->file) { + $file = $this->file->storeAs('features/' . $feat->id, uniqid() . '.' . $this->file->getClientOriginalExtension(), 's3'); + $featFile = new FeatureFile(); + $featFile->feature_id = $feat->id; + $featFile->path = $file; + $featFile->save(); + } + $this->success = true; $this->title = ''; $this->description = ''; diff --git a/app/Models/Feature.php b/app/Models/Feature.php index 77dfb4b919..464b16402f 100644 --- a/app/Models/Feature.php +++ b/app/Models/Feature.php @@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasOne; /** @@ -52,6 +53,11 @@ public function uservote(): HasOne ->where('user_id', auth()->user()->id); } + public function featureFiles(): HasMany + { + return $this->hasMany(FeatureFile::class, 'feature_id', 'id'); + } + public function scopeApproved(Builder $builder): Builder { return $builder->whereIn('status_id', [\App\Enums\FeatureStatus::Approved]) diff --git a/app/Models/FeatureFile.php b/app/Models/FeatureFile.php new file mode 100644 index 0000000000..3663a6a85a --- /dev/null +++ b/app/Models/FeatureFile.php @@ -0,0 +1,25 @@ +belongsTo(Feature::class, 'feature_id'); + } +} diff --git a/config/livewire.php b/config/livewire.php new file mode 100644 index 0000000000..c57b8388b0 --- /dev/null +++ b/config/livewire.php @@ -0,0 +1,159 @@ + 'App\\Livewire', + + /* + |--------------------------------------------------------------------------- + | View Path + |--------------------------------------------------------------------------- + | + | This value is used to specify where Livewire component Blade templates are + | stored when running file creation commands like `artisan make:livewire`. + | It is also used if you choose to omit a component's render() method. + | + */ + + 'view_path' => resource_path('views/livewire'), + + /* + |--------------------------------------------------------------------------- + | Layout + |--------------------------------------------------------------------------- + | The view that will be used as the layout when rendering a single component + | as an entire page via `Route::get('/post/create', CreatePost::class);`. + | In this case, the view returned by CreatePost will render into $slot. + | + */ + + 'layout' => 'components.layouts.app', + + /* + |--------------------------------------------------------------------------- + | Lazy Loading Placeholder + |--------------------------------------------------------------------------- + | Livewire allows you to lazy load components that would otherwise slow down + | the initial page load. Every component can have a custom placeholder or + | you can define the default placeholder view for all components below. + | + */ + + 'lazy_placeholder' => null, + + /* + |--------------------------------------------------------------------------- + | Temporary File Uploads + |--------------------------------------------------------------------------- + | + | Livewire handles file uploads by storing uploads in a temporary directory + | before the file is stored permanently. All file uploads are directed to + | a global endpoint for temporary storage. You may configure this below: + | + */ + + 'temporary_file_upload' => [ + 'disk' => 'local', // Example: 'local', 's3' | Default: 'default' + 'rules' => null, // Example: ['file', 'mimes:png,jpg'] | Default: ['required', 'file', 'max:12288'] (12MB) + 'directory' => null, // Example: 'tmp' | Default: 'livewire-tmp' + 'middleware' => null, // Example: 'throttle:5,1' | Default: 'throttle:60,1' + 'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs... + 'png', 'gif', 'bmp', 'svg', 'wav', 'mp4', + 'mov', 'avi', 'wmv', 'mp3', 'm4a', + 'jpg', 'jpeg', 'mpga', 'webp', 'wma', + ], + 'max_upload_time' => 5, // Max duration (in minutes) before an upload is invalidated... + ], + + /* + |--------------------------------------------------------------------------- + | Render On Redirect + |--------------------------------------------------------------------------- + | + | This value determines if Livewire will run a component's `render()` method + | after a redirect has been triggered using something like `redirect(...)` + | Setting this to true will render the view once more before redirecting + | + */ + + 'render_on_redirect' => false, + + /* + |--------------------------------------------------------------------------- + | Eloquent Model Binding + |--------------------------------------------------------------------------- + | + | Previous versions of Livewire supported binding directly to eloquent model + | properties using wire:model by default. However, this behavior has been + | deemed too "magical" and has therefore been put under a feature flag. + | + */ + + 'legacy_model_binding' => false, + + /* + |--------------------------------------------------------------------------- + | Auto-inject Frontend Assets + |--------------------------------------------------------------------------- + | + | By default, Livewire automatically injects its JavaScript and CSS into the + | and of pages containing Livewire components. By disabling + | this behavior, you need to use @livewireStyles and @livewireScripts. + | + */ + + 'inject_assets' => true, + + /* + |--------------------------------------------------------------------------- + | Navigate (SPA mode) + |--------------------------------------------------------------------------- + | + | By adding `wire:navigate` to links in your Livewire application, Livewire + | will prevent the default link handling and instead request those pages + | via AJAX, creating an SPA-like effect. Configure this behavior here. + | + */ + + 'navigate' => [ + 'show_progress_bar' => true, + 'progress_bar_color' => '#2299dd', + ], + + /* + |--------------------------------------------------------------------------- + | HTML Morph Markers + |--------------------------------------------------------------------------- + | + | Livewire intelligently "morphs" existing HTML into the newly rendered HTML + | after each update. To make this process more reliable, Livewire injects + | "markers" into the rendered Blade surrounding @if, @class & @foreach. + | + */ + + 'inject_morph_markers' => true, + + /* + |--------------------------------------------------------------------------- + | Pagination Theme + |--------------------------------------------------------------------------- + | + | When enabling Livewire's pagination feature by using the `WithPagination` + | trait, Livewire will use Tailwind templates to render pagination views + | on the page. If you want Bootstrap CSS, you can specify: "bootstrap" + | + */ + + 'pagination_theme' => 'tailwind', +]; diff --git a/database/migrations/2024_02_06_162941_create_feature_files.php b/database/migrations/2024_02_06_162941_create_feature_files.php new file mode 100644 index 0000000000..437de7f942 --- /dev/null +++ b/database/migrations/2024_02_06_162941_create_feature_files.php @@ -0,0 +1,31 @@ +id(); + $table->unsignedBigInteger('feature_id'); + $table->string('path')->nullable(); + $table->timestamps(); + + $table->foreign('feature_id')->references('id')->on('features')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('feature_files'); + } +}; diff --git a/resources/views/livewire/roadmap/form.blade.php b/resources/views/livewire/roadmap/form.blade.php index f861ffe3d9..adc2efbb61 100644 --- a/resources/views/livewire/roadmap/form.blade.php +++ b/resources/views/livewire/roadmap/form.blade.php @@ -44,6 +44,13 @@ +
+ +
+ @error('file') {{ $message }} @enderror +
+
+

Once reviewed, your idea will show up in the ideas section. If we have questions, we'll contact you on the Discord.

From 27bcb19c65722d644b19d51602478d3693482ebc Mon Sep 17 00:00:00 2001 From: spitfire305 Date: Tue, 6 Feb 2024 21:50:41 +0000 Subject: [PATCH 2/2] Fix styling --- app/Jobs/Discord/SendNewFeature.php | 1 - app/Livewire/Roadmap/Form.php | 2 +- app/Models/Image.php | 3 +-- database/migrations/2024_02_06_162941_create_feature_files.php | 3 +-- routes/campaigns/search.php | 1 - 5 files changed, 3 insertions(+), 7 deletions(-) diff --git a/app/Jobs/Discord/SendNewFeature.php b/app/Jobs/Discord/SendNewFeature.php index 4613644ae5..42dbb46efd 100644 --- a/app/Jobs/Discord/SendNewFeature.php +++ b/app/Jobs/Discord/SendNewFeature.php @@ -34,7 +34,6 @@ public function __construct(int $feature) /** * Execute the job. * - * @return void */ public function handle(): void { diff --git a/app/Livewire/Roadmap/Form.php b/app/Livewire/Roadmap/Form.php index aeaa16e9d3..826f97bb0c 100644 --- a/app/Livewire/Roadmap/Form.php +++ b/app/Livewire/Roadmap/Form.php @@ -12,7 +12,7 @@ class Form extends Component { use WithFileUploads; - + #[Validate('image|nullable|max:3072')] // 3MB Max public $file; diff --git a/app/Models/Image.php b/app/Models/Image.php index b7e72e166b..b11f26a9c0 100644 --- a/app/Models/Image.php +++ b/app/Models/Image.php @@ -71,7 +71,7 @@ class Image extends Model ]; /** - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + * @return BelongsTo */ public function user() { @@ -79,7 +79,6 @@ public function user() } /** - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function campaign(): BelongsTo { diff --git a/database/migrations/2024_02_06_162941_create_feature_files.php b/database/migrations/2024_02_06_162941_create_feature_files.php index 437de7f942..cd85be9d5d 100644 --- a/database/migrations/2024_02_06_162941_create_feature_files.php +++ b/database/migrations/2024_02_06_162941_create_feature_files.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class () extends Migration { /** * Run the migrations. */ diff --git a/routes/campaigns/search.php b/routes/campaigns/search.php index 698ced9626..5bf8c48393 100644 --- a/routes/campaigns/search.php +++ b/routes/campaigns/search.php @@ -46,4 +46,3 @@ Route::get('/w/{campaign}/search/live', [App\Http\Controllers\Search\LiveController::class, 'index'])->name('search.live'); Route::get('/w/{campaign}/search/recent', [App\Http\Controllers\Search\RecentController::class, 'index'])->name('search.recent'); -