From 7ddf5bec91af721b9af3e015b2f71e065430770a Mon Sep 17 00:00:00 2001 From: Arthur Monney Date: Tue, 16 Nov 2021 20:59:45 +0100 Subject: [PATCH 01/27] :card_file_box: ajout du model et migration pour les discussions --- app/Models/Article.php | 7 - app/Models/Discussion.php | 146 ++++++++++++++++++ app/Models/Thread.php | 2 +- app/Traits/HasTags.php | 8 + ..._11_16_191729_create_discussions_table.php | 37 +++++ 5 files changed, 192 insertions(+), 8 deletions(-) create mode 100644 app/Models/Discussion.php create mode 100644 database/migrations/2021_11_16_191729_create_discussions_table.php diff --git a/app/Models/Article.php b/app/Models/Article.php index 46842e9f..edd21bf2 100644 --- a/app/Models/Article.php +++ b/app/Models/Article.php @@ -288,13 +288,6 @@ public function scopeNotDeclined(Builder $query): Builder return $query->whereNull('declined_at'); } - public function scopeForTag(Builder $query, string $tag): Builder - { - return $query->whereHas('tags', function ($query) use ($tag) { - $query->where('tags.slug', $tag); - }); - } - public function scopeRecent(Builder $query): Builder { return $query->orderBy('is_pinned', 'desc') diff --git a/app/Models/Discussion.php b/app/Models/Discussion.php new file mode 100644 index 00000000..abe2257f --- /dev/null +++ b/app/Models/Discussion.php @@ -0,0 +1,146 @@ + 'boolean', + 'is_pinned' => 'boolean', + ]; + + /** + * The relations to eager load on every query. + * + * @var array + */ + protected $with = [ + 'tags', + 'author', + ]; + + protected $removeViewsOnDelete = true; + + public static function boot() + { + parent::boot(); + } + + /** + * Get the route key for the model. + * + * @return string + */ + public function getRouteKeyName(): string + { + return 'slug'; + } + + public function subject(): string + { + return $this->title; + } + + public function replyAbleSubject(): string + { + return $this->title; + } + + public function getPathUrl(): string + { + return "/discussions/{$this->slug()}"; + } + + public function excerpt(int $limit = 110): string + { + return Str::limit(strip_tags(md_to_html($this->body)), $limit); + } + + public function isPinned(): bool + { + return (bool) $this->is_pinned; + } + + public function isLocked(): bool + { + return (bool) $this->locked; + } + + public function scopePinned(Builder $query): Builder + { + return $query->where('is_pinned', true); + } + + public function scopeNotPinned(Builder $query): Builder + { + return $query->where('is_pinned', false); + } + + public function scopeRecent(Builder $query): Builder + { + return $query->orderBy('is_pinned', 'desc') + ->orderBy('created_at', 'desc'); + } + + public function scopePopular(Builder $query): Builder + { + return $query->withCount('reactions') + ->orderBy('reactions_count', 'desc'); + } + + public function scopeActive(Builder $query): Builder + { + return $query->withCount(['replies' => function ($query) { + $query->where('created_at', '>=', now()->subWeek()); + }]) + ->orderBy('replies_count', 'desc'); + } + + public function lockedDiscussion() + { + $this->update(['locked' => true]); + } +} diff --git a/app/Models/Thread.php b/app/Models/Thread.php index 2defa123..a04922a9 100644 --- a/app/Models/Thread.php +++ b/app/Models/Thread.php @@ -104,7 +104,7 @@ public function replyAbleSubject(): string public function getPathUrl(): string { - return "/forum/{$this->slug}"; + return "/forum/{$this->slug()}"; } public function excerpt(int $limit = 100): string diff --git a/app/Traits/HasTags.php b/app/Traits/HasTags.php index 6af8b9b5..9478bc72 100644 --- a/app/Traits/HasTags.php +++ b/app/Traits/HasTags.php @@ -3,6 +3,7 @@ namespace App\Traits; use App\Models\Tag; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Relations\MorphToMany; trait HasTags @@ -22,6 +23,13 @@ public function removeTags() $this->unsetRelation('tags'); } + public function scopeForTag(Builder $query, string $tag): Builder + { + return $query->whereHas('tags', function ($query) use ($tag) { + $query->where('tags.slug', $tag); + }); + } + public function tags(): MorphToMany { return $this->morphToMany(Tag::class, 'taggable'); diff --git a/database/migrations/2021_11_16_191729_create_discussions_table.php b/database/migrations/2021_11_16_191729_create_discussions_table.php new file mode 100644 index 00000000..58129937 --- /dev/null +++ b/database/migrations/2021_11_16_191729_create_discussions_table.php @@ -0,0 +1,37 @@ +id(); + $table->foreignId('user_id')->constrained()->cascadeOnDelete(); + $table->string('title'); + $table->string('slug')->unique(); + $table->text('body'); + $table->boolean('is_pinned')->default(false); + $table->boolean('locked')->default(false); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('discussions'); + } +} From 4f6e0ea9ea1dcf7f86ca889498c70e2c48a84b67 Mon Sep 17 00:00:00 2001 From: Arthur Monney Date: Tue, 16 Nov 2021 21:52:30 +0100 Subject: [PATCH 02/27] :white_check_mark: mis en place des tests unitaires pour les discussions --- database/factories/DiscussionFactory.php | 19 +++++++ database/factories/TagFactory.php | 2 +- tests/Integration/DiscussionTest.php | 70 ++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 database/factories/DiscussionFactory.php create mode 100644 tests/Integration/DiscussionTest.php diff --git a/database/factories/DiscussionFactory.php b/database/factories/DiscussionFactory.php new file mode 100644 index 00000000..a2e84801 --- /dev/null +++ b/database/factories/DiscussionFactory.php @@ -0,0 +1,19 @@ + $attributes['user_id'] ?? User::factory(), + 'title' => $this->faker->sentence(), + 'body' => $this->faker->paragraphs(3, true), + 'slug' => $this->faker->unique()->slug(), + ]; + } +} diff --git a/database/factories/TagFactory.php b/database/factories/TagFactory.php index 201eac38..90992dd2 100644 --- a/database/factories/TagFactory.php +++ b/database/factories/TagFactory.php @@ -23,7 +23,7 @@ public function definition() { return [ 'name' => $this->faker->text(15), - 'slug' => $this->faker->slug, + 'slug' => $this->faker->slug(), 'concerns' => ['post', 'discussion', 'tutorial'], ]; } diff --git a/tests/Integration/DiscussionTest.php b/tests/Integration/DiscussionTest.php new file mode 100644 index 00000000..3015ee9b --- /dev/null +++ b/tests/Integration/DiscussionTest.php @@ -0,0 +1,70 @@ +create(['slug' => 'foo']); + + expect(Discussion::findBySlug('foo'))->toBeInstanceOf(Discussion::class); +}); + +it('can give an excerpt of its body', function () { + $discussion = Discussion::factory()->make(['body' => 'This is a pretty long text.']); + + expect($discussion->excerpt(7))->toEqual('This is...'); +}); + +test('html in excerpts is html encoded', function () { + $discussion = Discussion::factory()->make(['body' => '

Discussion body

']); + + expect($discussion->excerpt())->toEqual("<p>Discussion body</p>\n"); +}); + +it('can have many tags', function () { + $tags = Tag::factory()->count(3)->create(); + $discussion = Discussion::factory()->create(); + $discussion->syncTags($tags->modelKeys()); + + expect($discussion->tags->count())->toEqual(3); +}); + +it('records activity when a discussion is created', function () { + actingAs(); + + $discussion = Discussion::factory()->create(['user_id' => auth()->id()]); + + $this->assertDatabaseHas('activities', [ + 'type' => 'created_discussion', + 'user_id' => auth()->id(), + 'subject_id' => $discussion->id, + 'subject_type' => Discussion::class, + ]); + + $activity = Activity::first(); + + $this->assertEquals($activity->subject->id, $discussion->id); + + $this->assertEquals(auth()->user()->activities->count(), 1); +}); + +it('generates a slug when valid url characters provided', function () { + $discussion = Discussion::factory()->make(['slug' => 'Help with eloquent']); + + expect($discussion->slug())->toEqual('help-with-eloquent'); +}); + +it('generates a unique slug when valid url characters provided', function () { + $discussionOne = Discussion::factory()->create(['slug' => 'Help with eloquent']); + $discussionTwo = Discussion::factory()->create(['slug' => 'Help with eloquent']); + + expect($discussionTwo->slug())->toEqual('help-with-eloquent-1'); +}); + +it('generates a slug when invalid url characters provided', function () { + $discussion = Discussion::factory()->make(['slug' => '한글 테스트']); + + // When providing a slug with invalid url characters, a random 5 character string is returned. + expect($discussion->slug())->toMatch('/\w{5}/'); +}); From e68e8dfe58edc930c66e2af7e3a9d7853ef562d5 Mon Sep 17 00:00:00 2001 From: Arthur Monney Date: Wed, 17 Nov 2021 09:49:59 +0100 Subject: [PATCH 03/27] :sparkles: ajout de la creation et du detail d'une description --- app/Http/Controllers/DiscussionController.php | 48 +++++ .../Controllers/Forum/ReplyController.php | 12 -- .../{Forum => }/ThreadController.php | 2 +- app/Http/Livewire/Articles/Create.php | 19 +- app/Http/Livewire/Discussions/Browse.php | 13 ++ app/Http/Livewire/Discussions/Create.php | 58 +++++ app/Http/Livewire/Discussions/Edit.php | 16 ++ app/Http/Livewire/Discussions/Subscribe.php | 51 +++++ app/Models/Article.php | 7 + app/Models/Discussion.php | 27 ++- app/Models/User.php | 16 +- .../PostDiscussionToTelegram.php | 30 +++ app/Policies/DiscussionPolicy.php | 40 ++++ app/Providers/AppServiceProvider.php | 4 + app/Providers/AuthServiceProvider.php | 3 + app/View/Composers/InactiveDiscussions.php | 19 ++ .../Composers/TopContributorsComposer.php | 22 ++ public/css/app.css | 198 +++++++++++------- .../feeds/created_discussion.blade.php | 0 .../discussions/_contributions.blade.php | 57 +++++ resources/views/discussions/edit.blade.php | 9 + resources/views/discussions/index.blade.php | 9 + resources/views/discussions/new.blade.php | 22 ++ resources/views/discussions/show.blade.php | 64 ++++++ resources/views/layouts/_nav.blade.php | 10 +- .../livewire/discussions/_form.blade.php | 36 ++++ .../livewire/discussions/browse.blade.php | 3 + .../livewire/discussions/create.blade.php | 3 + .../views/livewire/discussions/edit.blade.php | 3 + .../livewire/discussions/subscribe.blade.php | 17 ++ .../views/livewire/forum/subscribe.blade.php | 4 +- resources/views/user/profile.blade.php | 2 +- routes/web.php | 16 +- 33 files changed, 727 insertions(+), 113 deletions(-) create mode 100644 app/Http/Controllers/DiscussionController.php delete mode 100644 app/Http/Controllers/Forum/ReplyController.php rename app/Http/Controllers/{Forum => }/ThreadController.php (97%) create mode 100644 app/Http/Livewire/Discussions/Browse.php create mode 100644 app/Http/Livewire/Discussions/Create.php create mode 100644 app/Http/Livewire/Discussions/Edit.php create mode 100644 app/Http/Livewire/Discussions/Subscribe.php create mode 100644 app/Notifications/PostDiscussionToTelegram.php create mode 100644 app/Policies/DiscussionPolicy.php create mode 100644 app/View/Composers/InactiveDiscussions.php create mode 100644 app/View/Composers/TopContributorsComposer.php create mode 100644 resources/views/components/feeds/created_discussion.blade.php create mode 100644 resources/views/discussions/_contributions.blade.php create mode 100644 resources/views/discussions/edit.blade.php create mode 100644 resources/views/discussions/index.blade.php create mode 100644 resources/views/discussions/new.blade.php create mode 100644 resources/views/discussions/show.blade.php create mode 100644 resources/views/livewire/discussions/_form.blade.php create mode 100644 resources/views/livewire/discussions/browse.blade.php create mode 100644 resources/views/livewire/discussions/create.blade.php create mode 100644 resources/views/livewire/discussions/edit.blade.php create mode 100644 resources/views/livewire/discussions/subscribe.blade.php diff --git a/app/Http/Controllers/DiscussionController.php b/app/Http/Controllers/DiscussionController.php new file mode 100644 index 00000000..78d09939 --- /dev/null +++ b/app/Http/Controllers/DiscussionController.php @@ -0,0 +1,48 @@ +middleware(['auth', 'verified'], ['except' => ['index', 'show']]); + } + + public function index() + { + return view('discussions.index'); + } + + public function show(Discussion $discussion) + { + views($discussion)->record(); + + seo() + ->title($discussion->title) + ->description($discussion->excerpt(100)) + ->image(asset('images/socialcard.png')) + ->twitterTitle($discussion->title) + ->twitterDescription($discussion->excerpt(100)) + ->twitterImage(asset('images/socialcard.png')) + ->twitterSite('laravelcm') + ->withUrl(); + + return view('discussions.show', compact('discussion')); + } + + public function create() + { + return view('discussions.new'); + } + + public function edit(Discussion $discussion) + { + $this->authorize(DiscussionPolicy::UPDATE, $discussion); + + return view('discussions.edit', compact('discussion')); + } +} diff --git a/app/Http/Controllers/Forum/ReplyController.php b/app/Http/Controllers/Forum/ReplyController.php deleted file mode 100644 index 09d3d268..00000000 --- a/app/Http/Controllers/Forum/ReplyController.php +++ /dev/null @@ -1,12 +0,0 @@ -submitted = ! auth()->user()->hasAnyRole(['admin', 'moderator']); - $this->submitted_at = auth()->user()->hasAnyRole(['admin', 'moderator']) ? now() : null; - $this->approved_at = auth()->user()->hasAnyRole(['admin', 'moderator']) ? now() : null; + $user = Auth::user(); + + $this->submitted = ! $user->hasAnyRole(['admin', 'moderator']); + $this->submitted_at = $user->hasAnyRole(['admin', 'moderator']) ? now() : null; + $this->approved_at = $user->hasAnyRole(['admin', 'moderator']) ? now() : null; } public function onMarkdownUpdate(string $content) @@ -43,6 +46,8 @@ public function store() { $this->validate(); + $user = Auth::user(); + $article = Article::create([ 'title' => $this->title, 'slug' => $this->slug, @@ -52,10 +57,12 @@ public function store() 'approved_at' => $this->approved_at, 'show_toc' => $this->show_toc, 'canonical_url' => $this->canonical_url, - 'user_id' => auth()->id(), + 'user_id' => $user->id, ]); - $article->syncTags($this->associateTags); + if (collect($this->associateTags)->isNotEmpty()) { + $article->syncTags($this->associateTags); + } if ($this->file) { $article->addMedia($this->file->getRealPath())->toMediaCollection('media'); @@ -66,7 +73,7 @@ public function store() session()->flash('success', 'Merci d\'avoir soumis votre article. Vous aurez des nouvelles que lorsque nous accepterons votre article.'); } - auth()->user()->hasRole('user') ? + $user->hasRole('user') ? $this->redirect('/articles/me') : $this->redirect('/admin/articles'); } diff --git a/app/Http/Livewire/Discussions/Browse.php b/app/Http/Livewire/Discussions/Browse.php new file mode 100644 index 00000000..e32e21fa --- /dev/null +++ b/app/Http/Livewire/Discussions/Browse.php @@ -0,0 +1,13 @@ + 'onMarkdownUpdate']; + + protected $rules = [ + 'title' => ['required', 'max:150'], + 'body' => ['required'], + 'tags_selected' => 'nullable|array', + ]; + + public function onMarkdownUpdate(string $content) + { + $this->body = $content; + } + + public function store() + { + $this->validate(); + + $discussion = Discussion::create([ + 'title' => $this->title, + 'slug' => $this->title, + 'body' => $this->body, + 'user_id' => Auth::id(), + ]); + + if (collect($this->associateTags)->isNotEmpty()) { + $discussion->syncTags($this->associateTags); + } + + Auth::user()->notify(new PostDiscussionToTelegram($discussion)); + + $this->redirectRoute('discussions.show', $discussion); + } + + public function render() + { + return view('livewire.discussions.create', [ + 'tags' => Tag::whereJsonContains('concerns', ['discussion'])->get(), + ]); + } +} diff --git a/app/Http/Livewire/Discussions/Edit.php b/app/Http/Livewire/Discussions/Edit.php new file mode 100644 index 00000000..f211f1f6 --- /dev/null +++ b/app/Http/Livewire/Discussions/Edit.php @@ -0,0 +1,16 @@ + '$refresh']; + + public function subscribe() + { + $this->authorize(DiscussionPolicy::SUBSCRIBE, $this->discussion); + + $subscribe = new SubscribeModel(); + $subscribe->uuid = Uuid::uuid4()->toString(); + $subscribe->user()->associate(Auth::user()); + $this->discussion->subscribes()->save($subscribe); + + $this->notification()->success('Abonnement', 'Vous êtes maintenant abonné à cette discussion.'); + $this->emitSelf('refresh'); + } + + public function unsubscribe() + { + $this->authorize(DiscussionPolicy::UNSUBSCRIBE, $this->discussion); + + $this->discussion->subscribes() + ->where('user_id', Auth::id()) + ->delete(); + + $this->notification()->success('Désabonnement', 'Vous êtes maintenant désabonné de cette discussion.'); + $this->emitSelf('refresh'); + } + + public function render() + { + return view('livewire.discussions.subscribe'); + } +} diff --git a/app/Models/Article.php b/app/Models/Article.php index edd21bf2..10652f0d 100644 --- a/app/Models/Article.php +++ b/app/Models/Article.php @@ -336,4 +336,11 @@ public function markAsPublish() { $this->update(['tweet_id' => $this->author->id]); } + + public function delete() + { + $this->removeTags(); + + parent::delete(); + } } diff --git a/app/Models/Discussion.php b/app/Models/Discussion.php index abe2257f..68a7b2b1 100644 --- a/app/Models/Discussion.php +++ b/app/Models/Discussion.php @@ -4,9 +4,11 @@ use App\Contracts\ReactableInterface; use App\Contracts\ReplyInterface; +use App\Contracts\SubscribeInterface; use App\Traits\HasAuthor; use App\Traits\HasReplies; use App\Traits\HasSlug; +use App\Traits\HasSubscribers; use App\Traits\HasTags; use App\Traits\Reactable; use App\Traits\RecordsActivity; @@ -17,11 +19,12 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Str; -class Discussion extends Model implements ReactableInterface, ReplyInterface, Viewable +class Discussion extends Model implements ReactableInterface, ReplyInterface, SubscribeInterface, Viewable { use HasAuthor, HasFactory, HasReplies, + HasSubscribers, HasSlug, HasTags, InteractsWithViews, @@ -62,6 +65,15 @@ class Discussion extends Model implements ReactableInterface, ReplyInterface, Vi 'author', ]; + /** + * The relationship counts that should be eager loaded on every query. + * + * @var array + */ + protected $withCount = [ + 'replies', + ]; + protected $removeViewsOnDelete = true; public static function boot() @@ -139,8 +151,21 @@ public function scopeActive(Builder $query): Builder ->orderBy('replies_count', 'desc'); } + public function scopeNoComments(Builder $query): Builder + { + return $query->whereDoesntHave('replies'); + } + public function lockedDiscussion() { $this->update(['locked' => true]); } + + public function delete() + { + $this->removeTags(); + $this->deleteReplies(); + + parent::delete(); + } } diff --git a/app/Models/User.php b/app/Models/User.php index fa29f7fb..c5ccbb80 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -154,7 +154,7 @@ public function providers(): HasMany public function articles(): HasMany { - return $this->hasMany(Article::class, 'user_id'); + return $this->hasMany(Article::class); } public function activities(): HasMany @@ -169,7 +169,12 @@ public function threads(): HasMany public function replyAble(): HasMany { - return $this->hasMany(Reply::class, 'author_id'); + return $this->hasMany(Reply::class); + } + + public function discussions(): HasMany + { + return $this->hasMany(Discussion::class); } public function deleteThreads() @@ -309,7 +314,7 @@ public function scopeMostSubmissions(Builder $query, int $inLastDays = null) } return $query; - }])->orderBy('articles_count', 'desc'); + }])->orderByDesc('articles_count'); } public function scopeMostSolutionsInLastDays(Builder $query, int $days) @@ -333,4 +338,9 @@ public function scopeWithCounts(Builder $query) }, ]); } + + public function scopeTopContributors(Builder $query): Builder + { + return $query->withCount(['discussions'])->orderByDesc('discussions_count'); + } } diff --git a/app/Notifications/PostDiscussionToTelegram.php b/app/Notifications/PostDiscussionToTelegram.php new file mode 100644 index 00000000..3799d466 --- /dev/null +++ b/app/Notifications/PostDiscussionToTelegram.php @@ -0,0 +1,30 @@ +to('@laravelcm') + ->content("{$this->discussion->title} " . route('discussions.show', $this->discussion->slug())); + } +} diff --git a/app/Policies/DiscussionPolicy.php b/app/Policies/DiscussionPolicy.php new file mode 100644 index 00000000..ba7a44b0 --- /dev/null +++ b/app/Policies/DiscussionPolicy.php @@ -0,0 +1,40 @@ +isAuthoredBy($user) || $user->isModerator() || $user->isAdmin(); + } + + public function delete(User $user, Discussion $discussion): bool + { + return $discussion->isAuthoredBy($user) || $user->isModerator() || $user->isAdmin(); + } + + public function togglePinnedStatus(User $user, Discussion $discussion): bool + { + return $user->isModerator() || $user->isAdmin(); + } + + public function subscribe(User $user, Discussion $discussion): bool + { + return ! $discussion->hasSubscriber($user); + } + + public function unsubscribe(User $user, Discussion $discussion): bool + { + return $discussion->hasSubscriber($user); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 94929f4b..b93e319c 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,7 +3,9 @@ namespace App\Providers; use App\View\Composers\ChannelsComposer; +use App\View\Composers\InactiveDiscussions; use App\View\Composers\ModeratorsComposer; +use App\View\Composers\TopContributorsComposer; use App\View\Composers\TopMembersComposer; use Illuminate\Support\Facades\Blade; use Illuminate\Support\Facades\View; @@ -59,5 +61,7 @@ public function bootViewsComposer() View::composer('forum._channels', ChannelsComposer::class); View::composer('forum._top-members', TopMembersComposer::class); View::composer('forum._moderators', ModeratorsComposer::class); + View::composer('discussions._contributions', TopContributorsComposer::class); + View::composer('discussions._contributions', InactiveDiscussions::class); } } diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 35968fc6..e054b304 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -3,9 +3,11 @@ namespace App\Providers; use App\Models\Article; +use App\Models\Discussion; use App\Models\Reply; use App\Models\Thread; use App\Policies\ArticlePolicy; +use App\Policies\DiscussionPolicy; use App\Policies\ReplyPolicy; use App\Policies\ThreadPolicy; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; @@ -22,6 +24,7 @@ class AuthServiceProvider extends ServiceProvider Article::class => ArticlePolicy::class, Thread::class => ThreadPolicy::class, Reply::class => ReplyPolicy::class, + Discussion::class => DiscussionPolicy::class, ]; /** diff --git a/app/View/Composers/InactiveDiscussions.php b/app/View/Composers/InactiveDiscussions.php new file mode 100644 index 00000000..66383911 --- /dev/null +++ b/app/View/Composers/InactiveDiscussions.php @@ -0,0 +1,19 @@ +limit(5)->get(); + }); + + $view->with('discussions', $discussions); + } +} diff --git a/app/View/Composers/TopContributorsComposer.php b/app/View/Composers/TopContributorsComposer.php new file mode 100644 index 00000000..58070187 --- /dev/null +++ b/app/View/Composers/TopContributorsComposer.php @@ -0,0 +1,22 @@ +get() + ->filter(fn ($contributor) => $contributor->discussions_count >= 1) + ->take(5); + }); + + $view->with('topContributors', $topContributors); + } +} diff --git a/public/css/app.css b/public/css/app.css index be0d75dd..d8645f2e 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -4486,6 +4486,12 @@ select { .-ml-1 { margin-left: -0.25rem; } +.ml-1\.5 { + margin-left: 0.375rem; +} +.ml-1 { + margin-left: 0.25rem; +} .mr-1\.5 { margin-right: 0.375rem; } @@ -4507,9 +4513,6 @@ select { .-mr-1 { margin-right: -0.25rem; } -.ml-1 { - margin-left: 0.25rem; -} .mt-16 { margin-top: 4rem; } @@ -4537,9 +4540,6 @@ select { .-mt-12 { margin-top: -3rem; } -.ml-1\.5 { - margin-left: 0.375rem; -} .-mb-px { margin-bottom: -1px; } @@ -4582,6 +4582,9 @@ select { .mb-5 { margin-bottom: 1.25rem; } +.ml-9 { + margin-left: 2.25rem; +} .ml-5 { margin-left: 1.25rem; } @@ -4591,9 +4594,6 @@ select { .-mt-4 { margin-top: -1rem; } -.ml-9 { - margin-left: 2.25rem; -} .-ml-0\.5 { margin-left: -0.125rem; } @@ -4654,6 +4654,9 @@ select { .h-56 { height: 14rem; } +.h-\[450px\] { + height: 450px; +} .h-9 { height: 2.25rem; } @@ -4666,9 +4669,6 @@ select { .h-3 { height: 0.75rem; } -.h-\[450px\] { - height: 450px; -} .h-7 { height: 1.75rem; } @@ -4696,6 +4696,9 @@ select { .h-64 { height: 16rem; } +.h-14 { + height: 3.5rem; +} .h-\[350px\] { height: 350px; } @@ -4705,9 +4708,6 @@ select { .h-screen { height: 100vh; } -.h-14 { - height: 3.5rem; -} .max-h-96 { max-height: 24rem; } @@ -4786,6 +4786,9 @@ select { .w-60 { width: 15rem; } +.w-14 { + width: 3.5rem; +} .w-48 { width: 12rem; } @@ -4795,9 +4798,6 @@ select { .w-1\/3 { width: 33.333333%; } -.w-14 { - width: 3.5rem; -} .min-w-0 { min-width: 0px; } @@ -5122,21 +5122,26 @@ select { margin-top: calc(1.25rem * calc(1 - var(--tw-space-y-reverse))); margin-bottom: calc(1.25rem * var(--tw-space-y-reverse)); } +.space-y-16 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(4rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(4rem * var(--tw-space-y-reverse)); +} .space-x-6 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(1.5rem * var(--tw-space-x-reverse)); margin-left: calc(1.5rem * calc(1 - var(--tw-space-x-reverse))); } -.space-x-5 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(1.25rem * var(--tw-space-x-reverse)); - margin-left: calc(1.25rem * calc(1 - var(--tw-space-x-reverse))); -} .-space-x-2 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(-0.5rem * var(--tw-space-x-reverse)); margin-left: calc(-0.5rem * calc(1 - var(--tw-space-x-reverse))); } +.space-x-5 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1.25rem * var(--tw-space-x-reverse)); + margin-left: calc(1.25rem * calc(1 - var(--tw-space-x-reverse))); +} .divide-x > :not([hidden]) ~ :not([hidden]) { --tw-divide-x-reverse: 0; border-right-width: calc(1px * var(--tw-divide-x-reverse)); @@ -5159,6 +5164,10 @@ select { --tw-divide-opacity: 1; border-color: rgba(228, 228, 231, var(--tw-divide-opacity)); } +.divide-gray-200 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 1; + border-color: rgba(229, 231, 235, var(--tw-divide-opacity)); +} .self-start { align-self: flex-start; } @@ -5227,6 +5236,10 @@ select { .rounded-3xl { border-radius: 1.5rem; } +.rounded-b-lg { + border-bottom-right-radius: 0.5rem; + border-bottom-left-radius: 0.5rem; +} .rounded-l-md { border-top-left-radius: 0.375rem; border-bottom-left-radius: 0.375rem; @@ -5239,10 +5252,6 @@ select { border-top-left-radius: 0.5rem; border-bottom-left-radius: 0.5rem; } -.rounded-b-lg { - border-bottom-right-radius: 0.5rem; - border-bottom-left-radius: 0.5rem; -} .rounded-r-lg { border-top-right-radius: 0.5rem; border-bottom-right-radius: 0.5rem; @@ -5279,12 +5288,12 @@ select { .border-b { border-bottom-width: 1px; } -.border-r-0 { - border-right-width: 0px; -} .border-t { border-top-width: 1px; } +.border-r-0 { + border-right-width: 0px; +} .border-b-2 { border-bottom-width: 2px; } @@ -5696,14 +5705,14 @@ select { padding-left: 0.875rem; padding-right: 0.875rem; } -.py-2\.5 { - padding-top: 0.625rem; - padding-bottom: 0.625rem; -} .py-14 { padding-top: 3.5rem; padding-bottom: 3.5rem; } +.py-2\.5 { + padding-top: 0.625rem; + padding-bottom: 0.625rem; +} .px-0\.5 { padding-left: 0.125rem; padding-right: 0.125rem; @@ -5727,12 +5736,15 @@ select { .pl-5 { padding-left: 1.25rem; } -.pl-3 { - padding-left: 0.75rem; -} .pt-6 { padding-top: 1.5rem; } +.pt-5 { + padding-top: 1.25rem; +} +.pl-3 { + padding-left: 0.75rem; +} .pl-10 { padding-left: 2.5rem; } @@ -5742,9 +5754,6 @@ select { .pt-16 { padding-top: 4rem; } -.pt-5 { - padding-top: 1.25rem; -} .pt-4 { padding-top: 1rem; } @@ -5849,6 +5858,10 @@ select { font-size: 1.25rem; line-height: 1.75rem; } +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} .text-xs { font-size: 0.75rem; line-height: 1rem; @@ -5869,10 +5882,6 @@ select { font-size: 2.25rem; line-height: 2.5rem; } -.text-base { - font-size: 1rem; - line-height: 1.5rem; -} .text-\[12px\] { font-size: 12px; } @@ -5913,6 +5922,9 @@ select { .leading-7 { line-height: 1.75rem; } +.leading-6 { + line-height: 1.5rem; +} .leading-5 { line-height: 1.25rem; } @@ -5922,9 +5934,6 @@ select { .leading-tight { line-height: 1.25; } -.leading-6 { - line-height: 1.5rem; -} .leading-4 { line-height: 1rem; } @@ -6167,6 +6176,10 @@ select { --tw-text-opacity: 1; color: rgba(127, 29, 29, var(--tw-text-opacity)); } +.text-gray-900 { + --tw-text-opacity: 1; + color: rgba(17, 24, 39, var(--tw-text-opacity)); +} .underline { text-decoration: underline; } @@ -6320,6 +6333,10 @@ select { --tw-ring-opacity: 1; --tw-ring-color: rgba(209, 213, 219, var(--tw-ring-opacity)); } +.ring-white { + --tw-ring-opacity: 1; + --tw-ring-color: rgba(255, 255, 255, var(--tw-ring-opacity)); +} .ring-secondary-600 { --tw-ring-opacity: 1; --tw-ring-color: rgba(82, 82, 91, var(--tw-ring-opacity)); @@ -6340,10 +6357,6 @@ select { --tw-ring-opacity: 1; --tw-ring-color: rgba(37, 99, 235, var(--tw-ring-opacity)); } -.ring-white { - --tw-ring-opacity: 1; - --tw-ring-color: rgba(255, 255, 255, var(--tw-ring-opacity)); -} .ring-opacity-5 { --tw-ring-opacity: 0.05; } @@ -6718,6 +6731,11 @@ html { background-color: rgba(24, 24, 27, var(--tw-bg-opacity)); } +.hover\:bg-gray-50:hover { + --tw-bg-opacity: 1; + background-color: rgba(249, 250, 251, var(--tw-bg-opacity)); +} + .hover\:text-skin-inverted-muted:hover { --tw-text-opacity: 1; color: rgba(var(--color-text-inverted-muted), var(--tw-text-opacity)); @@ -6854,14 +6872,14 @@ html { border-color: rgba(var(--color-input-border), var(--tw-border-opacity)); } -.focus\:border-green-500:focus { +.focus\:border-skin-base:focus { --tw-border-opacity: 1; - border-color: rgba(16, 185, 129, var(--tw-border-opacity)); + border-color: rgba(var(--color-border), var(--tw-border-opacity)); } -.focus\:border-skin-base:focus { +.focus\:border-green-500:focus { --tw-border-opacity: 1; - border-color: rgba(var(--color-border), var(--tw-border-opacity)); + border-color: rgba(16, 185, 129, var(--tw-border-opacity)); } .focus\:border-green-300:focus { @@ -7384,6 +7402,10 @@ html { max-width: 32rem; } + .sm\:max-w-4xl { + max-width: 56rem; + } + .sm\:max-w-sm { max-width: 24rem; } @@ -7404,10 +7426,6 @@ html { max-width: 48rem; } - .sm\:max-w-4xl { - max-width: 56rem; - } - .sm\:max-w-5xl { max-width: 64rem; } @@ -7537,6 +7555,12 @@ html { row-gap: 3rem; } + .sm\:space-y-5 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.25rem * var(--tw-space-y-reverse)); + } + .sm\:space-y-10 > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 0; margin-top: calc(2.5rem * calc(1 - var(--tw-space-y-reverse))); @@ -7567,12 +7591,6 @@ html { margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); } - .sm\:space-y-5 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1.25rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1.25rem * var(--tw-space-y-reverse)); - } - .sm\:rounded-lg { border-radius: 0.5rem; } @@ -7648,14 +7666,18 @@ html { padding-bottom: 0.5rem; } + .sm\:py-9 { + padding-top: 2.25rem; + padding-bottom: 2.25rem; + } + .sm\:py-10 { padding-top: 2.5rem; padding-bottom: 2.5rem; } - .sm\:py-9 { - padding-top: 2.25rem; - padding-bottom: 2.25rem; + .sm\:pt-5 { + padding-top: 1.25rem; } .sm\:pb-1 { @@ -7666,10 +7688,6 @@ html { padding-top: 0px; } - .sm\:pt-5 { - padding-top: 1.25rem; - } - .sm\:pt-16 { padding-top: 4rem; } @@ -7762,6 +7780,10 @@ html { line-height: 1.25rem; } + .sm\:leading-8 { + line-height: 2rem; + } + .sm\:shadow-md { --tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); @@ -9071,6 +9093,30 @@ html { grid-column: span 10 / span 10; } + .lg\:col-start-9 { + grid-column-start: 9; + } + + .lg\:col-start-8 { + grid-column-start: 8; + } + + .lg\:col-start-10 { + grid-column-start: 10; + } + + .lg\:col-end-12 { + grid-column-end: 12; + } + + .lg\:col-end-13 { + grid-column-end: 13; + } + + .lg\:col-end-auto { + grid-column-end: auto; + } + .lg\:mx-0 { margin-left: 0px; margin-right: 0px; @@ -9124,14 +9170,14 @@ html { height: 5rem; } - .lg\:w-20 { - width: 5rem; - } - .lg\:w-90 { width: 22.5rem; } + .lg\:w-20 { + width: 5rem; + } + .lg\:max-w-none { max-width: none; } diff --git a/resources/views/components/feeds/created_discussion.blade.php b/resources/views/components/feeds/created_discussion.blade.php new file mode 100644 index 00000000..e69de29b diff --git a/resources/views/discussions/_contributions.blade.php b/resources/views/discussions/_contributions.blade.php new file mode 100644 index 00000000..bd675e86 --- /dev/null +++ b/resources/views/discussions/_contributions.blade.php @@ -0,0 +1,57 @@ +
+
+

Top Contributeurs

+

Les personnes qui ont lancé le plus de discussions sur le site.

+
+
    + @foreach($topContributors as $contributor) +
  • +
    +
    + +
    +
    +

    + {{ $contributor->name }} +

    +

    + {{ '@' . $contributor->username }} +

    +
    +
    + + {{ $contributor->discussions_count }} + +
    +
    +
  • + @endforeach +
+
+
+ +
+

Discussions sans commentaires

+

Les discussions/sujets qui n’ont pas encore eu de commentaires. Soyez le premier à apporter votre avis.

+ +
+
    + @foreach($discussions as $discussion) +
  • +
    + +
    +
    +

    {{ $discussion->author->name }}

    +

    {{ $discussion->created_at->diffForHumans() }}

    +
    +

    {{ $discussion->title }}

    +
    +
    +
  • + @endforeach +
+
+ +
+
diff --git a/resources/views/discussions/edit.blade.php b/resources/views/discussions/edit.blade.php new file mode 100644 index 00000000..e65c09cf --- /dev/null +++ b/resources/views/discussions/edit.blade.php @@ -0,0 +1,9 @@ +@title('Modifier mon sujet') + +@extends('layouts.master') + +@section('content') + + + +@endsection diff --git a/resources/views/discussions/index.blade.php b/resources/views/discussions/index.blade.php new file mode 100644 index 00000000..e7941392 --- /dev/null +++ b/resources/views/discussions/index.blade.php @@ -0,0 +1,9 @@ +@title('Tous les sujets de discussion') + +@extends('layouts.default') + +@section('body') + + + +@endsection diff --git a/resources/views/discussions/new.blade.php b/resources/views/discussions/new.blade.php new file mode 100644 index 00000000..530e6c50 --- /dev/null +++ b/resources/views/discussions/new.blade.php @@ -0,0 +1,22 @@ +@title('Créer un nouveau sujet de discussion') + +@extends('layouts.default') + +@section('body') + +
+
+
+

+ Créer un sujet de discussion +

+

+ Assurez-vous d'avoir lu nos règles de conduite avant de continuer. +

+
+ + +
+
+ +@endsection diff --git a/resources/views/discussions/show.blade.php b/resources/views/discussions/show.blade.php new file mode 100644 index 00000000..9802585d --- /dev/null +++ b/resources/views/discussions/show.blade.php @@ -0,0 +1,64 @@ +@title($discussion->title) + +@extends('layouts.default') + +@section('body') + +
+
+
+
+

{{ $discussion->title }}

+
+ + + + @if ($discussion->tags->isNotEmpty()) +
+ @foreach ($discussion->tags as $tag) + + @endforeach +
+ @endif +
+
+
+
+
+ + + + +
+
+
+
+

+ {{ $discussion->author->name }} +

+
+
+ +
+
+
+ +
+
+
+
+
+
+

Commentaires ({{ $discussion->replies_count }})

+ @auth + + @endauth +
+
+
+ +
+ +@endsection diff --git a/resources/views/layouts/_nav.blade.php b/resources/views/layouts/_nav.blade.php index 3ea01169..78c3e251 100644 --- a/resources/views/layouts/_nav.blade.php +++ b/resources/views/layouts/_nav.blade.php @@ -17,7 +17,7 @@ {{ __('Vidéos') }} - + {{ __('Discussions') }}
@@ -56,7 +56,7 @@ class="absolute z-10 -ml-4 mt-3 transform w-screen max-w-md lg:max-w-3xl lg:ml-0
- +
@@ -192,7 +192,7 @@ class="origin-top absolute right-0 mt-2 w-56 rounded-md shadow-lg py-1 bg-skin-m
Nouveau sujet - + Nouvelle discussion @@ -286,7 +286,7 @@ class="origin-top-right absolute right-0 mt-2 w-60 rounded-md shadow-lg py-1 bg- {{ __('Forum') }} {{ __('Articles') }} {{ __('Vidéos') }} - {{ __('Discussions') }} + {{ __('Discussions') }}

{{ __('Autres') }}

@@ -299,7 +299,7 @@ class="origin-top-right absolute right-0 mt-2 w-60 rounded-md shadow-lg py-1 bg- {{ __('Snippets') }} - + {{ __('Guides') }} diff --git a/resources/views/livewire/discussions/_form.blade.php b/resources/views/livewire/discussions/_form.blade.php new file mode 100644 index 00000000..e59bb26f --- /dev/null +++ b/resources/views/livewire/discussions/_form.blade.php @@ -0,0 +1,36 @@ +
+ + + +
+ {{ __('Titre') }} + +

Maximum de 160 caractères.

+
+ +
+ Tags + + @foreach($tags as $tag) + + @endforeach + +
+ +
+ {{ __('Votre contenu') }} + +
+ +
+
+ + + Enregistrer + +
+
+
diff --git a/resources/views/livewire/discussions/browse.blade.php b/resources/views/livewire/discussions/browse.blade.php new file mode 100644 index 00000000..04b21c8b --- /dev/null +++ b/resources/views/livewire/discussions/browse.blade.php @@ -0,0 +1,3 @@ +
+ {{-- Be like water. --}} +
diff --git a/resources/views/livewire/discussions/create.blade.php b/resources/views/livewire/discussions/create.blade.php new file mode 100644 index 00000000..c0958488 --- /dev/null +++ b/resources/views/livewire/discussions/create.blade.php @@ -0,0 +1,3 @@ +
+ @include('livewire.discussions._form') +
diff --git a/resources/views/livewire/discussions/edit.blade.php b/resources/views/livewire/discussions/edit.blade.php new file mode 100644 index 00000000..ad58cc83 --- /dev/null +++ b/resources/views/livewire/discussions/edit.blade.php @@ -0,0 +1,3 @@ +
+ {{-- Stop trying to control. --}} +
diff --git a/resources/views/livewire/discussions/subscribe.blade.php b/resources/views/livewire/discussions/subscribe.blade.php new file mode 100644 index 00000000..c3787739 --- /dev/null +++ b/resources/views/livewire/discussions/subscribe.blade.php @@ -0,0 +1,17 @@ +
+ + @can(App\Policies\DiscussionPolicy::UNSUBSCRIBE, $discussion) + + + Se désabonner + + + @elsecan(App\Policies\DiscussionPolicy::SUBSCRIBE, $discussion) + + + S'abonner + + + @endcan + +
diff --git a/resources/views/livewire/forum/subscribe.blade.php b/resources/views/livewire/forum/subscribe.blade.php index 459ad8d1..6bac68ee 100644 --- a/resources/views/livewire/forum/subscribe.blade.php +++ b/resources/views/livewire/forum/subscribe.blade.php @@ -23,11 +23,11 @@ @elsecan(App\Policies\ThreadPolicy::SUBSCRIBE, $thread) - + S'abonner - + @endcan
diff --git a/resources/views/user/profile.blade.php b/resources/views/user/profile.blade.php index 7e90f92b..8d7cd52c 100644 --- a/resources/views/user/profile.blade.php +++ b/resources/views/user/profile.blade.php @@ -147,7 +147,7 @@ class="border-transparent text-skin-base hover:text-skin-inverted-muted hover:bo

{{ $user->name }} n'a pas encore posté de discussions

@if ($user->isLoggedInUser()) - + Nouvelle discussion diff --git a/routes/web.php b/routes/web.php index caa29016..186b9aa0 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,8 +1,8 @@ group(function () { Route::get('/', [ArticlesController::class, 'index'])->name('articles'); Route::get('/new', [ArticlesController::class, 'create'])->name('articles.new'); - Route::post('/new', [ArticlesController::class, 'store'])->name('articles.store'); Route::get('/{article}', [ArticlesController::class, 'show'])->name('articles.show'); Route::get('/{article}/edit', [ArticlesController::class, 'edit'])->name('articles.edit'); }); +// Discussions +Route::prefix('discussions')->as('discussions.')->group(function () { + Route::get('/', [DiscussionController::class, 'index'])->name('index'); + Route::get('/new', [DiscussionController::class, 'create'])->name('new'); + Route::get('/{discussion}', [DiscussionController::class, 'show'])->name('show'); + Route::get('/{discussion}/edit', [DiscussionController::class, 'edit'])->name('edit'); +}); + // Forum Route::prefix('forum')->as('forum.')->group(function () { Route::redirect('/channels', '/forum'); @@ -54,9 +61,6 @@ Route::get('/{thread}/edit', [ThreadController::class, 'edit'])->name('edit'); }); -// Replies -Route::post('replies', [ReplyController::class, 'store'])->name('replies.store'); - // Subscriptions Route::get('subscriptions/{subscription}/unsubscribe', [SubscriptionController::class, 'unsubscribe']) ->name('subscriptions.unsubscribe'); From 83258c4dda60a7a9c48c111752090e7540f90e78 Mon Sep 17 00:00:00 2001 From: Arthur Monney Date: Wed, 17 Nov 2021 17:04:51 +0100 Subject: [PATCH 04/27] :sparkles: mise en place du listing des discussions --- app/Http/Controllers/DiscussionController.php | 2 +- app/Http/Livewire/Articles/Browse.php | 21 +--- app/Http/Livewire/Discussions/Browse.php | 41 ++++++- app/Http/Livewire/Reactions.php | 2 + app/Models/Discussion.php | 1 - app/Traits/WithTags.php | 25 +++++ public/css/app.css | 100 +++++++----------- resources/views/articles/show.blade.php | 2 +- .../components/articles/filter.blade.php | 2 +- .../components/articles/overview.blade.php | 8 +- .../components/discussions/overview.blade.php | 50 +++++++++ .../forum/thread-overview.blade.php | 2 +- .../components/{articles => }/tag.blade.php | 0 resources/views/components/tags.blade.php | 39 +++++++ .../discussions/_contributions.blade.php | 6 +- resources/views/discussions/show.blade.php | 4 +- resources/views/layouts/_nav.blade.php | 8 +- .../views/livewire/articles/browse.blade.php | 21 +--- .../livewire/discussions/browse.blade.php | 66 +++++++++++- resources/views/livewire/reactions.blade.php | 11 +- 20 files changed, 287 insertions(+), 124 deletions(-) create mode 100644 app/Traits/WithTags.php create mode 100644 resources/views/components/discussions/overview.blade.php rename resources/views/components/{articles => }/tag.blade.php (100%) create mode 100644 resources/views/components/tags.blade.php diff --git a/app/Http/Controllers/DiscussionController.php b/app/Http/Controllers/DiscussionController.php index 78d09939..72be812c 100644 --- a/app/Http/Controllers/DiscussionController.php +++ b/app/Http/Controllers/DiscussionController.php @@ -31,7 +31,7 @@ public function show(Discussion $discussion) ->twitterSite('laravelcm') ->withUrl(); - return view('discussions.show', compact('discussion')); + return view('discussions.show', ['discussion' => $discussion->load('tags')]); } public function create() diff --git a/app/Http/Livewire/Articles/Browse.php b/app/Http/Livewire/Articles/Browse.php index b2ec3563..ac154dd7 100644 --- a/app/Http/Livewire/Articles/Browse.php +++ b/app/Http/Livewire/Articles/Browse.php @@ -5,35 +5,18 @@ use App\Models\Article; use App\Models\Tag; use App\Traits\WithInfiniteScroll; +use App\Traits\WithTags; use Livewire\Component; class Browse extends Component { - use WithInfiniteScroll; - - public string $sortBy = 'recent'; - public ?string $tag = null; + use WithInfiniteScroll, WithTags; protected $queryString = [ 'tag' => ['except' => ''], 'sortBy' => ['except' => 'recent'], ]; - public function toggleTag($tag): void - { - $this->tag = $this->tag !== $tag && $this->tagExists($tag) ? $tag : null; - } - - public function sortBy($sort): void - { - $this->sortBy = $this->validSort($sort) ? $sort : 'recent'; - } - - public function tagExists($tag): bool - { - return Tag::where('slug', $tag)->exists(); - } - public function validSort($sort): bool { return in_array($sort, [ diff --git a/app/Http/Livewire/Discussions/Browse.php b/app/Http/Livewire/Discussions/Browse.php index e32e21fa..5f244bb1 100644 --- a/app/Http/Livewire/Discussions/Browse.php +++ b/app/Http/Livewire/Discussions/Browse.php @@ -2,12 +2,51 @@ namespace App\Http\Livewire\Discussions; +use App\Models\Discussion; +use App\Models\Tag; +use App\Traits\WithInfiniteScroll; +use App\Traits\WithTags; use Livewire\Component; class Browse extends Component { + use WithInfiniteScroll, WithTags; + + protected $queryString = [ + 'tag' => ['except' => ''], + 'sortBy' => ['except' => 'recent'], + ]; + + public function validSort($sort): bool + { + return in_array($sort, [ + 'recent', + 'popular', + 'active', + ]); + } + public function render() { - return view('livewire.discussions.browse'); + $discussions = Discussion::with('tags') + ->withCount('replies') + ->notPinned(); + + $tags = Tag::whereJsonContains('concerns', ['discussion'])->orderBy('name')->get(); + + $selectedTag = Tag::where('name', $this->tag)->first(); + + if ($this->tag) { + $discussions->forTag($this->tag); + } + + $discussions->{$this->sortBy}(); + + return view('livewire.discussions.browse', [ + 'discussions' => $discussions->paginate($this->perPage), + 'tags' => $tags, + 'selectedTag' => $selectedTag, + 'selectedSortBy' => $this->sortBy, + ]); } } diff --git a/app/Http/Livewire/Reactions.php b/app/Http/Livewire/Reactions.php index 6bd9b775..c35ecfe8 100644 --- a/app/Http/Livewire/Reactions.php +++ b/app/Http/Livewire/Reactions.php @@ -14,6 +14,8 @@ class Reactions extends Component public Model $model; public bool $withPlaceHolder = true; + public bool $withBackground = true; + public string $direction = 'right'; public function userReacted(string $reaction) { diff --git a/app/Models/Discussion.php b/app/Models/Discussion.php index 68a7b2b1..5121ea51 100644 --- a/app/Models/Discussion.php +++ b/app/Models/Discussion.php @@ -61,7 +61,6 @@ class Discussion extends Model implements ReactableInterface, ReplyInterface, Su * @var array */ protected $with = [ - 'tags', 'author', ]; diff --git a/app/Traits/WithTags.php b/app/Traits/WithTags.php new file mode 100644 index 00000000..06dec1da --- /dev/null +++ b/app/Traits/WithTags.php @@ -0,0 +1,25 @@ +tag = $this->tag !== $tag && $this->tagExists($tag) ? $tag : null; + } + + public function sortBy($sort): void + { + $this->sortBy = $this->validSort($sort) ? $sort : 'recent'; + } + + public function tagExists($tag): bool + { + return Tag::where('slug', $tag)->exists(); + } +} diff --git a/public/css/app.css b/public/css/app.css index d8645f2e..02230929 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -4331,6 +4331,9 @@ select { .bottom-0 { bottom: 0px; } +.-right-1 { + right: -0.25rem; +} .-top-3 { top: -0.75rem; } @@ -4346,9 +4349,6 @@ select { .-bottom-0\.5 { bottom: -0.125rem; } -.-right-1 { - right: -0.25rem; -} .-bottom-0 { bottom: 0px; } @@ -4389,6 +4389,14 @@ select { margin-left: auto; margin-right: auto; } +.mx-2 { + margin-left: 0.5rem; + margin-right: 0.5rem; +} +.mx-0 { + margin-left: 0px; + margin-right: 0px; +} .mx-1\.5 { margin-left: 0.375rem; margin-right: 0.375rem; @@ -4397,10 +4405,6 @@ select { margin-left: 0.25rem; margin-right: 0.25rem; } -.mx-2 { - margin-left: 0.5rem; - margin-right: 0.5rem; -} .my-8 { margin-top: 2rem; margin-bottom: 2rem; @@ -4417,10 +4421,6 @@ select { margin-top: 2.5rem; margin-bottom: 2.5rem; } -.mx-0 { - margin-left: 0px; - margin-right: 0px; -} .-mx-4 { margin-left: -1rem; margin-right: -1rem; @@ -4486,6 +4486,9 @@ select { .-ml-1 { margin-left: -0.25rem; } +.mr-1 { + margin-right: 0.25rem; +} .ml-1\.5 { margin-left: 0.375rem; } @@ -4495,9 +4498,6 @@ select { .mr-1\.5 { margin-right: 0.375rem; } -.mr-1 { - margin-right: 0.25rem; -} .mt-4 { margin-top: 1rem; } @@ -5117,16 +5117,16 @@ select { margin-right: calc(2rem * var(--tw-space-x-reverse)); margin-left: calc(2rem * calc(1 - var(--tw-space-x-reverse))); } -.space-y-5 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1.25rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1.25rem * var(--tw-space-y-reverse)); -} .space-y-16 > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 0; margin-top: calc(4rem * calc(1 - var(--tw-space-y-reverse))); margin-bottom: calc(4rem * var(--tw-space-y-reverse)); } +.space-y-5 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.25rem * var(--tw-space-y-reverse)); +} .space-x-6 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(1.5rem * var(--tw-space-x-reverse)); @@ -5164,10 +5164,6 @@ select { --tw-divide-opacity: 1; border-color: rgba(228, 228, 231, var(--tw-divide-opacity)); } -.divide-gray-200 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-opacity: 1; - border-color: rgba(229, 231, 235, var(--tw-divide-opacity)); -} .self-start { align-self: flex-start; } @@ -5267,15 +5263,15 @@ select { .rounded-tl-sm { border-top-left-radius: 0.125rem; } +.rounded-tl { + border-top-left-radius: 0.25rem; +} .rounded-tr-lg { border-top-right-radius: 0.5rem; } .rounded-br-lg { border-bottom-right-radius: 0.5rem; } -.rounded-tl { - border-top-left-radius: 0.25rem; -} .border { border-width: 1px; } @@ -5701,6 +5697,14 @@ select { padding-left: 0px; padding-right: 0px; } +.px-0\.5 { + padding-left: 0.125rem; + padding-right: 0.125rem; +} +.py-px { + padding-top: 1px; + padding-bottom: 1px; +} .px-3\.5 { padding-left: 0.875rem; padding-right: 0.875rem; @@ -5713,14 +5717,6 @@ select { padding-top: 0.625rem; padding-bottom: 0.625rem; } -.px-0\.5 { - padding-left: 0.125rem; - padding-right: 0.125rem; -} -.py-px { - padding-top: 1px; - padding-bottom: 1px; -} .pr-2 { padding-right: 0.5rem; } @@ -5832,6 +5828,9 @@ select { .pt-12 { padding-top: 3rem; } +.pb-6 { + padding-bottom: 1.5rem; +} .text-left { text-align: left; } @@ -6176,10 +6175,6 @@ select { --tw-text-opacity: 1; color: rgba(127, 29, 29, var(--tw-text-opacity)); } -.text-gray-900 { - --tw-text-opacity: 1; - color: rgba(17, 24, 39, var(--tw-text-opacity)); -} .underline { text-decoration: underline; } @@ -6731,11 +6726,6 @@ html { background-color: rgba(24, 24, 27, var(--tw-bg-opacity)); } -.hover\:bg-gray-50:hover { - --tw-bg-opacity: 1; - background-color: rgba(249, 250, 251, var(--tw-bg-opacity)); -} - .hover\:text-skin-inverted-muted:hover { --tw-text-opacity: 1; color: rgba(var(--color-text-inverted-muted), var(--tw-text-opacity)); @@ -9093,30 +9083,10 @@ html { grid-column: span 10 / span 10; } - .lg\:col-start-9 { - grid-column-start: 9; - } - - .lg\:col-start-8 { - grid-column-start: 8; - } - .lg\:col-start-10 { grid-column-start: 10; } - .lg\:col-end-12 { - grid-column-end: 12; - } - - .lg\:col-end-13 { - grid-column-end: 13; - } - - .lg\:col-end-auto { - grid-column-end: auto; - } - .lg\:mx-0 { margin-left: 0px; margin-right: 0px; @@ -9235,6 +9205,10 @@ html { justify-content: flex-end; } + .lg\:justify-between { + justify-content: space-between; + } + .lg\:gap-6 { gap: 1.5rem; } diff --git a/resources/views/articles/show.blade.php b/resources/views/articles/show.blade.php index 96cc77d3..5f1bb0c9 100644 --- a/resources/views/articles/show.blade.php +++ b/resources/views/articles/show.blade.php @@ -85,7 +85,7 @@ @if ($article->tags->isNotEmpty())
@foreach ($article->tags as $tag) - + @endforeach
@endif diff --git a/resources/views/components/articles/filter.blade.php b/resources/views/components/articles/filter.blade.php index fe08bf61..27332673 100644 --- a/resources/views/components/articles/filter.blade.php +++ b/resources/views/components/articles/filter.blade.php @@ -13,7 +13,7 @@ class="{{ $selectedSortBy === 'recent' ? 'bg-skin-link text-skin-inverted': 'tex - Récents + Récent diff --git a/resources/views/components/articles/overview.blade.php b/resources/views/components/articles/overview.blade.php index 910e7ea5..2253ec8f 100644 --- a/resources/views/components/articles/overview.blade.php +++ b/resources/views/components/articles/overview.blade.php @@ -10,15 +10,15 @@ @if ($article->tags->isNotEmpty())
@foreach ($article->tags as $tag) - + @endforeach
@endif
-

{{ $article->title }}

+

{{ $article->title }}

-

+

{!! $article->excerpt() !!}

@@ -35,7 +35,7 @@

- + {{ $article->readTime() }} min de lecture
diff --git a/resources/views/components/discussions/overview.blade.php b/resources/views/components/discussions/overview.blade.php new file mode 100644 index 00000000..fc602e37 --- /dev/null +++ b/resources/views/components/discussions/overview.blade.php @@ -0,0 +1,50 @@ +@props(['discussion']) + +
+
+ +
+ @if (count($tags = $discussion->tags)) +
+ @foreach ($tags as $tag) + + @endforeach +
+ @endif +
+
+

+ {!! $discussion->excerpt(175) !!} +

+
+
+ + {{ $discussion->author->name }} + + Posté par +
+ {{ $discussion->author->name }} + + +
+
+
+ +

+ + {{ $discussion->replies_count }} + réponses +

+
+
+
diff --git a/resources/views/components/forum/thread-overview.blade.php b/resources/views/components/forum/thread-overview.blade.php index 7e6380aa..fccfae06 100644 --- a/resources/views/components/forum/thread-overview.blade.php +++ b/resources/views/components/forum/thread-overview.blade.php @@ -1,6 +1,6 @@ @props(['thread']) -
+
diff --git a/resources/views/components/articles/tag.blade.php b/resources/views/components/tag.blade.php similarity index 100% rename from resources/views/components/articles/tag.blade.php rename to resources/views/components/tag.blade.php diff --git a/resources/views/components/tags.blade.php b/resources/views/components/tags.blade.php new file mode 100644 index 00000000..e9a75b32 --- /dev/null +++ b/resources/views/components/tags.blade.php @@ -0,0 +1,39 @@ +@props(['tags', 'isLowercase' => false, 'showHashTag' => false]) + +
+ @foreach($tags as $tag) + + @endforeach +
diff --git a/resources/views/discussions/_contributions.blade.php b/resources/views/discussions/_contributions.blade.php index bd675e86..5d8e34cf 100644 --- a/resources/views/discussions/_contributions.blade.php +++ b/resources/views/discussions/_contributions.blade.php @@ -1,4 +1,4 @@ -
+
@@ -54,4 +54,4 @@
-
+ diff --git a/resources/views/discussions/show.blade.php b/resources/views/discussions/show.blade.php index 9802585d..84de5ab1 100644 --- a/resources/views/discussions/show.blade.php +++ b/resources/views/discussions/show.blade.php @@ -4,7 +4,7 @@ @section('body') -
+
@@ -16,7 +16,7 @@ @if ($discussion->tags->isNotEmpty())
@foreach ($discussion->tags as $tag) - + @endforeach
@endif diff --git a/resources/views/layouts/_nav.blade.php b/resources/views/layouts/_nav.blade.php index 78c3e251..2de47fa9 100644 --- a/resources/views/layouts/_nav.blade.php +++ b/resources/views/layouts/_nav.blade.php @@ -14,12 +14,12 @@ {{ __('Articles') }} - - {{ __('Vidéos') }} - {{ __('Discussions') }} + + {{ __('Vidéos') }} +
- @endforeach -
+
-
- {{-- Be like water. --}} +
+ +
+
+ +
+
+ @foreach($discussions as $discussion) + + @endforeach +
+
+
diff --git a/resources/views/livewire/reactions.blade.php b/resources/views/livewire/reactions.blade.php index dde7ecd4..7222bbd2 100644 --- a/resources/views/livewire/reactions.blade.php +++ b/resources/views/livewire/reactions.blade.php @@ -10,7 +10,10 @@ class="flex items-center text-skin-base hover:underline text-sm leading-5 focus: @else +
+ @endcan
diff --git a/resources/views/livewire/discussions/edit.blade.php b/resources/views/livewire/discussions/edit.blade.php index ad58cc83..c0958488 100644 --- a/resources/views/livewire/discussions/edit.blade.php +++ b/resources/views/livewire/discussions/edit.blade.php @@ -1,3 +1,3 @@
- {{-- Stop trying to control. --}} + @include('livewire.discussions._form')
diff --git a/resources/views/livewire/modals/delete-discussion.blade.php b/resources/views/livewire/modals/delete-discussion.blade.php new file mode 100644 index 00000000..b49f2a4b --- /dev/null +++ b/resources/views/livewire/modals/delete-discussion.blade.php @@ -0,0 +1,30 @@ + + +
+
+ +
+
+ +
+

+ {{ __('Voulez-vous vraiment supprimer cette discussion? Tous les commentaires seront supprimés cette action est irréversible.') }} +

+
+
+
+
+ + + + + + + + {{ __('Annuler') }} + + + +
From 2d643cc7a86609cc18aca647fd3954b26048cadd Mon Sep 17 00:00:00 2001 From: Arthur Monney Date: Thu, 18 Nov 2021 00:23:15 +0100 Subject: [PATCH 07/27] :bento: ajout de la config js pour webpack --- jsconfig.json | 12 ++++++++++++ webpack.mix.js | 24 ++++++++++++++++++------ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/jsconfig.json b/jsconfig.json index e69de29b..04f7ddba 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es2017", + "allowSyntheticDefaultImports": false, + "baseUrl": "./", + "paths": { + "@/*": ["resources/js/*"], + "@components/*": ["resources/js/components/*"] + } + }, + "exclude": ["node_modules", "public"] +} diff --git a/webpack.mix.js b/webpack.mix.js index 9f45d59f..4637e4c2 100644 --- a/webpack.mix.js +++ b/webpack.mix.js @@ -1,4 +1,5 @@ const mix = require('laravel-mix'); +const path = require('path'); /* |-------------------------------------------------------------------------- @@ -19,11 +20,22 @@ mix.js('resources/js/app.js', 'public/js') require('tailwindcss'), require('autoprefixer'), ], + }) + .webpackConfig({ + output: { chunkFilename: 'js/[name].js?id=[chunkhash]' }, + resolve: { + extensions: ['*', '.js', '.jsx'], + alias: { + '@': path.resolve('./resources/js'), + '@components': path.resolve('./resources/js/components'), + }, + modules: [ + 'node_modules', + __dirname + '/vendor/spatie/laravel-medialibrary-pro/resources/js', + ] + } }); -mix.override((webpackConfig) => { - webpackConfig.resolve.modules = [ - "node_modules", - __dirname + "/vendor/spatie/laravel-medialibrary-pro/resources/js", - ]; -}); +if (mix.inProduction()) { + mix.version(); +} From 91096be8e3f0cc4598f80c2a43d2354425d10d04 Mon Sep 17 00:00:00 2001 From: Arthur Monney Date: Thu, 18 Nov 2021 00:46:54 +0100 Subject: [PATCH 08/27] :sparkles: ajout des discussions dans le profil utilisateur --- app/Http/Livewire/User/Activities.php | 5 -- app/Http/Livewire/User/Articles.php | 12 +-- app/Http/Livewire/User/Discussions.php | 22 ++++++ app/Http/Livewire/User/Threads.php | 11 ++- public/css/app.css | 75 +++++++++++-------- .../feeds/created_discussion.blade.php | 22 ++++++ resources/views/errors/minimal.blade.php | 2 +- .../views/livewire/user/discussions.blade.php | 22 ++++++ resources/views/user/profile.blade.php | 13 +--- 9 files changed, 121 insertions(+), 63 deletions(-) create mode 100644 app/Http/Livewire/User/Discussions.php create mode 100644 resources/views/livewire/user/discussions.blade.php diff --git a/app/Http/Livewire/User/Activities.php b/app/Http/Livewire/User/Activities.php index 821aadb6..2e73ff86 100644 --- a/app/Http/Livewire/User/Activities.php +++ b/app/Http/Livewire/User/Activities.php @@ -10,11 +10,6 @@ class Activities extends Component { public User $user; - public function mount(User $user) - { - $this->user = $user; - } - public function render() { return view('livewire.user.activities', [ diff --git a/app/Http/Livewire/User/Articles.php b/app/Http/Livewire/User/Articles.php index 05152a1f..01603ab4 100644 --- a/app/Http/Livewire/User/Articles.php +++ b/app/Http/Livewire/User/Articles.php @@ -9,15 +9,15 @@ class Articles extends Component { public User $user; - public function mount(User $user) - { - $this->user = $user; - } - public function render() { return view('livewire.user.articles', [ - 'articles' => $this->user->articles()->with('tags')->published()->recent()->limit(5)->get(), + 'articles' => $this->user->articles() + ->with('tags') + ->published() + ->recent() + ->limit(5) + ->get(), ]); } } diff --git a/app/Http/Livewire/User/Discussions.php b/app/Http/Livewire/User/Discussions.php new file mode 100644 index 00000000..adaa10f2 --- /dev/null +++ b/app/Http/Livewire/User/Discussions.php @@ -0,0 +1,22 @@ + $this->user->discussions() + ->with('tags') + ->withCount('replies') + ->limit(5) + ->get(), + ]); + } +} diff --git a/app/Http/Livewire/User/Threads.php b/app/Http/Livewire/User/Threads.php index b9f7cde4..a5425818 100644 --- a/app/Http/Livewire/User/Threads.php +++ b/app/Http/Livewire/User/Threads.php @@ -9,15 +9,14 @@ class Threads extends Component { public User $user; - public function mount(User $user) - { - $this->user = $user; - } - public function render() { return view('livewire.user.threads', [ - 'threads' => $this->user->threads()->with(['solutionReply', 'channels', 'reactions'])->orderByDesc('created_at')->limit(5)->get(), + 'threads' => $this->user->threads() + ->with(['solutionReply', 'channels', 'reactions']) + ->orderByDesc('created_at') + ->limit(5) + ->get(), ]); } } diff --git a/public/css/app.css b/public/css/app.css index 02230929..5ae99995 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -4519,12 +4519,6 @@ select { .-mb-8 { margin-bottom: -2rem; } -.mb-1\.5 { - margin-bottom: 0.375rem; -} -.mb-1 { - margin-bottom: 0.25rem; -} .mt-12 { margin-top: 3rem; } @@ -4564,6 +4558,12 @@ select { .mb-3 { margin-bottom: 0.75rem; } +.mb-1\.5 { + margin-bottom: 0.375rem; +} +.mb-1 { + margin-bottom: 0.25rem; +} .mb-2 { margin-bottom: 0.5rem; } @@ -4600,6 +4600,9 @@ select { .-ml-0 { margin-left: 0px; } +.-mt-6 { + margin-top: -1.5rem; +} .block { display: block; } @@ -5217,15 +5220,15 @@ select { .rounded-full { border-radius: 9999px; } -.rounded { - border-radius: 0.25rem; -} .rounded-sm { border-radius: 0.125rem; } .rounded-xl { border-radius: 0.75rem; } +.rounded { + border-radius: 0.25rem; +} .rounded-none { border-radius: 0px; } @@ -5774,12 +5777,18 @@ select { .pr-3 { padding-right: 0.75rem; } +.pr-1 { + padding-right: 0.25rem; +} .pb-3 { padding-bottom: 0.75rem; } .pr-4 { padding-right: 1rem; } +.pb-6 { + padding-bottom: 1.5rem; +} .pl-16 { padding-left: 4rem; } @@ -5807,9 +5816,6 @@ select { .pr-2\.5 { padding-right: 0.625rem; } -.pr-1 { - padding-right: 0.25rem; -} .pr-14 { padding-right: 3.5rem; } @@ -5828,9 +5834,6 @@ select { .pt-12 { padding-top: 3rem; } -.pb-6 { - padding-bottom: 1.5rem; -} .text-left { text-align: left; } @@ -5853,22 +5856,18 @@ select { font-size: 0.875rem; line-height: 1.25rem; } +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} .text-xl { font-size: 1.25rem; line-height: 1.75rem; } -.text-base { - font-size: 1rem; - line-height: 1.5rem; -} .text-xs { font-size: 0.75rem; line-height: 1rem; } -.text-lg { - font-size: 1.125rem; - line-height: 1.75rem; -} .text-2xl { font-size: 1.5rem; line-height: 2rem; @@ -5881,6 +5880,10 @@ select { font-size: 2.25rem; line-height: 2.5rem; } +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} .text-\[12px\] { font-size: 12px; } @@ -5894,18 +5897,18 @@ select { .font-medium { font-weight: 500; } -.font-bold { - font-weight: 700; +.font-semibold { + font-weight: 600; } .font-normal { font-weight: 300; } -.font-semibold { - font-weight: 600; -} .font-extrabold { font-weight: 800; } +.font-bold { + font-weight: 700; +} .uppercase { text-transform: uppercase; } @@ -5918,9 +5921,6 @@ select { .italic { font-style: italic; } -.leading-7 { - line-height: 1.75rem; -} .leading-6 { line-height: 1.5rem; } @@ -5939,6 +5939,9 @@ select { .leading-8 { line-height: 2rem; } +.leading-7 { + line-height: 1.75rem; +} .leading-none { line-height: 1; } @@ -7581,14 +7584,20 @@ html { margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); } - .sm\:rounded-lg { - border-radius: 0.5rem; + .sm\:space-y-8 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(2rem * var(--tw-space-y-reverse)); } .sm\:rounded-xl { border-radius: 0.75rem; } + .sm\:rounded-lg { + border-radius: 0.5rem; + } + .sm\:border-0 { border-width: 0px; } diff --git a/resources/views/components/feeds/created_discussion.blade.php b/resources/views/components/feeds/created_discussion.blade.php index e69de29b..356e9042 100644 --- a/resources/views/components/feeds/created_discussion.blade.php +++ b/resources/views/components/feeds/created_discussion.blade.php @@ -0,0 +1,22 @@ +@props(['activity']) + +
  • +
    + +
    +
    + + + +
    +
    +
    +

    a démarré une conversation {{ $activity->subject->title }}

    +
    +
    + +
    +
    +
    +
    +
  • diff --git a/resources/views/errors/minimal.blade.php b/resources/views/errors/minimal.blade.php index f546b0d4..2db4e73c 100644 --- a/resources/views/errors/minimal.blade.php +++ b/resources/views/errors/minimal.blade.php @@ -85,7 +85,7 @@

    - + Guides diff --git a/resources/views/livewire/user/discussions.blade.php b/resources/views/livewire/user/discussions.blade.php new file mode 100644 index 00000000..f5236ed8 --- /dev/null +++ b/resources/views/livewire/user/discussions.blade.php @@ -0,0 +1,22 @@ +
    + @if($discussions->isNotEmpty()) +
    + @foreach($discussions as $discussion) + + @endforeach +
    + @else +
    +
    + +

    {{ $user->name }} n'a pas encore posté de discussions

    + @if ($user->isLoggedInUser()) + + + Nouvelle discussion + + @endif +
    +
    + @endif +
    diff --git a/resources/views/user/profile.blade.php b/resources/views/user/profile.blade.php index 8d7cd52c..e5bd7530 100644 --- a/resources/views/user/profile.blade.php +++ b/resources/views/user/profile.blade.php @@ -142,18 +142,7 @@ class="border-transparent text-skin-base hover:text-skin-inverted-muted hover:bo

    -
    -
    - -

    {{ $user->name }} n'a pas encore posté de discussions

    - @if ($user->isLoggedInUser()) - - - Nouvelle discussion - - @endif -
    -
    +
    From 926df0cb19d5b4a9d450a35dbe3826cff9814624 Mon Sep 17 00:00:00 2001 From: Arthur Monney Date: Thu, 18 Nov 2021 08:33:40 +0100 Subject: [PATCH 09/27] :sparkles: modification de l'affichage des dates avec le time-ago customElement --- resources/js/elements/Confetti.js | 0 resources/js/elements/TimeAgo.js | 0 resources/js/elements/TimeCountdown.js | 0 resources/js/elements/index.js | 0 resources/js/helpers/api.js | 0 resources/js/helpers/auth.js | 0 resources/js/helpers/cookie.js | 0 resources/js/helpers/darkMode.js | 0 resources/js/helpers/preact.js | 0 resources/js/helpers/string.js | 0 resources/js/helpers/window.js | 0 .../views/components/discussions/overview.blade.php | 2 +- .../views/components/forum/thread-overview.blade.php | 2 +- resources/views/discussions/show.blade.php | 5 ++++- resources/views/forum/thread.blade.php | 7 +++++-- resources/views/layouts/master.blade.php | 10 +++++++++- resources/views/user/profile.blade.php | 4 ++-- 17 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 resources/js/elements/Confetti.js create mode 100644 resources/js/elements/TimeAgo.js create mode 100644 resources/js/elements/TimeCountdown.js create mode 100644 resources/js/elements/index.js create mode 100644 resources/js/helpers/api.js create mode 100644 resources/js/helpers/auth.js create mode 100644 resources/js/helpers/cookie.js create mode 100644 resources/js/helpers/darkMode.js create mode 100644 resources/js/helpers/preact.js create mode 100644 resources/js/helpers/string.js create mode 100644 resources/js/helpers/window.js diff --git a/resources/js/elements/Confetti.js b/resources/js/elements/Confetti.js new file mode 100644 index 00000000..e69de29b diff --git a/resources/js/elements/TimeAgo.js b/resources/js/elements/TimeAgo.js new file mode 100644 index 00000000..e69de29b diff --git a/resources/js/elements/TimeCountdown.js b/resources/js/elements/TimeCountdown.js new file mode 100644 index 00000000..e69de29b diff --git a/resources/js/elements/index.js b/resources/js/elements/index.js new file mode 100644 index 00000000..e69de29b diff --git a/resources/js/helpers/api.js b/resources/js/helpers/api.js new file mode 100644 index 00000000..e69de29b diff --git a/resources/js/helpers/auth.js b/resources/js/helpers/auth.js new file mode 100644 index 00000000..e69de29b diff --git a/resources/js/helpers/cookie.js b/resources/js/helpers/cookie.js new file mode 100644 index 00000000..e69de29b diff --git a/resources/js/helpers/darkMode.js b/resources/js/helpers/darkMode.js new file mode 100644 index 00000000..e69de29b diff --git a/resources/js/helpers/preact.js b/resources/js/helpers/preact.js new file mode 100644 index 00000000..e69de29b diff --git a/resources/js/helpers/string.js b/resources/js/helpers/string.js new file mode 100644 index 00000000..e69de29b diff --git a/resources/js/helpers/window.js b/resources/js/helpers/window.js new file mode 100644 index 00000000..e69de29b diff --git a/resources/views/components/discussions/overview.blade.php b/resources/views/components/discussions/overview.blade.php index fc602e37..23ee88c0 100644 --- a/resources/views/components/discussions/overview.blade.php +++ b/resources/views/components/discussions/overview.blade.php @@ -29,7 +29,7 @@
    {{ $discussion->author->name }} - +
    diff --git a/resources/views/components/forum/thread-overview.blade.php b/resources/views/components/forum/thread-overview.blade.php index fccfae06..6f45e0c1 100644 --- a/resources/views/components/forum/thread-overview.blade.php +++ b/resources/views/components/forum/thread-overview.blade.php @@ -16,7 +16,7 @@

    - +

    diff --git a/resources/views/discussions/show.blade.php b/resources/views/discussions/show.blade.php index 6364fa99..c10ef3c8 100644 --- a/resources/views/discussions/show.blade.php +++ b/resources/views/discussions/show.blade.php @@ -38,7 +38,9 @@

    - + + Crée +
    @@ -69,6 +71,7 @@ @endauth
    +
    diff --git a/resources/views/layouts/master.blade.php b/resources/views/layouts/master.blade.php index 0f804d3b..2eb063f4 100644 --- a/resources/views/layouts/master.blade.php +++ b/resources/views/layouts/master.blade.php @@ -14,6 +14,9 @@ {{ config('app.name') }} {{ is_active('home') ? '- La plus grande communauté de développeurs Laravel & PHP au Cameroun' : '' }} + + + @@ -22,7 +25,12 @@ @livewireStyles diff --git a/resources/views/user/profile.blade.php b/resources/views/user/profile.blade.php index e5bd7530..9ef2112f 100644 --- a/resources/views/user/profile.blade.php +++ b/resources/views/user/profile.blade.php @@ -25,7 +25,7 @@ @endif

    - {{ __('Inscrit depuis') }} + {{ __('Inscrit') }}

    @@ -47,7 +47,7 @@ {{ $user->name }}

    - {{ __('Inscrit depuis') }} + {{ __('Inscrit') }}

    From 11f82c05df829ee85f4b229e3b8c12e79bc2899d Mon Sep 17 00:00:00 2001 From: Arthur Monney Date: Fri, 19 Nov 2021 16:05:49 +0100 Subject: [PATCH 10/27] :card_file_box: update des tags seeder --- babel.config.json | 0 database/seeders/TagSeeder.php | 3 ++- resources/js/api/comments.js | 0 resources/js/components/Comments.jsx | 0 resources/js/helpers/animation.js | 0 resources/js/helpers/dom.js | 0 resources/js/helpers/hooks.js | 0 resources/views/components/user-status.blade.php | 0 8 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 babel.config.json create mode 100644 resources/js/api/comments.js create mode 100644 resources/js/components/Comments.jsx create mode 100644 resources/js/helpers/animation.js create mode 100644 resources/js/helpers/dom.js create mode 100644 resources/js/helpers/hooks.js create mode 100644 resources/views/components/user-status.blade.php diff --git a/babel.config.json b/babel.config.json new file mode 100644 index 00000000..e69de29b diff --git a/database/seeders/TagSeeder.php b/database/seeders/TagSeeder.php index 0c98eb8d..73a70f39 100644 --- a/database/seeders/TagSeeder.php +++ b/database/seeders/TagSeeder.php @@ -21,7 +21,7 @@ public function run() $this->createTag('Packages', 'packages', ['post', 'tutorial']); $this->createTag('Design', 'design', ['post', 'tutorial']); $this->createTag('PHP', 'php', ['post', 'tutorial']); - $this->createTag('Tailwind', 'tailwind', ['post', 'tutorial']); + $this->createTag('TailwindCSS', 'tailwindcss', ['post', 'tutorial']); $this->createTag('JavaScript', 'javascript', ['post', 'tutorial']); $this->createTag('Applications', 'applications', ['post', 'tutorial']); $this->createTag('React', 'react', ['post', 'tutorial']); @@ -35,6 +35,7 @@ public function run() $this->createTag('Branding', 'branding', ['discussion']); $this->createTag('Projets', 'projets', ['discussion']); $this->createTag('Paiement en ligne', 'paiement-en-ligne', ['discussion']); + $this->createTag('Développement', 'developpement', ['discussion']); } private function createTag(string $name, string $slug, array $concerns = []): void diff --git a/resources/js/api/comments.js b/resources/js/api/comments.js new file mode 100644 index 00000000..e69de29b diff --git a/resources/js/components/Comments.jsx b/resources/js/components/Comments.jsx new file mode 100644 index 00000000..e69de29b diff --git a/resources/js/helpers/animation.js b/resources/js/helpers/animation.js new file mode 100644 index 00000000..e69de29b diff --git a/resources/js/helpers/dom.js b/resources/js/helpers/dom.js new file mode 100644 index 00000000..e69de29b diff --git a/resources/js/helpers/hooks.js b/resources/js/helpers/hooks.js new file mode 100644 index 00000000..e69de29b diff --git a/resources/views/components/user-status.blade.php b/resources/views/components/user-status.blade.php new file mode 100644 index 00000000..e69de29b From 891c119dfaa034ae196882addf33d29337d1a56a Mon Sep 17 00:00:00 2001 From: Arthur Monney Date: Fri, 19 Nov 2021 16:06:42 +0100 Subject: [PATCH 11/27] :heavy_plus_sign: installation et configuration de preact et babel avec laarvel mix --- babel.config.json | 29 +++++++++++++++++++++++++++++ jsconfig.json | 4 +++- package.json | 14 +++++++++++++- webpack.mix.js | 6 +++++- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/babel.config.json b/babel.config.json index e69de29b..cc6acf7d 100644 --- a/babel.config.json +++ b/babel.config.json @@ -0,0 +1,29 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "targets": "last 2 versions, not dead, not ie 6-11", + "modules": false, + "useBuiltIns": false, + "corejs": null + } + ] + ], + "plugins": [ + ["@babel/plugin-transform-react-jsx", { "pragma": "h", "pragmaFrag": "Fragment" }], + [ + "babel-plugin-jsx-pragmatic", + { + "module": "preact", + "import": "h, Fragment", + "export": "h, Fragment" + } + ] + ], + "env": { + "test": { + "presets": [["@babel/preset-env"]] + } + } +} diff --git a/jsconfig.json b/jsconfig.json index 04f7ddba..7db190c7 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -5,7 +5,9 @@ "baseUrl": "./", "paths": { "@/*": ["resources/js/*"], - "@components/*": ["resources/js/components/*"] + "@api/*": ["resources/js/api/*"], + "@components/*": ["resources/js/components/*"], + "@helpers/*": ["resources/js/helpers/*"] } }, "exclude": ["node_modules", "public"] diff --git a/package.json b/package.json index 20588d5a..492ca11f 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,18 @@ "production": "mix --production" }, "devDependencies": { + "@babel/core": "^7.12.10", + "@babel/plugin-transform-react-jsx": "^7.12.12", + "@babel/plugin-transform-runtime": "^7.16.4", + "@babel/preset-env": "^7.12.11", + "@babel/preset-react": "^7.16.0", + "babel-plugin-jsx-pragmatic": "^1.0.2", + "eslint-config-preact": "^1.1.3", "laravel-mix": "^6.0.6", "lodash": "^4.17.19", - "postcss": "^8.2.9" + "postcss": "^8.2.9", + "prettier": "^2.3.1", + "prettier-standard": "^16.4.1" }, "dependencies": { "@tailwindcss/aspect-ratio": "^0.2.0", @@ -22,9 +31,12 @@ "alpinejs": "^3.2.0", "autoprefixer": "^10.2.6", "axios": "^0.21.1", + "canvas-confetti": "^1.4.0", "choices.js": "^9.0.1", "highlight.js": "^10.7.1", + "htm": "^3.1.0", "intl-tel-input": "^17.0.13", + "preact": "^10.5.15", "tailwindcss": "^2.2.4" } } diff --git a/webpack.mix.js b/webpack.mix.js index 4637e4c2..55e15ceb 100644 --- a/webpack.mix.js +++ b/webpack.mix.js @@ -14,7 +14,7 @@ const path = require('path'); mix.disableNotifications(); -mix.js('resources/js/app.js', 'public/js') +mix.js('resources/js/app.js', 'public/js').react() .postCss('resources/css/app.css', 'public/css').options({ postCss: [ require('tailwindcss'), @@ -27,7 +27,11 @@ mix.js('resources/js/app.js', 'public/js') extensions: ['*', '.js', '.jsx'], alias: { '@': path.resolve('./resources/js'), + '@api': path.resolve('./resources/js/api'), '@components': path.resolve('./resources/js/components'), + '@helpers': path.resolve('./resources/js/helpers'), + "react": "preact/compat", + "react-dom": "preact/compat" }, modules: [ 'node_modules', From 3e8ade6e0dbe321baec424ad298276eb6f496a2a Mon Sep 17 00:00:00 2001 From: Arthur Monney Date: Fri, 19 Nov 2021 16:07:32 +0100 Subject: [PATCH 12/27] :sparkles: Ajout des controllers, api, et modification des services providers --- app/Http/Controllers/Api/ReplyController.php | 35 +++++++++++++++++ app/Http/Resources/ReplyResource.php | 21 ++++++++++ app/Http/Resources/UserResource.php | 18 +++++++++ app/Models/Reply.php | 24 +++++++++++- app/Providers/AppServiceProvider.php | 39 ++++++++++++++++--- .../Composers/InactiveDiscussionsComposer.php | 2 +- 6 files changed, 130 insertions(+), 9 deletions(-) create mode 100644 app/Http/Controllers/Api/ReplyController.php create mode 100644 app/Http/Resources/ReplyResource.php create mode 100644 app/Http/Resources/UserResource.php diff --git a/app/Http/Controllers/Api/ReplyController.php b/app/Http/Controllers/Api/ReplyController.php new file mode 100644 index 00000000..60311cad --- /dev/null +++ b/app/Http/Controllers/Api/ReplyController.php @@ -0,0 +1,35 @@ +replies); + } + + public function store() + { + + } + + public function update() + { + + } + + public function delete() + { + + } +} diff --git a/app/Http/Resources/ReplyResource.php b/app/Http/Resources/ReplyResource.php new file mode 100644 index 00000000..268f243c --- /dev/null +++ b/app/Http/Resources/ReplyResource.php @@ -0,0 +1,21 @@ + $this->id, + 'body' => $this->body, + 'model_type' => $this->replyable_type, + 'model_id' => $this->replyable_id, + 'created_at' => $this->created_at->getTimestamp(), + 'author' => new UserResource($this->author), + 'replies' => $this->replyable_type === 'discussion' ? self::collection($this->replies) : null, + ]; + } +} diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php new file mode 100644 index 00000000..50a4e838 --- /dev/null +++ b/app/Http/Resources/UserResource.php @@ -0,0 +1,18 @@ + $this->id, + 'name' => $this->name, + 'username' => $this->username, + 'profile_photo_url' => $this->profile_photo_url, + ]; + } +} diff --git a/app/Models/Reply.php b/app/Models/Reply.php index 0d705597..d405dcd6 100644 --- a/app/Models/Reply.php +++ b/app/Models/Reply.php @@ -5,6 +5,7 @@ use App\Contracts\ReactableInterface; use App\Contracts\ReplyInterface; use App\Traits\HasAuthor; +use App\Traits\HasReplies; use App\Traits\Reactable; use App\Traits\RecordsActivity; use Carbon\Carbon; @@ -15,9 +16,13 @@ use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Support\Str; -class Reply extends Model implements ReactableInterface +class Reply extends Model implements ReactableInterface, ReplyInterface { - use HasAuthor, HasFactory, Reactable, RecordsActivity; + use HasAuthor, + HasFactory, + HasReplies, + Reactable, + RecordsActivity; /** * The attributes that are mass assignable. @@ -33,6 +38,21 @@ public static function boot() parent::boot(); } + public function subject(): string + { + return $this->id; + } + + public function replyAbleSubject(): string + { + return $this->id; + } + + public function getPathUrl(): string + { + return "#reply-{$this->id}"; + } + public function solutionTo(): HasOne { return $this->hasOne(Thread::class, 'solution_reply_id'); diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 474b5ed7..eb8e3972 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,11 +2,18 @@ namespace App\Providers; +use App\Http\Resources\ReplyResource; +use App\Models\Article; +use App\Models\Discussion; +use App\Models\Reply; +use App\Models\Thread; +use App\Models\User; use App\View\Composers\ChannelsComposer; use App\View\Composers\InactiveDiscussionsComposer; use App\View\Composers\ModeratorsComposer; use App\View\Composers\TopContributorsComposer; use App\View\Composers\TopMembersComposer; +use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Support\Facades\Blade; use Illuminate\Support\Facades\View; use Illuminate\Support\ServiceProvider; @@ -31,14 +38,13 @@ public function register() */ public function boot() { - Str::macro('readDuration', function (...$text) { - $totalWords = str_word_count(implode(' ', $text)); - $minutesToRead = round($totalWords / 200); - - return (int) max(1, $minutesToRead); - }); + date_default_timezone_set('Africa/Douala'); + $this->bootMacros(); $this->bootViewsComposer(); + $this->bootEloquentMorphs(); + + ReplyResource::withoutWrapping(); } public function registerBladeDirective() @@ -56,6 +62,16 @@ public function registerBladeDirective() }); } + public function bootMacros() + { + Str::macro('readDuration', function (...$text) { + $totalWords = str_word_count(implode(' ', $text)); + $minutesToRead = round($totalWords / 200); + + return (int) max(1, $minutesToRead); + }); + } + public function bootViewsComposer() { View::composer('forum._channels', ChannelsComposer::class); @@ -64,4 +80,15 @@ public function bootViewsComposer() View::composer('discussions._contributions', TopContributorsComposer::class); View::composer('discussions._contributions', InactiveDiscussionsComposer::class); } + + public function bootEloquentMorphs() + { + Relation::morphMap([ + 'article' => Article::class, + 'discussion' => Discussion::class, + 'thread' => Thread::class, + 'reply' => Reply::class, + 'user' => User::class, + ]); + } } diff --git a/app/View/Composers/InactiveDiscussionsComposer.php b/app/View/Composers/InactiveDiscussionsComposer.php index acb17304..944036f0 100644 --- a/app/View/Composers/InactiveDiscussionsComposer.php +++ b/app/View/Composers/InactiveDiscussionsComposer.php @@ -10,7 +10,7 @@ class InactiveDiscussionsComposer { public function compose(View $view) { - $discussions = Cache::remember('inactive-discussions', 60 * 60 * 24, function () { + $discussions = Cache::remember('inactive-discussions', 60 * 30, function () { return Discussion::noComments()->limit(5)->get(); }); From 82fe473d23e91f8713b1b6703b2aee57a7b7b587 Mon Sep 17 00:00:00 2001 From: Arthur Monney Date: Fri, 19 Nov 2021 16:08:03 +0100 Subject: [PATCH 13/27] :recycle: refactoring des composants et vues --- resources/views/components/forum/thread-author.blade.php | 3 --- resources/views/components/user-status.blade.php | 3 +++ resources/views/discussions/_contributions.blade.php | 6 ++++-- resources/views/discussions/show.blade.php | 7 +++++-- resources/views/user/profile.blade.php | 4 +--- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/resources/views/components/forum/thread-author.blade.php b/resources/views/components/forum/thread-author.blade.php index 013e9d6e..5e77eaa0 100644 --- a/resources/views/components/forum/thread-author.blade.php +++ b/resources/views/components/forum/thread-author.blade.php @@ -6,9 +6,6 @@
    {{ $author->name }} - @if($author->hasAnyRole('admin', 'moderator')) - Admin - @endif

    {{ '@'. $author->username }}

    diff --git a/resources/views/components/user-status.blade.php b/resources/views/components/user-status.blade.php index e69de29b..01ccc183 100644 --- a/resources/views/components/user-status.blade.php +++ b/resources/views/components/user-status.blade.php @@ -0,0 +1,3 @@ + + Modérateur + diff --git a/resources/views/discussions/_contributions.blade.php b/resources/views/discussions/_contributions.blade.php index 5d8e34cf..94da61f8 100644 --- a/resources/views/discussions/_contributions.blade.php +++ b/resources/views/discussions/_contributions.blade.php @@ -42,8 +42,10 @@
    -

    {{ $discussion->author->name }}

    -

    {{ $discussion->created_at->diffForHumans() }}

    +

    + {{ $discussion->author->name }} +

    +

    {{ $discussion->title }}
    diff --git a/resources/views/discussions/show.blade.php b/resources/views/discussions/show.blade.php index c10ef3c8..3e7f3a68 100644 --- a/resources/views/discussions/show.blade.php +++ b/resources/views/discussions/show.blade.php @@ -33,8 +33,11 @@
    -

    +

    {{ $discussion->author->name }} + @if($discussion->author->hasAnyRole('admin', 'moderator')) + + @endif

    @@ -72,7 +75,7 @@ @endauth
    -
    +