Skip to content

Commit

Permalink
Update marking things as read or unread to reflect in the bell icon
Browse files Browse the repository at this point in the history
  • Loading branch information
austinkregel committed Feb 13, 2024
1 parent 05495dd commit 0afaea1
Show file tree
Hide file tree
Showing 22 changed files with 265 additions and 272 deletions.
13 changes: 12 additions & 1 deletion app/Events/Models/Message/MessageCreated.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,22 @@

use App\Events\AbstractLogicalEvent;
use App\Models\Message;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class MessageCreated extends AbstractLogicalEvent
class MessageCreated extends AbstractLogicalEvent implements ShouldBroadcast
{
public function __construct(
public Message $model,
) {
}

public function broadcastOn()
{
$this->model->load('credential');

return [
new PrivateChannel('App.Models.User.'.$this->model->credential->user_id,)
];
}
}
13 changes: 11 additions & 2 deletions app/Http/Controllers/Api/Mail/MarkAsReadController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,27 @@
namespace App\Http\Controllers\Api\Mail;

use App\Http\Controllers\Controller;
use App\Http\Requests\Messages\MarkAsReadRequest;
use App\Http\Requests\Messages\MailOwnerRequest;
use App\Models\Message;
use App\Services\ImapService;
use App\Services\Messaging\ImapFactoryService;

class MarkAsReadController extends Controller
{
public function __invoke(MarkAsReadRequest $request, ImapFactoryService $factoryService)
public function __invoke(MailOwnerRequest $request, ImapFactoryService $factoryService)
{
request()->validate([
'id' => 'integer',
]);

$message = Message::query()
->with('credential')
->findOrFail($request->get('id'));

$service = $factoryService->make($message->credential);
$service->markAsRead($message->event_id);
$message->update([
'seen' => true,
]);
}
}
15 changes: 13 additions & 2 deletions app/Http/Controllers/Api/Mail/MarkAsUnreadController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,27 @@
namespace App\Http\Controllers\Api\Mail;

use App\Http\Controllers\Controller;
use App\Http\Requests\Messages\MailOwnerRequest;
use App\Models\Message;
use App\Services\ImapService;
use App\Services\Messaging\ImapFactoryService;

class MarkAsUnreadController extends Controller
{
public function __invoke(ImapService $imap)
public function __invoke(MailOwnerRequest $request, ImapFactoryService $factoryService)
{
request()->validate([
'id' => 'integer',
]);

$imap->markAsUnread(request('id'));
$message = Message::query()
->with('credential')
->findOrFail($request->get('id'));

$service = $factoryService->make($message->credential);
$service->markAsUnread($message->event_id);
$message->update([
'seen' => false,
]);
}
}
7 changes: 7 additions & 0 deletions app/Http/Middleware/HandleInertiaRequests.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ public function share(Request $request): array
'page',
request('page')
),
'unread_email_count' => $request->user() ?
$request->user()->messages()
->where('messages.type', 'email')
->where('seen', false)
->count()
: 0,
'notifications' => $request->user()?->notifications ?? [],
]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,15 @@
use App\Models\Message;
use Illuminate\Foundation\Http\FormRequest;

class MarkAsReadRequest extends FormRequest
class MailOwnerRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
$messageId = $this->get('id');
$message = Message::findOrFail($messageId);

dd($message);

return false;
return $this->user()->credentials()->where('id', $message->credential_id)->exists();
}

/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
Expand Down
2 changes: 1 addition & 1 deletion app/Jobs/SyncMailboxIfCredentialsAreSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public function __construct(
protected Credential $credential,
protected ?Carbon $since = null,
) {
$this->since = now()->subDay();
$this->since = now()->subMonth();
}

public function handle(ImapFactoryService $imapFactory): void
Expand Down
8 changes: 8 additions & 0 deletions app/Models/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
use Spatie\Tags\HasTags;
use Staudenmeir\EloquentJsonRelations\HasJsonRelationships;

/**
* @property-read Credential $credential
*/
/** @mixin \Eloquent */
class Message extends Model
{
Expand Down Expand Up @@ -64,6 +67,11 @@ public function getIsUserAttribute()
return auth()->id() === $this->from_person;
}

public function credential()
{
return $this->belongsTo(Credential::class);
}

