From 6215e4c4a4d6978c28a510bf900ef631303b7e11 Mon Sep 17 00:00:00 2001 From: Micah Shackelford Date: Sat, 27 Jul 2024 15:38:32 -0500 Subject: [PATCH 01/47] WIP: new logo, new layout (both authenticated and guest), feed, whole screen upload, better upload flow (model based + ability to set title/settings/add more images, collection or single uploads) feature flagging progress --- app/Data/ShotData.php | 42 + app/Data/UserData.php | 19 + app/Enums/ShotType.php | 10 + app/Http/Controllers/FeedController.php | 17 + app/Http/Controllers/ShotController.php | 51 +- app/Http/Controllers/UploadController.php | 80 +- app/Http/Controllers/UserController.php | 41 + app/Http/Requests/UploadRequest.php | 7 + app/Models/Shot.php | 37 +- app/Models/ShotUpload.php | 32 + app/Models/User.php | 12 + app/Models/UserFollower.php | 15 + composer.json | 1 + composer.lock | 1937 ++++++++++++++--- config/features.php | 5 + database/factories/ShotUploadFactory.php | 23 + ...07_27_172851_create_shot_uploads_table.php | 38 + ...00_update_shots_table_drop_path_column.php | 28 + ...301_update_shots_table_add_type_column.php | 40 + ...8_update_users_table_add_handle_column.php | 58 + ..._27_200353_create_user_followers_table.php | 42 + resources/js/Components/ApplicationLogo.vue | 42 +- resources/js/Components/CardShot.vue | 53 + resources/js/Components/User.vue | 19 + .../Components/ui/radio-group/RadioGroup.vue | 34 + .../ui/radio-group/RadioGroupItem.vue | 45 + .../js/Components/ui/radio-group/index.js | 2 + resources/js/Layouts/GuestLayout.vue | 19 +- resources/js/Layouts/Layout.vue | 331 ++- resources/js/Pages/Auth/ForgotPassword.vue | 61 +- resources/js/Pages/Auth/Login.vue | 99 +- resources/js/Pages/Auth/Register.vue | 128 +- resources/js/Pages/Feed.vue | 220 ++ resources/js/Pages/Home.vue | 102 - resources/js/Pages/Shots/Show.vue | 115 +- resources/js/Pages/Users/Show.vue | 78 + resources/views/app.blade.php | 14 - routes/web.php | 18 +- 38 files changed, 3248 insertions(+), 667 deletions(-) create mode 100644 app/Data/ShotData.php create mode 100644 app/Data/UserData.php create mode 100644 app/Enums/ShotType.php create mode 100644 app/Http/Controllers/FeedController.php create mode 100644 app/Http/Controllers/UserController.php create mode 100644 app/Models/ShotUpload.php create mode 100644 app/Models/UserFollower.php create mode 100644 database/factories/ShotUploadFactory.php create mode 100644 database/migrations/2024_07_27_172851_create_shot_uploads_table.php create mode 100644 database/migrations/2024_07_27_173300_update_shots_table_drop_path_column.php create mode 100644 database/migrations/2024_07_27_173301_update_shots_table_add_type_column.php create mode 100644 database/migrations/2024_07_27_192208_update_users_table_add_handle_column.php create mode 100644 database/migrations/2024_07_27_200353_create_user_followers_table.php create mode 100644 resources/js/Components/CardShot.vue create mode 100644 resources/js/Components/User.vue create mode 100644 resources/js/Components/ui/radio-group/RadioGroup.vue create mode 100644 resources/js/Components/ui/radio-group/RadioGroupItem.vue create mode 100644 resources/js/Components/ui/radio-group/index.js create mode 100644 resources/js/Pages/Feed.vue delete mode 100644 resources/js/Pages/Home.vue create mode 100644 resources/js/Pages/Users/Show.vue diff --git a/app/Data/ShotData.php b/app/Data/ShotData.php new file mode 100644 index 0000000..3566d18 --- /dev/null +++ b/app/Data/ShotData.php @@ -0,0 +1,42 @@ +loadMissing([ + 'user', + 'uploads', + ]); + + return new static( + $shot->publicIdentifier, + $shot->name, + $shot->type, + $shot->created_at, + $shot->updated_at, + $shot->uploads->pluck("url")->toArray(), + $shot->anonymize + ? null + : UserData::from($shot->user), + ); + } +} diff --git a/app/Data/UserData.php b/app/Data/UserData.php new file mode 100644 index 0000000..2a0e9e2 --- /dev/null +++ b/app/Data/UserData.php @@ -0,0 +1,19 @@ +handle = Str::start($this->handle, "@"); + } +} diff --git a/app/Enums/ShotType.php b/app/Enums/ShotType.php new file mode 100644 index 0000000..ace55eb --- /dev/null +++ b/app/Enums/ShotType.php @@ -0,0 +1,10 @@ +firstOrFail(); - - if ($shot->parent_shot_id) { - $parentShotId = config('features.uuid_routes') - // TODO: Don't load parent (perhaps migrate parent_shot_id to be either uuid or id pending settings) - ? $shot->parentShot->uuid - : $shot->parent_shot_id; - - return to_route('shots.show', [ - 'id' => $parentShotId, - 'selected_shot_id' => $shot->getKey(), - ]); - } + $shot = Shot::with(['user', 'uploads']) + ->wherePublicIdentifier($id) + ->firstOrFail(); + + // if ($shot->parent_shot_id) { + // $parentShotId = config('features.uuid_routes') + // // TODO: Don't load parent (perhaps migrate parent_shot_id to be either uuid or id pending settings) + // ? $shot->parentShot->uuid + // : $shot->parent_shot_id; + + // return to_route('shots.show', [ + // 'id' => $parentShotId, + // 'selected_shot_id' => $shot->getKey(), + // ]); + // } if ($shot->require_logged_in && ! $request->user()) { abort(404); } return Inertia::render('Shots/Show', [ - 'shot' => fn () => $shot->fresh(), - 'childShots' => fn () => Shot::whereParentShotId($shot->getKey())->get(), - 'author' => fn () => $shot->anonymize ? null : $shot->user->only(['id', 'name']), - 'reaction' => fn () => $request->user()?->reactions()->whereShotId($shot->getKey())->first(), - 'reactionCounts' => fn () => ShotReaction::whereShotId($shot->getKey()) - ->select('reaction', DB::raw('count(*) as count')) - ->groupBy('reaction') - ->get() - ->mapWithKeys(fn ($result) => [$result['reaction'] => $result['count']]), - 'showLinks' => config('shots.links'), - 'isOwner' => $shot->user_id == $request->user()?->getKey(), + 'shot' => fn () => ShotData::fromModel($shot), + // 'author' => fn () => $shot->anonymize ? null : $shot->user->only(['id', 'name']), + // 'childShots' => fn () => Shot::whereParentShotId($shot->getKey())->get(), + // 'reaction' => fn () => $request->user()?->reactions()->whereShotId($shot->getKey())->first(), + // 'reactionCounts' => fn () => ShotReaction::whereShotId($shot->getKey()) + // ->select('reaction', DB::raw('count(*) as count')) + // ->groupBy('reaction') + // ->get() + // ->mapWithKeys(fn ($result) => [$result['reaction'] => $result['count']]), + // 'showLinks' => config('shots.links'), + // 'isOwner' => $shot->user_id == $request->user()?->getKey(), ]); } diff --git a/app/Http/Controllers/UploadController.php b/app/Http/Controllers/UploadController.php index dd5264c..091bc41 100644 --- a/app/Http/Controllers/UploadController.php +++ b/app/Http/Controllers/UploadController.php @@ -2,38 +2,82 @@ namespace App\Http\Controllers; +use App\Enums\ShotType; use App\Http\Requests\UploadRequest; +use App\Models\Shot; use Illuminate\Http\UploadedFile; class UploadController extends Controller { public function __invoke(UploadRequest $request) { - $parentShot = null; + // TODO: Automated testing for ShareX + Shutter integrations - foreach ($request->files->get('images') as $i => $image) { - $path = UploadedFile::createFromBase($image)->storePublicly('uploads'); + $type = ShotType::tryFrom($request->get('type')) ?? ShotType::Individual; - $shot = $request->user()->shots()->create([ - 'path' => $path, - 'parent_shot_id' => $parentShot?->getKey(), - ]); + $returnShot = null; - if ($i === 0) { - $parentShot = $shot; + if($type === ShotType::Individual) { + foreach($request->files->get('images') as $image) { + $shot = $request->user()->shots()->create(array_merge(['type' => $type], $request->only([ + 'title', + 'require_logged_in', + 'anonymize', + ]))); + + // return the first shot we create + $returnShot ??= $shot; + + $shot->uploads()->create([ + 'path' => UploadedFile::createFromBase($image)->storePublicly('uploads') + ]); } - } + } else { + $shot = $request->user()->shots()->create(array_merge(['type' => $type], $request->only([ + 'title', + 'require_logged_in', + 'anonymize', + ]))); - $id = $parentShot->publicIdentifier; + // return the first shot we create + $returnShot ??= $shot; - if ($request->expectsJson()) { - return response()->json([ - 'data' => [ - 'link' => route('shots.show', $id), - ], - ]); + foreach($request->files->get('images') as $image) { + $shot->uploads()->create([ + 'path' => UploadedFile::createFromBase($image)->storePublicly('uploads') + ]); + } } - return to_route('shots.show', $id); + // foreach ($request->files->get('images') as $image) { + + // } + + // $parentShot = null; + + // foreach ($request->files->get('images') as $i => $image) { + // $path = UploadedFile::createFromBase($image)->storePublicly('uploads'); + + // $shot = $request->user()->shots()->create([ + // 'path' => $path, + // 'parent_shot_id' => $parentShot?->getKey(), + // ]); + + // if ($i === 0) { + // $parentShot = $shot; + // } + // } + + // $id = $parentShot->publicIdentifier; + + // if ($request->expectsJson()) { + // return response()->json([ + // 'data' => [ + // 'link' => route('shots.show', $id), + // ], + // ]); + // } + + return to_route('shots.show', $returnShot->publicIdentifier); } } diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php new file mode 100644 index 0000000..ca1f7f3 --- /dev/null +++ b/app/Http/Controllers/UserController.php @@ -0,0 +1,41 @@ +firstOrFail(); + + return Inertia::render("Users/Show", [ + 'user' => UserData::from($user), + 'shots' => ShotData::collect($user->shots() + ->orderByDesc("created_at") + ->where("anonymize", false) + ->cursorPaginate()), + 'isFollowing' => $user->followers()->whereFollowerId($request->user()->getKey())->count() > 0 + ]); + } + + public function follow(Request $request, string $handle) + { + $user = User::whereHandle($handle)->firstOrFail(); + + $user->followers()->attach([$request->user()->getKey()]); + } + + public function unfollow(Request $request, string $handle) + { + $user = User::whereHandle($handle)->firstOrFail(); + + $user->followers()->detach([$request->user()->getKey()]); + } +} diff --git a/app/Http/Requests/UploadRequest.php b/app/Http/Requests/UploadRequest.php index 928a3a3..5769ca5 100644 --- a/app/Http/Requests/UploadRequest.php +++ b/app/Http/Requests/UploadRequest.php @@ -2,7 +2,9 @@ namespace App\Http\Requests; +use App\Enums\ShotType; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Rule; class UploadRequest extends FormRequest { @@ -24,6 +26,11 @@ public function rules(): array return [ 'images' => ['array', 'min:1'], 'images.*' => ['mimes:'.implode(',', $this->allowedTypes)], + + 'title' => ['string', 'sometimes', 'nullable'], + 'type' => [Rule::enum(ShotType::class), 'sometimes', 'nullable'], + 'require_logged_in' => ['boolean', 'sometimes', 'nullable'], + 'anonymize' => ['boolean', 'sometimes', 'nullable'], ]; } diff --git a/app/Models/Shot.php b/app/Models/Shot.php index 2f23530..3a36307 100644 --- a/app/Models/Shot.php +++ b/app/Models/Shot.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Enums\ShotType; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -15,12 +16,12 @@ * @property string $uuid * @property int $user_id * @property string $name - * @property string $path + * @property ShotType $type * @property int $parent_shot_id * @property bool $require_logged_in * @property bool $anonymize - * @property Carbon $created_at - * @property Carbon $updated_at + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at */ class Shot extends Model { @@ -32,10 +33,15 @@ class Shot extends Model ]; protected $appends = [ - 'links', + // 'links', + ]; + + protected $attributes = [ + 'type' => ShotType::Individual, ]; protected $casts = [ + 'type' => ShotType::class, 'require_logged_in' => 'bool', 'anonymize' => 'bool', ]; @@ -49,15 +55,15 @@ protected static function booted(): void }); } - protected function links(): Attribute - { - return Attribute::make( - get: fn ($_, array $attributes) => [ - 'url' => route('shots.show', $this->publicIdentifier), - 'asset_url' => asset($attributes['path']), - ], - ); - } + // protected function links(): Attribute + // { + // return Attribute::make( + // get: fn ($_, array $attributes) => [ + // 'url' => route('shots.show', $this->publicIdentifier), + // 'asset_url' => asset($attributes['path']), + // ], + // ); + // } protected function publicIdentifier(): Attribute { @@ -82,6 +88,11 @@ public function user(): BelongsTo return $this->belongsTo(User::class); } + public function uploads() + { + return $this->hasMany(ShotUpload::class); + } + public function childShots(): HasMany { return $this->hasMany(Shot::class, 'parent_shot_id'); diff --git a/app/Models/ShotUpload.php b/app/Models/ShotUpload.php new file mode 100644 index 0000000..a40e8f0 --- /dev/null +++ b/app/Models/ShotUpload.php @@ -0,0 +1,32 @@ + asset($this->path), + ); + } + + public function shot() + { + return $this->belongsTo(Shot::class); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index a76ecb9..f6e7b13 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -12,6 +12,7 @@ use Illuminate\Support\Str; use Laravel\Sanctum\HasApiTokens; use Laravel\Sanctum\NewAccessToken; +use Illuminate\Database\Eloquent\Builder; class User extends Authenticatable { @@ -24,6 +25,7 @@ class User extends Authenticatable */ protected $fillable = [ 'name', + 'handle', 'email', 'password', ]; @@ -72,4 +74,14 @@ public function reactions(): HasMany { return $this->hasMany(ShotReaction::class); } + + public function followers() + { + return $this->belongsToMany(User::class, UserFollower::class, "user_id", "follower_id"); + } + + public function scopeWhereHandle(Builder $query, string $handle) + { + $query->where("handle", Str::ltrim($handle, '@')); + } } diff --git a/app/Models/UserFollower.php b/app/Models/UserFollower.php new file mode 100644 index 0000000..29a9b84 --- /dev/null +++ b/app/Models/UserFollower.php @@ -0,0 +1,15 @@ +=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Future/functions.php", + "src/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v3.0.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-05-10T21:37:46+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "daa00f2efdbd71565bf64ffefa89e37542addf93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/daa00f2efdbd71565bf64ffefa89e37542addf93", + "reference": "daa00f2efdbd71565bf64ffefa89e37542addf93", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/parser": "^1.1", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2.3" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.22.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php" + ], + "psr-4": { + "Amp\\ByteStream\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "https://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v2.1.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-02-17T04:49:38+00:00" + }, + { + "name": "amphp/cache", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/cache.git", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/cache/zipball/46912e387e6aa94933b61ea1ead9cf7540b7797c", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Cache\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + } + ], + "description": "A fiber-aware cache API based on Amp and Revolt.", + "homepage": "https://amphp.org/cache", + "support": { + "issues": "https://github.com/amphp/cache/issues", + "source": "https://github.com/amphp/cache/tree/v2.0.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-19T03:38:06+00:00" + }, + { + "name": "amphp/dns", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/dns.git", + "reference": "758266b0ea7470e2e42cd098493bc6d6c7100cf7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/dns/zipball/758266b0ea7470e2e42cd098493bc6d6c7100cf7", + "reference": "758266b0ea7470e2e42cd098493bc6d6c7100cf7", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parser": "^1", + "amphp/windows-registry": "^1.0.1", + "daverandom/libdns": "^2.0.2", + "ext-filter": "*", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.20" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Dns\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Wright", + "email": "addr@daverandom.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "Async DNS resolution for Amp.", + "homepage": "https://github.com/amphp/dns", + "keywords": [ + "amp", + "amphp", + "async", + "client", + "dns", + "resolve" + ], + "support": { + "issues": "https://github.com/amphp/dns/issues", + "source": "https://github.com/amphp/dns/tree/v2.2.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-06-02T19:54:12+00:00" + }, + { + "name": "amphp/parallel", + "version": "v2.2.9", + "source": { + "type": "git", + "url": "https://github.com/amphp/parallel.git", + "reference": "73d293f1fc4df1bebc3c4fce1432e82dd7032238" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parallel/zipball/73d293f1fc4df1bebc3c4fce1432e82dd7032238", + "reference": "73d293f1fc4df1bebc3c4fce1432e82dd7032238", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parser": "^1", + "amphp/pipeline": "^1", + "amphp/process": "^2", + "amphp/serialization": "^1", + "amphp/socket": "^2", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.18" + }, + "type": "library", + "autoload": { + "files": [ + "src/Context/functions.php", + "src/Context/Internal/functions.php", + "src/Ipc/functions.php", + "src/Worker/functions.php" + ], + "psr-4": { + "Amp\\Parallel\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Parallel processing component for Amp.", + "homepage": "https://github.com/amphp/parallel", + "keywords": [ + "async", + "asynchronous", + "concurrent", + "multi-processing", + "multi-threading" + ], + "support": { + "issues": "https://github.com/amphp/parallel/issues", + "source": "https://github.com/amphp/parallel/tree/v2.2.9" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-24T18:27:44+00:00" + }, + { + "name": "amphp/parser", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/parser.git", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parser/zipball/3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Parser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A generator parser to make streaming parsers simple.", + "homepage": "https://github.com/amphp/parser", + "keywords": [ + "async", + "non-blocking", + "parser", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/parser/issues", + "source": "https://github.com/amphp/parser/tree/v1.1.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-21T19:16:53+00:00" + }, + { + "name": "amphp/pipeline", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/pipeline.git", + "reference": "66c095673aa5b6e689e63b52d19e577459129ab3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/pipeline/zipball/66c095673aa5b6e689e63b52d19e577459129ab3", + "reference": "66c095673aa5b6e689e63b52d19e577459129ab3", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.18" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Pipeline\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Asynchronous iterators and operators.", + "homepage": "https://amphp.org/pipeline", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "iterator", + "non-blocking" + ], + "support": { + "issues": "https://github.com/amphp/pipeline/issues", + "source": "https://github.com/amphp/pipeline/tree/v1.2.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-07-04T00:56:47+00:00" + }, + { + "name": "amphp/process", + "version": "v2.0.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/process.git", + "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/process/zipball/52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d", + "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Process\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A fiber-aware process manager based on Amp and Revolt.", + "homepage": "https://amphp.org/process", + "support": { + "issues": "https://github.com/amphp/process/issues", + "source": "https://github.com/amphp/process/tree/v2.0.3" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-19T03:13:44+00:00" + }, + { + "name": "amphp/serialization", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/serialization.git", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/serialization/zipball/693e77b2fb0b266c3c7d622317f881de44ae94a1", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "phpunit/phpunit": "^9 || ^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Serialization\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Serialization tools for IPC and data storage in PHP.", + "homepage": "https://github.com/amphp/serialization", + "keywords": [ + "async", + "asynchronous", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/amphp/serialization/issues", + "source": "https://github.com/amphp/serialization/tree/master" + }, + "time": "2020-03-25T21:39:07+00:00" + }, + { + "name": "amphp/socket", + "version": "v2.3.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/socket.git", + "reference": "58e0422221825b79681b72c50c47a930be7bf1e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/socket/zipball/58e0422221825b79681b72c50c47a930be7bf1e1", + "reference": "58e0422221825b79681b72c50c47a930be7bf1e1", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/dns": "^2", + "ext-openssl": "*", + "kelunik/certificate": "^1.1", + "league/uri": "^6.5 | ^7", + "league/uri-interfaces": "^2.3 | ^7", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "amphp/process": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "5.20" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php", + "src/SocketAddress/functions.php" + ], + "psr-4": { + "Amp\\Socket\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@gmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Non-blocking socket connection / server implementations based on Amp and Revolt.", + "homepage": "https://github.com/amphp/socket", + "keywords": [ + "amp", + "async", + "encryption", + "non-blocking", + "sockets", + "tcp", + "tls" + ], + "support": { + "issues": "https://github.com/amphp/socket/issues", + "source": "https://github.com/amphp/socket/tree/v2.3.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-21T14:33:03+00:00" + }, + { + "name": "amphp/sync", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/sync.git", + "reference": "375ef5b54a0d12c38e12728dde05a55e30f2fbec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/sync/zipball/375ef5b54a0d12c38e12728dde05a55e30f2fbec", + "reference": "375ef5b54a0d12c38e12728dde05a55e30f2fbec", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Sync\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Non-blocking synchronization primitives for PHP based on Amp and Revolt.", + "homepage": "https://github.com/amphp/sync", + "keywords": [ + "async", + "asynchronous", + "mutex", + "semaphore", + "synchronization" + ], + "support": { + "issues": "https://github.com/amphp/sync/issues", + "source": "https://github.com/amphp/sync/tree/v2.2.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-12T01:00:01+00:00" + }, + { + "name": "amphp/windows-registry", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/windows-registry.git", + "reference": "0d569e8f256cca974e3842b6e78b4e434bf98306" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/windows-registry/zipball/0d569e8f256cca974e3842b6e78b4e434bf98306", + "reference": "0d569e8f256cca974e3842b6e78b4e434bf98306", + "shasum": "" + }, + "require": { + "amphp/byte-stream": "^2", + "amphp/process": "^2", + "php": ">=8.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\WindowsRegistry\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Windows Registry Reader.", + "support": { + "issues": "https://github.com/amphp/windows-registry/issues", + "source": "https://github.com/amphp/windows-registry/tree/v1.0.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-01-30T23:01:51+00:00" + }, { "name": "brick/math", "version": "0.11.0", @@ -130,6 +989,50 @@ ], "time": "2024-02-09T16:56:22+00:00" }, + { + "name": "daverandom/libdns", + "version": "v2.1.0", + "source": { + "type": "git", + "url": "https://github.com/DaveRandom/LibDNS.git", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "Required for IDN support" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "LibDNS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "DNS protocol implementation written in pure PHP", + "keywords": [ + "dns" + ], + "support": { + "issues": "https://github.com/DaveRandom/LibDNS/issues", + "source": "https://github.com/DaveRandom/LibDNS/tree/v2.1.0" + }, + "time": "2024-04-12T12:12:48+00:00" + }, { "name": "dflydev/dot-access-data", "version": "v3.0.2", @@ -191,19 +1094,66 @@ "homepage": "https://www.colinodell.com" } ], - "description": "Given a deep data structure, access data by dot notation.", - "homepage": "https://github.com/dflydev/dflydev-dot-access-data", - "keywords": [ - "access", - "data", - "dot", - "notation" - ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2" + }, + "time": "2022-10-27T11:44:00+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", "support": { - "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", - "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2" + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" }, - "time": "2022-10-27T11:44:00+00:00" + "time": "2024-01-30T19:34:25+00:00" }, { "name": "doctrine/inflector", @@ -1120,6 +2070,64 @@ ], "time": "2024-03-09T00:30:58+00:00" }, + { + "name": "kelunik/certificate", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/kelunik/certificate.git", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kelunik/certificate/zipball/7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "php": ">=7.0" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^6 | 7 | ^8 | ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Kelunik\\Certificate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Access certificate details and transform between different formats.", + "keywords": [ + "DER", + "certificate", + "certificates", + "openssl", + "pem", + "x509" + ], + "support": { + "issues": "https://github.com/kelunik/certificate/issues", + "source": "https://github.com/kelunik/certificate/tree/v1.1.3" + }, + "time": "2023-02-03T21:26:53+00:00" + }, { "name": "laravel/framework", "version": "v11.4.0", @@ -1965,6 +2973,180 @@ ], "time": "2024-01-28T23:22:08+00:00" }, + { + "name": "league/uri", + "version": "7.4.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "bedb6e55eff0c933668addaa7efa1e1f2c417cc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/bedb6e55eff0c933668addaa7efa1e1f2c417cc4", + "reference": "bedb6e55eff0c933668addaa7efa1e1f2c417cc4", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.3", + "php": "^8.1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "league/uri-components": "Needed to easily manipulate URI objects components", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.4.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-03-23T07:42:40+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.4.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "8d43ef5c841032c87e2de015972c06f3865ef718" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/8d43ef5c841032c87e2de015972c06f3865ef718", + "reference": "8d43ef5c841032c87e2de015972c06f3865ef718", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-factory": "^1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common interfaces and classes for URI representation and interaction", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.4.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-03-23T07:42:40+00:00" + }, { "name": "monolog/monolog", "version": "3.6.0", @@ -2450,21 +3632,132 @@ "issues": "https://github.com/nunomaduro/termwind/issues", "source": "https://github.com/nunomaduro/termwind/tree/v2.0.1" }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2024-03-06T16:17:14+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "153ae662783729388a584b4361f2545e4d841e3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", + "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://github.com/xiCO2k", - "type": "github" + "name": "Mike van Riel", + "email": "me@mikevanriel.com" } ], - "time": "2024-03-06T16:17:14+00:00" + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + }, + "time": "2024-02-23T11:10:43+00:00" }, { "name": "phpoption/phpoption", @@ -2541,6 +3834,53 @@ ], "time": "2023-11-12T21:59:55+00:00" }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.28.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb", + "reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.28.0" + }, + "time": "2024-04-03T18:51:33+00:00" + }, { "name": "psr/clock", "version": "1.0.0", @@ -3210,52 +4550,348 @@ "squizlabs/php_codesniffer": "^3.5", "vimeo/psalm": "^4.9" }, - "suggest": { - "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", - "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", - "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", - "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", - "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.7.5" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", + "type": "tidelift" + } + ], + "time": "2023-11-08T05:53:05+00:00" + }, + { + "name": "revolt/event-loop", + "version": "v1.0.6", + "source": { + "type": "git", + "url": "https://github.com/revoltphp/event-loop.git", + "reference": "25de49af7223ba039f64da4ae9a28ec2d10d0254" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/25de49af7223ba039f64da4ae9a28ec2d10d0254", + "reference": "25de49af7223ba039f64da4ae9a28ec2d10d0254", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.15" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Revolt\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Rock-solid event loop for concurrent PHP applications.", + "keywords": [ + "async", + "asynchronous", + "concurrency", + "event", + "event-loop", + "non-blocking", + "scheduler" + ], + "support": { + "issues": "https://github.com/revoltphp/event-loop/issues", + "source": "https://github.com/revoltphp/event-loop/tree/v1.0.6" + }, + "time": "2023-11-30T05:34:44+00:00" + }, + { + "name": "spatie/laravel-data", + "version": "4.7.2", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-data.git", + "reference": "8980ee53f03721428c21b0b32ef662f85b277403" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-data/zipball/8980ee53f03721428c21b0b32ef662f85b277403", + "reference": "8980ee53f03721428c21b0b32ef662f85b277403", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^10.0|^11.0", + "php": "^8.1", + "phpdocumentor/type-resolver": "^1.5", + "spatie/laravel-package-tools": "^1.9.0", + "spatie/php-structure-discoverer": "^2.0" + }, + "require-dev": { + "fakerphp/faker": "^1.14", + "friendsofphp/php-cs-fixer": "^3.0", + "inertiajs/inertia-laravel": "^1.2", + "livewire/livewire": "^3.0", + "mockery/mockery": "^1.6", + "nesbot/carbon": "^2.63", + "nunomaduro/larastan": "^2.0", + "orchestra/testbench": "^8.0|^9.0", + "pestphp/pest": "^2.31", + "pestphp/pest-plugin-laravel": "^2.0", + "pestphp/pest-plugin-livewire": "^2.1", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpunit/phpunit": "^10.0", + "spatie/invade": "^1.0", + "spatie/laravel-typescript-transformer": "^2.3", + "spatie/pest-plugin-snapshots": "^2.1", + "spatie/test-time": "^1.2" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\LaravelData\\LaravelDataServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\LaravelData\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ruben Van Assche", + "email": "ruben@spatie.be", + "role": "Developer" + } + ], + "description": "Create unified resources and data transfer objects", + "homepage": "https://github.com/spatie/laravel-data", + "keywords": [ + "laravel", + "laravel-data", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-data/issues", + "source": "https://github.com/spatie/laravel-data/tree/4.7.2" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2024-07-25T11:17:44+00:00" + }, + { + "name": "spatie/laravel-package-tools", + "version": "1.16.4", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-package-tools.git", + "reference": "ddf678e78d7f8b17e5cdd99c0c3413a4a6592e53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/ddf678e78d7f8b17e5cdd99c0c3413a4a6592e53", + "reference": "ddf678e78d7f8b17e5cdd99c0c3413a4a6592e53", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^9.28|^10.0|^11.0", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "orchestra/testbench": "^7.7|^8.0", + "pestphp/pest": "^1.22", + "phpunit/phpunit": "^9.5.24", + "spatie/pest-plugin-test-time": "^1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\LaravelPackageTools\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "description": "Tools for creating Laravel packages", + "homepage": "https://github.com/spatie/laravel-package-tools", + "keywords": [ + "laravel-package-tools", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-package-tools/issues", + "source": "https://github.com/spatie/laravel-package-tools/tree/1.16.4" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2024-03-20T07:29:11+00:00" + }, + { + "name": "spatie/php-structure-discoverer", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/php-structure-discoverer.git", + "reference": "24f5221641560ec0f7dce23dd814e7d555b0098b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/php-structure-discoverer/zipball/24f5221641560ec0f7dce23dd814e7d555b0098b", + "reference": "24f5221641560ec0f7dce23dd814e7d555b0098b", + "shasum": "" + }, + "require": { + "amphp/amp": "^v3.0", + "amphp/parallel": "^2.2", + "illuminate/collections": "^10.0|^11.0", + "php": "^8.1", + "spatie/laravel-package-tools": "^1.4.3", + "symfony/finder": "^6.0|^7.0" + }, + "require-dev": { + "illuminate/console": "^10.0|^11.0", + "laravel/pint": "^1.0", + "nunomaduro/collision": "^7.0|^8.0", + "nunomaduro/larastan": "^2.0.1", + "orchestra/testbench": "^7.0|^8.0|^9.0", + "pestphp/pest": "^2.0", + "pestphp/pest-plugin-laravel": "^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.5|^10.0", + "spatie/laravel-ray": "^1.26" }, "type": "library", "extra": { - "captainhook": { - "force-install": true + "laravel": { + "providers": [ + "Spatie\\StructureDiscoverer\\StructureDiscovererServiceProvider" + ] } }, "autoload": { - "files": [ - "src/functions.php" - ], "psr-4": { - "Ramsey\\Uuid\\": "src/" + "Spatie\\StructureDiscoverer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "authors": [ + { + "name": "Ruben Van Assche", + "email": "ruben@spatie.be", + "role": "Developer" + } + ], + "description": "Automatically discover structures within your PHP application", + "homepage": "https://github.com/spatie/php-structure-discoverer", "keywords": [ - "guid", - "identifier", - "uuid" + "discover", + "laravel", + "php", + "php-structure-discoverer" ], "support": { - "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.7.5" + "issues": "https://github.com/spatie/php-structure-discoverer/issues", + "source": "https://github.com/spatie/php-structure-discoverer/tree/2.1.1" }, "funding": [ { - "url": "https://github.com/ramsey", + "url": "https://github.com/LaravelAutoDiscoverer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", - "type": "tidelift" } ], - "time": "2023-11-08T05:53:05+00:00" + "time": "2024-03-13T16:08:30+00:00" }, { "name": "symfony/clock", @@ -5986,53 +7622,6 @@ ], "time": "2024-02-20T07:24:02+00:00" }, - { - "name": "doctrine/deprecations", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "1.4.10 || 1.10.15", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "0.18.4", - "psr/log": "^1 || ^2 || ^3", - "vimeo/psalm": "4.30.0 || 5.12.0" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.3" - }, - "time": "2024-01-30T19:34:25+00:00" - }, { "name": "fakerphp/faker", "version": "v1.23.1", @@ -7207,59 +8796,6 @@ }, "time": "2022-02-21T01:04:05+00:00" }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, { "name": "phpdocumentor/reflection-docblock", "version": "5.4.0", @@ -7324,111 +8860,6 @@ }, "time": "2024-04-09T21:13:58+00:00" }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.8.2", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "153ae662783729388a584b4361f2545e4d841e3c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", - "reference": "153ae662783729388a584b4361f2545e4d841e3c", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.3 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" - }, - "time": "2024-02-23T11:10:43+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "1.28.0", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb", - "reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^4.15", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.28.0" - }, - "time": "2024-04-03T18:51:33+00:00" - }, { "name": "phpstan/phpstan", "version": "1.10.67", diff --git a/config/features.php b/config/features.php index b418a2a..1a33e23 100644 --- a/config/features.php +++ b/config/features.php @@ -8,4 +8,9 @@ // TODO: Will move to uuid routes as the default in 2.0.0 'uuid_routes' => env('FEATURE_UUID_ROUTES', false), 'footer' => env('FEATURE_FOOTER', true), + + 'home' => false, + 'feed' => true, + 'explore' => true, + 'search' => true, ]; diff --git a/database/factories/ShotUploadFactory.php b/database/factories/ShotUploadFactory.php new file mode 100644 index 0000000..f045729 --- /dev/null +++ b/database/factories/ShotUploadFactory.php @@ -0,0 +1,23 @@ + + */ +class ShotUploadFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + // + ]; + } +} diff --git a/database/migrations/2024_07_27_172851_create_shot_uploads_table.php b/database/migrations/2024_07_27_172851_create_shot_uploads_table.php new file mode 100644 index 0000000..a8aa2f2 --- /dev/null +++ b/database/migrations/2024_07_27_172851_create_shot_uploads_table.php @@ -0,0 +1,38 @@ +id(); + + $table->foreignIdFor(Shot::class) + ->constrained() + ->cascadeOnDelete(); + $table->string("path"); + }); + + foreach(Shot::lazy(100) as $shot) { + $shot->uploads()->create([ + "path" => $shot->path, + ]); + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('shot_uploads'); + } +}; diff --git a/database/migrations/2024_07_27_173300_update_shots_table_drop_path_column.php b/database/migrations/2024_07_27_173300_update_shots_table_drop_path_column.php new file mode 100644 index 0000000..a98be1c --- /dev/null +++ b/database/migrations/2024_07_27_173300_update_shots_table_drop_path_column.php @@ -0,0 +1,28 @@ +dropColumn("path"); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('shots', function (Blueprint $table) { + $table->string("path")->nullable(); + }); + } +}; diff --git a/database/migrations/2024_07_27_173301_update_shots_table_add_type_column.php b/database/migrations/2024_07_27_173301_update_shots_table_add_type_column.php new file mode 100644 index 0000000..265c58c --- /dev/null +++ b/database/migrations/2024_07_27_173301_update_shots_table_add_type_column.php @@ -0,0 +1,40 @@ +string("type") + ->after("name") + ->nullable(); + }); + + Shot::whereNull("type")->update([ + "type" => ShotType::Individual + ]); + + Schema::table('shots', function (Blueprint $table) { + $table->string("type")->nullable(false)->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('shots', function (Blueprint $table) { + $table->dropColumn("type"); + }); + } +}; diff --git a/database/migrations/2024_07_27_192208_update_users_table_add_handle_column.php b/database/migrations/2024_07_27_192208_update_users_table_add_handle_column.php new file mode 100644 index 0000000..8d173b0 --- /dev/null +++ b/database/migrations/2024_07_27_192208_update_users_table_add_handle_column.php @@ -0,0 +1,58 @@ +string('handle') + ->nullable() + ->after('name') + ->unique(); + }); + + foreach (User::all() as $user) { + $this->setUserHandle($user); + } + + Schema::table('users', function (Blueprint $table) { + $table->string('handle') + ->nullable(false) + ->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('handle'); + }); + } + + protected function setUserHandle(User $user): void + { + $user->handle = Str::of($user->name) + ->lower() + ->replace(' ', '.') + ->toString(); + + // Append numbers until we are unique + while (User::whereHandle($user->handle)->count() >= 1) { + $user->handle .= rand(1, 9); + } + + $user->save(); + } +}; diff --git a/database/migrations/2024_07_27_200353_create_user_followers_table.php b/database/migrations/2024_07_27_200353_create_user_followers_table.php new file mode 100644 index 0000000..b277070 --- /dev/null +++ b/database/migrations/2024_07_27_200353_create_user_followers_table.php @@ -0,0 +1,42 @@ +id(); + + $table->foreignIdFor(User::class) + ->constrained() + ->cascadeOnDelete(); + $table->foreignIdFor(User::class, "follower_id") + ->constrained("users") + ->cascadeOnDelete(); + + $table->index(["user_id"]); + $table->index(["follower_id"]); + + $table->unique([ + "user_id", + "follower_id", + ]); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('user_followers'); + } +}; diff --git a/resources/js/Components/ApplicationLogo.vue b/resources/js/Components/ApplicationLogo.vue index d952df7..4887b63 100644 --- a/resources/js/Components/ApplicationLogo.vue +++ b/resources/js/Components/ApplicationLogo.vue @@ -1,7 +1,39 @@ + diff --git a/resources/js/Components/CardShot.vue b/resources/js/Components/CardShot.vue new file mode 100644 index 0000000..8410898 --- /dev/null +++ b/resources/js/Components/CardShot.vue @@ -0,0 +1,53 @@ + + + diff --git a/resources/js/Components/User.vue b/resources/js/Components/User.vue new file mode 100644 index 0000000..2738c1b --- /dev/null +++ b/resources/js/Components/User.vue @@ -0,0 +1,19 @@ + + diff --git a/resources/js/Components/ui/radio-group/RadioGroup.vue b/resources/js/Components/ui/radio-group/RadioGroup.vue new file mode 100644 index 0000000..252ef5f --- /dev/null +++ b/resources/js/Components/ui/radio-group/RadioGroup.vue @@ -0,0 +1,34 @@ + + + diff --git a/resources/js/Components/ui/radio-group/RadioGroupItem.vue b/resources/js/Components/ui/radio-group/RadioGroupItem.vue new file mode 100644 index 0000000..af9bb18 --- /dev/null +++ b/resources/js/Components/ui/radio-group/RadioGroupItem.vue @@ -0,0 +1,45 @@ + + + diff --git a/resources/js/Components/ui/radio-group/index.js b/resources/js/Components/ui/radio-group/index.js new file mode 100644 index 0000000..1387e43 --- /dev/null +++ b/resources/js/Components/ui/radio-group/index.js @@ -0,0 +1,2 @@ +export { default as RadioGroup } from "./RadioGroup.vue"; +export { default as RadioGroupItem } from "./RadioGroupItem.vue"; diff --git a/resources/js/Layouts/GuestLayout.vue b/resources/js/Layouts/GuestLayout.vue index 4eb3d38..06c2fff 100644 --- a/resources/js/Layouts/GuestLayout.vue +++ b/resources/js/Layouts/GuestLayout.vue @@ -1,19 +1,16 @@ diff --git a/resources/js/Layouts/Layout.vue b/resources/js/Layouts/Layout.vue index f39a81c..84b5b1d 100644 --- a/resources/js/Layouts/Layout.vue +++ b/resources/js/Layouts/Layout.vue @@ -1,5 +1,6 @@ +0 diff --git a/resources/js/Pages/Auth/ForgotPassword.vue b/resources/js/Pages/Auth/ForgotPassword.vue index 36862cb..d5633bd 100644 --- a/resources/js/Pages/Auth/ForgotPassword.vue +++ b/resources/js/Pages/Auth/ForgotPassword.vue @@ -3,8 +3,8 @@ import GuestLayout from '@/Layouts/GuestLayout.vue'; import InputError from '@/Components/InputError.vue'; import InputLabel from '@/Components/InputLabel.vue'; import { Button } from '@/Components/ui/button' -import TextInput from '@/Components/TextInput.vue'; import { Head, useForm } from '@inertiajs/vue3'; +import { Input } from '@/Components/ui/input' defineProps({ status: { @@ -23,39 +23,42 @@ const submit = () => { diff --git a/resources/js/Pages/Auth/Login.vue b/resources/js/Pages/Auth/Login.vue index 9dfaa9c..aaaaccb 100644 --- a/resources/js/Pages/Auth/Login.vue +++ b/resources/js/Pages/Auth/Login.vue @@ -1,11 +1,12 @@ - diff --git a/resources/js/Pages/Auth/Register.vue b/resources/js/Pages/Auth/Register.vue index bbbda77..333347e 100644 --- a/resources/js/Pages/Auth/Register.vue +++ b/resources/js/Pages/Auth/Register.vue @@ -5,6 +5,9 @@ import InputLabel from '@/Components/InputLabel.vue'; import { Button } from '@/Components/ui/button' import TextInput from '@/Components/TextInput.vue'; import { Head, Link, useForm } from '@inertiajs/vue3'; +import { Input } from '@/Components/ui/input' +import { Checkbox } from '@/Components/ui/checkbox' +import { AtSymbolIcon } from '@heroicons/vue/24/outline'; const form = useForm({ name: '', @@ -22,82 +25,81 @@ const submit = () => { diff --git a/resources/js/Pages/Feed.vue b/resources/js/Pages/Feed.vue new file mode 100644 index 0000000..4e51453 --- /dev/null +++ b/resources/js/Pages/Feed.vue @@ -0,0 +1,220 @@ + + + diff --git a/resources/js/Pages/Home.vue b/resources/js/Pages/Home.vue deleted file mode 100644 index 2ff1f2d..0000000 --- a/resources/js/Pages/Home.vue +++ /dev/null @@ -1,102 +0,0 @@ - - - diff --git a/resources/js/Pages/Shots/Show.vue b/resources/js/Pages/Shots/Show.vue index 44de829..142a0dd 100644 --- a/resources/js/Pages/Shots/Show.vue +++ b/resources/js/Pages/Shots/Show.vue @@ -5,23 +5,32 @@ import { ref } from 'vue' import ShotImage from '@/Pages/Shots/Partials/ShotImage.vue' import ShotDetails from '@/Pages/Shots/Partials/ShotDetails.vue' import ShotLinks from '@/Pages/Shots/Partials/ShotLinks.vue' +import { ChatBubbleLeftRightIcon, EllipsisHorizontalIcon, HandThumbUpIcon } from '@heroicons/vue/24/outline'; +import { Button } from '@/Components/ui/button' +import UserAvatar from '@/Components/ui/UserAvatar.vue' +import TimeAgo from '@/Components/ui/TimeAgo.vue' + +import { TabsContent, TabsIndicator, TabsList, TabsRoot, TabsTrigger } from 'radix-vue' + + +import CardShot from '@/Components/CardShot.vue' const props = defineProps({ shot: Object, - showLinks: Object, - author: Object, - reaction: Object, - reactionCounts: Object, - childShots: Array, - isOwner: Boolean, + // author: Object, + // showLinks: Object, + // reaction: Object, + // reactionCounts: Object, + // childShots: Array, + // isOwner: Boolean, }) -let shots = [ - props.shot, - ...props.childShots -] +// let shots = [ +// props.shot, +// ...props.childShots +// ] -const selectedIndex = ref(0) +// const selectedIndex = ref(0) diff --git a/resources/js/Pages/Users/Show.vue b/resources/js/Pages/Users/Show.vue new file mode 100644 index 0000000..16b96fc --- /dev/null +++ b/resources/js/Pages/Users/Show.vue @@ -0,0 +1,78 @@ + + diff --git a/resources/views/app.blade.php b/resources/views/app.blade.php index b68dd58..6495ef3 100644 --- a/resources/views/app.blade.php +++ b/resources/views/app.blade.php @@ -4,20 +4,6 @@ - - - - - - - - - - - - - - {{ config('app.name', 'ShotShare') }} diff --git a/routes/web.php b/routes/web.php index c23c17c..dcd0779 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,9 +1,11 @@ name('home'); +Route::get('/', FeedController::class) + ->name('feed') + ->middleware(['auth', 'verified']); Route::prefix('shots') ->name('shots.') @@ -39,6 +41,16 @@ ->middleware('feature:reactions'); }); +Route::prefix('users') + ->name('users.') + ->controller(UserController::class) + ->group(function() { + Route::get("{handle}", "show"); + + Route::post("{handle}/follow", "follow")->name("follow"); + Route::delete("{handle}/unfollow", "unfollow")->name("unfollow"); + }); + Route::prefix('api-keys') ->name('api-keys.') ->controller(ApiKeyController::class) From 07e88f7f4776c0f3d76cce65a3475034f715ca46 Mon Sep 17 00:00:00 2001 From: Micah Shackelford Date: Sat, 27 Jul 2024 16:20:53 -0500 Subject: [PATCH 02/47] feat: reusable shot feed + serve /feed route --- .../Auth/RegisteredUserController.php | 2 + app/Http/Controllers/FeedController.php | 9 +- app/Models/User.php | 5 + config/features.php | 1 + resources/js/Components/CardShot.vue | 17 +- resources/js/Components/PaginatedShotList.vue | 47 +++++ resources/js/Pages/Auth/Register.vue | 1 + resources/js/Pages/Feed.vue | 186 +----------------- resources/js/Pages/Users/Show.vue | 19 +- routes/web.php | 8 +- 10 files changed, 90 insertions(+), 205 deletions(-) create mode 100644 resources/js/Components/PaginatedShotList.vue diff --git a/app/Http/Controllers/Auth/RegisteredUserController.php b/app/Http/Controllers/Auth/RegisteredUserController.php index 92094cb..6dcee24 100644 --- a/app/Http/Controllers/Auth/RegisteredUserController.php +++ b/app/Http/Controllers/Auth/RegisteredUserController.php @@ -33,12 +33,14 @@ public function store(Request $request): RedirectResponse { $request->validate([ 'name' => 'required|string|max:255', + 'handle' => 'required|unique:users|string|max:255', 'email' => 'required|string|lowercase|email|max:255|unique:'.User::class, 'password' => ['required', 'confirmed', Rules\Password::defaults()], ]); $user = User::create([ 'name' => $request->name, + 'handle' => $request->handle, 'email' => $request->email, 'password' => Hash::make($request->password), ]); diff --git a/app/Http/Controllers/FeedController.php b/app/Http/Controllers/FeedController.php index 7761a9a..7730fa9 100644 --- a/app/Http/Controllers/FeedController.php +++ b/app/Http/Controllers/FeedController.php @@ -2,6 +2,8 @@ namespace App\Http\Controllers; +use App\Data\ShotData; +use App\Models\Shot; use Illuminate\Http\Request; use Inertia\Inertia; @@ -12,6 +14,11 @@ class FeedController extends Controller */ public function __invoke(Request $request) { - return Inertia::render("Feed"); + return Inertia::render("Feed", [ + "shots" => ShotData::collect(Shot::whereHas("user.followers", fn($q) => $q->where("user_followers.follower_id", $request->user()->getKey())) + ->orderByDesc("created_at") + ->where("anonymize", false) + ->cursorPaginate()), + ]); } } diff --git a/app/Models/User.php b/app/Models/User.php index f6e7b13..e96f6d0 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -80,6 +80,11 @@ public function followers() return $this->belongsToMany(User::class, UserFollower::class, "user_id", "follower_id"); } + public function following() + { + return $this->belongsToMany(User::class, UserFollower::class, "follower_id", "user_id"); + } + public function scopeWhereHandle(Builder $query, string $handle) { $query->where("handle", Str::ltrim($handle, '@')); diff --git a/config/features.php b/config/features.php index 1a33e23..63186c8 100644 --- a/config/features.php +++ b/config/features.php @@ -13,4 +13,5 @@ 'feed' => true, 'explore' => true, 'search' => true, + 'followers' => true, ]; diff --git a/resources/js/Components/CardShot.vue b/resources/js/Components/CardShot.vue index 8410898..73f6023 100644 --- a/resources/js/Components/CardShot.vue +++ b/resources/js/Components/CardShot.vue @@ -2,11 +2,10 @@ import { ref } from 'vue' import { ChatBubbleLeftRightIcon, EllipsisHorizontalIcon, HandThumbUpIcon } from '@heroicons/vue/24/outline'; -import ShotImage from '@/Pages/Shots/Partials/ShotImage.vue' -import ShotDetails from '@/Pages/Shots/Partials/ShotDetails.vue' -import ShotLinks from '@/Pages/Shots/Partials/ShotLinks.vue' +import { Button } from '@/Components/ui/button'; import UserAvatar from '@/Components/ui/UserAvatar.vue' import TimeAgo from '@/Components/ui/TimeAgo.vue' +import { Link } from '@inertiajs/vue3'; defineProps({ shot: Object @@ -20,7 +19,17 @@ defineProps({
-
{{ shot.author?.name ?? "Anonymous User" }}
+ + {{ shot.author?.name ?? "Anonymous User" }} + +
+ Anonymous User +
{{ shot.author?.handle ?? 'none' }} • diff --git a/resources/js/Components/PaginatedShotList.vue b/resources/js/Components/PaginatedShotList.vue new file mode 100644 index 0000000..63488ea --- /dev/null +++ b/resources/js/Components/PaginatedShotList.vue @@ -0,0 +1,47 @@ + + diff --git a/resources/js/Pages/Auth/Register.vue b/resources/js/Pages/Auth/Register.vue index 333347e..31a15a8 100644 --- a/resources/js/Pages/Auth/Register.vue +++ b/resources/js/Pages/Auth/Register.vue @@ -11,6 +11,7 @@ import { AtSymbolIcon } from '@heroicons/vue/24/outline'; const form = useForm({ name: '', + handle: '', email: '', password: '', password_confirmation: '', diff --git a/resources/js/Pages/Feed.vue b/resources/js/Pages/Feed.vue index 4e51453..68f45d0 100644 --- a/resources/js/Pages/Feed.vue +++ b/resources/js/Pages/Feed.vue @@ -9,52 +9,10 @@ import { Avatar } from '@/Components/ui/avatar'; import { ChatBubbleLeftRightIcon, EllipsisHorizontalIcon, PhotoIcon } from '@heroicons/vue/24/outline'; import { Button } from '@/Components/ui/button'; import { ThumbsUpIcon } from 'lucide-vue-next'; - -const form = useForm({ - images: [], +import PaginatedShotList from '@/Components/PaginatedShotList.vue'; +const props = defineProps({ + shots: Object }) - -const hoveringDropzone = ref(false) - -const dropFile = (event) => { - form.images = [ - ...form.images, - ...event.dataTransfer.files - ] - - form.post('upload', { - onFinish: () => { - form.images = [] - } - }) -} - -const selectFile = (event) => { - - form.images = [ - ...form.images, - ...event.target.files - ] - - form.post('upload', { - onFinish: () => { - form.images = [] - } - }) -} - -const pasteFile = (event) => { - form.images = [ - ...form.images, - ...event.clipboardData.files - ] - - form.post('upload', { - onFinish: () => { - form.images = [] - } - }) -} diff --git a/resources/js/Pages/Users/Show.vue b/resources/js/Pages/Users/Show.vue index 16b96fc..37655c4 100644 --- a/resources/js/Pages/Users/Show.vue +++ b/resources/js/Pages/Users/Show.vue @@ -1,10 +1,10 @@