public function fromPerson()
{
return $this->belongsTo(Person::class, 'from_person');
Expand Down
1 change: 1 addition & 0 deletions app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ public function accounts()
{
return $this->hasManyThrough(Account::class, Credential::class);
}

public function messages()
{
return $this->hasManyThrough(Message::class, Credential::class)->orderByDesc('originated_at');
Expand Down
2 changes: 1 addition & 1 deletion config/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\HorizonServiceProvider::class,
App\Providers\Filament\AdminPanelProvider::class,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"vue-draggable-next": "^2.2.1",
"vue-json-pretty": "^2.2.4",
"vue-select": "^4.0.0-beta.6",
"vue3-markdown-it": "^1.0.10",
"vuedraggable": "^4.1.0",
"vuex": "^4.1.0",
"ws": "^8.14.2",
Expand Down
34 changes: 21 additions & 13 deletions resources/js/Components/Spork/CrudView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
<input type="checkbox" v-model="selectedItems" :value="datum">
</div>
<div class="flex-1">
<slot v-if="datum" class="flex-1" :data="datum" name="data">
<slot v-if="datum" class="flex-1" :data="datum" name="data" :open-modal="() => createOpen = true">
<pre class="flex-1">fallback: {{ datum}}</pre>
</slot>
</div>
Expand Down Expand Up @@ -119,32 +119,40 @@
</div>


<div v-if="createOpen" class="fixed z-0 inset-0 flex items-center outline-none w-screen h-screen overflow-y-scroll">
<div class="relative z-10 w-full md:w-1/2 mx-auto max-h-screen overflow-y-auto p-4">
<div class="w-full rounded p-4 bg-white dark:bg-stone-600 shadow-lg text-left dark:text-stone-50 ">
<div v-if="createOpen" class="fixed z-40 inset-0 flex items-center outline-none w-screen h-screen overflow-y-scroll">
<div class="relative z-50 w-full md:max-w-3xl mx-auto max-h-screen overflow-y-auto p-4">
<div class="w-full rounded p-4 dark:p-8 bg-white dark:bg-stone-800 shadow-lg text-left dark:text-white ">
<div class="text-xl flex justify-between">
<slot name="modal-title">Create Modal</slot>
<button @click="createOpen = false" class="focus:outline-none">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
</button>
</div>
<div class="flex flex-col border-t border-stone-200 mt-2 pt-4">
<slot name="form"></slot>
<div class="mt-4 flex justify-end">
<SporkButton @click.prevent="async () => {
<slot name="form" :open-modal="() => createOpen = true"></slot>
<div class="mt-4 flex justify-end gap-4">
<SporkButton @click.prevent="async () => {
createOpen = false;
}"
primary
medium
>
Close
</SporkButton>
<SporkButton @click.prevent="async () => {
$emit('save', form);
createOpen = false;
}"
primary
medium
>
Save
</SporkButton>
primary
medium
>
Save
</SporkButton>
</div>
</div>
</div>
</div>
<div @click="createOpen = false" v-if="createOpen" class="fixed z-0 cursor-pointer inset-0 flex items-center outline-none" style="background: rgba(0,0,0,0.4);"></div>
<div @click="createOpen = false" v-if="createOpen" class="fixed z-30 cursor-pointer inset-0 flex items-center outline-none bg-stone-900/50"></div>
</div>

</template>
Expand Down
57 changes: 48 additions & 9 deletions resources/js/Components/Spork/SporkDynamicInput.vue
Original file line number Diff line number Diff line change
@@ -1,37 +1,76 @@
<template>
<div class="w-full flex flex-col">
<div class="w-full flex flex-col divide-y divide-stone-700 dark:divide-stone-800">
<input
:value="modelValue.name"
@input="$emit('update:modelValue', {
...modelValue,
name: $event.target.value
})"
class="bg-transparent w-full rounded-t-md text-xs leading-loose tracking-wide font-bold py-0 px-4" placeholder="text" type="text"
:disabled="!editableLabel"
:class="inputClasses(!editableLabel)"
class="rounded-t-md text-xs leading-loose tracking-wide font-bold py-0 px-4" placeholder="text" type="text"
/>
<label
class="flex dark:bg-stone-600 dark:placeholder-stone-300 rounded-b-md h-10"
v-if="type !== 'object'"
:class="[type === 'checkbox' ? 'pl-4 pt-4': 'p-0']"
>
<input
class="py-2 px-3 block shadow-sm sm:text-sm dark:bg-stone-600 dark:placeholder-stone-300 rounded-b-md"
:class="[type !== 'checkbox' ? 'w-full': 'bg-stone-50 border-stone-50 dark:border-stone-600 border p-1']"
class="py-2 px-3 block shadow-sm sm:text-sm rounded-b-md"
:class="inputClasses(disabledInput)"
:value="modelValue.value"
@input="$emit('update:modelValue', {
...modelValue,
value: $event.target.value
})"
:type="type"
:disabled="disabledInput"
/>

</label>
</div>
</template>

<script>
export default {
props: ['modelValue', 'type', 'autofocus'],
emits: ['update:modelValue'],
}
<script setup>
import { computed } from 'vue';
const {
modelValue,
type,
autofocus,
disabledInput,
editableLabel
} = defineProps({
modelValue: Object,
type: String,
autofocus: Boolean,
disabledInput: {
type: Boolean,
default: () => true,
},
editableLabel: {
type: Boolean,
default: () => false,
}
})
// emits: ['update:modelValue'],
const inputClasses = (disabled) => {
let baseClasses = [];
if (type !== 'checkbox') {
baseClasses.push('w-full');
}
baseClasses.push('border', 'p-1');
console.log({disabled})
if (disabled) {
baseClasses.push('bg-stone-50', 'border-stone-50', 'dark:border-stone-900/50', 'dark:bg-stone-700/50', 'dark:placeholder-stone-300')
} else {
baseClasses.push('bg-stone-50', 'border-stone-50', 'dark:border-stone-700', 'dark:bg-stone-700', 'dark:placeholder-stone-300')
}
return baseClasses;
};
</script>

17 changes: 11 additions & 6 deletions resources/js/Layouts/AppLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ const userNavigation = [
{ name: 'Sign out', href: '#' },
]
const notificationCount = computed(() => {
return (page.props.unread_email_count ?? 0) + page.props.notifications.length
});
const sidebarOpen = ref(false)
const showingNavigationDropdown = ref(false);
const $settings = computed(() => page.props)
Expand Down Expand Up @@ -82,7 +85,7 @@ const logout = () => {
<Head :title="title" />
<Banner />
<TransitionRoot as="template" :show="sidebarOpen">
<Dialog as="div" class="relative z-50 lg:hidden" @close="sidebarOpen = false">
<Dialog as="div" class="relative z-0 lg:hidden" @close="sidebarOpen = false">
<TransitionChild as="template" enter="transition-opacity ease-linear duration-300" enter-from="opacity-0" enter-to="opacity-100" leave="transition-opacity ease-linear duration-300" leave-from="opacity-100" leave-to="opacity-0">
<div class="fixed inset-0 bg-stone-900/80" />
</TransitionChild>
Expand Down Expand Up @@ -120,11 +123,11 @@ const logout = () => {
</TransitionRoot>
<!-- Static sidebar for desktop -->
<div class="hidden lg:fixed lg:inset-y-0 lg:left-0 lg:z-50 lg:block lg:w-20 lg:bg-stone-950 lg:pb-4 relative">
<div class="hidden lg:fixed lg:inset-y-0 lg:left-0 lg:z-10 lg:block lg:w-20 lg:bg-stone-950 lg:pb-4 relative">
<Link href="/-" class="flex h-16 shrink-0 items-center flex flex-col justify-center">
<CpuChipIcon class="h-8 w-8 text-slate-500" />
</Link>
<div class="text-white absolute left-16 z-10 mr-8 -mt-4 bg-stone-700 rounded-full w-6 h-6">
<div class="text-white absolute left-16 z-0 mr-8 -mt-4 bg-stone-700 rounded-full w-6 h-6">
<ChevronDownIcon class="w-6 h-6 text-white -rotate-90" />
</div>
Expand All @@ -139,7 +142,7 @@ const logout = () => {
</div>
<div class="lg:pl-20 min-h-screen">
<div class="sticky top-0 z-40 flex h-16 shrink-0 items-center gap-x-4 border-b border-stone-200 dark:border-neutral-700 bg-white dark:bg-neutral-900 px-4 shadow-sm sm:gap-x-6 sm:px-6 lg:px-8">
<div class="sticky top-0 z-0 flex h-16 shrink-0 items-center gap-x-4 border-b border-stone-200 dark:border-neutral-700 bg-white dark:bg-neutral-900 px-4 shadow-sm sm:gap-x-6 sm:px-6 lg:px-8">
<button type="button" class="-m-2.5 p-2.5 text-stone-700 lg:hidden" @click="sidebarOpen = true">
<span class="sr-only">Open sidebar</span>
<Bars3Icon class="h-6 w-6" aria-hidden="true" />
Expand All @@ -158,7 +161,9 @@ const logout = () => {
<button @click="" class="-m-2.5 p-2.5 text-stone-400 hover:text-stone-500 relative">
<span class="sr-only">View notifications</span>
<BellIcon class="h-6 w-6" aria-hidden="true" />
<span class="bg-red-500 absolute top-0 right-0 rounded-full text-sm text-white py-0.5 px-1">1</span>
<span class="bg-red-500 absolute top-0 right-0 rounded-full text-sm text-white py-0.5 px-1">
{{ notificationCount }}
</span>
</button>
<!-- Separator -->
Expand All @@ -175,7 +180,7 @@ const logout = () => {
</span>
</MenuButton>
<transition enter-active-class="transition ease-out duration-100" enter-from-class="transform opacity-0 scale-95" enter-to-class="transform opacity-100 scale-100" leave-active-class="transition ease-in duration-75" leave-from-class="transform opacity-100 scale-100" leave-to-class="transform opacity-0 scale-95">
<MenuItems class="absolute right-0 z-10 mt-2.5 w-32 origin-top-right rounded-md bg-white dark:bg-stone-800 py-2 shadow-lg ring-1 ring-stone-900/5 focus:outline-none">
<MenuItems class="absolute right-0 z-0 mt-2.5 w-32 origin-top-right rounded-md bg-white dark:bg-stone-800 py-2 shadow-lg ring-1 ring-stone-900/5 focus:outline-none">
<MenuItem v-for="item in userNavigation" :key="item.name" v-slot="{ active }">
<Link :href="item.href" :class="[active ? 'bg-stone-50 dark:bg-stone-700' : '', 'block px-3 py-1 text-sm leading-6 dark:text-stone-50 text-stone-900']">{{ item.name }}</Link>
</MenuItem>
Expand Down
Loading

0 comments on commit 0afaea1

Please sign in to comment.