├── .github └── FUNDING.yml ├── .stubs.php ├── 3x1io-tomato-fcm.md ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── SECURITY.md ├── arts └── 3x1io-tomato-fcm.jpg ├── composer.json ├── config ├── .gitkeep └── filament-fcm.php ├── database └── migrations │ ├── .gitkeep │ └── 2022_05_29_032309_create_user_has_notifications_table.php ├── resources └── views │ ├── .gitkeep │ ├── firebase-base.blade.php │ └── firebase.blade.php ├── src ├── Console │ ├── .gitkeep │ └── FilamentFcmInstall.php ├── FilamentFcmPlugin.php ├── FilamentFcmServiceProvider.php ├── Http │ ├── Controllers │ │ └── .gitkeep │ ├── Middleware │ │ └── .gitkeep │ └── Requests │ │ └── .gitkeep ├── Jobs │ └── NotifyFCMJob.php ├── Livewire │ └── Firebase.php ├── Models │ ├── .gitkeep │ └── UserToken.php ├── Notifications │ └── FCMNotificationService.php └── Traits │ └── InteractsWithFCM.php ├── stubs └── firebase.stub └── tests ├── Feature └── .gitkeep └── Unit └── .gitkeep /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [3x1io] 2 | -------------------------------------------------------------------------------- /.stubs.php: -------------------------------------------------------------------------------- 1 | plugin(\TomatoPHP\FilamentFcm\FilamentFcmPlugin::make() 56 | ) 57 | ``` 58 | 59 | ## Usage 60 | 61 | you can use the filament native notification and we add some macro for you 62 | 63 | ```php 64 | use Filament\Notifications\Notification; 65 | 66 | Notification::make('send') 67 | ->title('Test Notifications') 68 | ->body('This is a test notification') 69 | ->icon('heroicon-o-bell') 70 | ->color('success') 71 | ->actions([ 72 | \Filament\Notifications\Actions\Action::make('view') 73 | ->label('View') 74 | ->url('https://google.com') 75 | ->markAsRead() 76 | ]) 77 | ->sendToFCM( 78 | user: auth()->user(), 79 | data: [ 80 | 'key' => 'value' 81 | ], 82 | sendToDatabase: false, 83 | type: 'fcm-web' // or fcm-api 84 | ) 85 | ``` 86 | 87 | or you can send it directly from the user model 88 | 89 | ```php 90 | 91 | $user->notifyFCMSDK( 92 | message: $this->message, 93 | type: $this->provider, 94 | title: $this->title, 95 | url: $this->url, 96 | image: $this->image, 97 | icon: $this->icon, 98 | data: [ 99 | 'url' => $this->url, 100 | 'id' => $this->model_id, 101 | 'actions' => [], 102 | 'body' => $this->message, 103 | 'color' => null, 104 | 'duration' => null, 105 | 'icon' => $this->icon, 106 | 'iconColor' => null, 107 | 'status' => null, 108 | 'title' => $this->title, 109 | 'view' => null, 110 | 'viewData' => null, 111 | 'data'=> $this->data 112 | ], 113 | sendToDatabase: false 114 | ); 115 | 116 | ``` 117 | ## Publish Assets 118 | 119 | you can publish config file by use this command 120 | 121 | ```bash 122 | php artisan vendor:publish --tag="filament-fcm-config" 123 | ``` 124 | 125 | you can publish views file by use this command 126 | 127 | ```bash 128 | php artisan vendor:publish --tag="filament-fcm-views" 129 | ``` 130 | 131 | 132 | you can publish migrations file by use this command 133 | 134 | ```bash 135 | php artisan vendor:publish --tag="filament-fcm-migrations" 136 | ``` 137 | 138 | ## Other Filament Packages 139 | 140 | Checkout our [Awesome TomatoPHP](https://github.com/tomatophp/awesome) 141 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-fcm/d0b78888e4590d47265517b0518c27b4b3bbcbe3/SECURITY.md -------------------------------------------------------------------------------- /arts/3x1io-tomato-fcm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-fcm/d0b78888e4590d47265517b0518c27b4b3bbcbe3/arts/3x1io-tomato-fcm.jpg -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tomatophp/filament-fcm", 3 | "type": "library", 4 | "description": "Firebase Cloud Messaging integration to Native FilamentPHP Notification Package", 5 | "keywords": [ 6 | "php", 7 | "laravel", 8 | "firebase", 9 | "google-firebase", 10 | "filamentphp", 11 | "integration", 12 | "filament-notifications" 13 | ], 14 | "license": "MIT", 15 | "autoload": { 16 | "psr-4": { 17 | "TomatoPHP\\FilamentFcm\\": "src/" 18 | } 19 | }, 20 | "autoload-dev": { 21 | "psr-4": { 22 | "Tests\\": "tests/" 23 | } 24 | }, 25 | "extra": { 26 | "laravel": { 27 | "providers": [ 28 | "TomatoPHP\\FilamentFcm\\FilamentFcmServiceProvider" 29 | ] 30 | } 31 | }, 32 | "authors": [ 33 | { 34 | "name": "Fady Mondy", 35 | "email": "info@3x1.io" 36 | } 37 | ], 38 | "require": { 39 | "php": "^8.1|^8.2", 40 | "filament/filament": "^3.0.0", 41 | "filament/notifications": "^3.0.0", 42 | "tomatophp/console-helpers": "^1.1", 43 | "tomatophp/fcm-notifications": "^1.0", 44 | "mobiledetect/mobiledetectlib": "^4.8" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-fcm/d0b78888e4590d47265517b0518c27b4b3bbcbe3/config/.gitkeep -------------------------------------------------------------------------------- /config/filament-fcm.php: -------------------------------------------------------------------------------- 1 | [ 11 | "apiKey"=> env("FIREBASE_API_KEY"), 12 | "authDomain"=> env("FIREBASE_AUTH_DOMAIN"), 13 | "databaseURL"=> env("FIREBASE_DATABASE_URL"), 14 | "projectId"=> env("FIREBASE_PROJECT_ID"), 15 | "storageBucket"=> env("FIREBASE_STORAGE_BUCKET"), 16 | "messagingSenderId"=> env("FIREBASE_MESSAGING_SENDER_ID"), 17 | "appId"=> env("FIREBASE_APP_ID"), 18 | "measurementId" => env("FIREBASE_MEASUREMENT_ID"), 19 | ], 20 | 21 | /* 22 | * --------------------------------------------------------------- 23 | * Firebase Cloud Messaging Configuration 24 | * --------------------------------------------------------------- 25 | * 26 | */ 27 | "vapid" => env("FIREBASE_VAPID"), 28 | 29 | /* 30 | * --------------------------------------------------------------- 31 | * Firebase Alert Sound when notification received 32 | * --------------------------------------------------------------- 33 | * 34 | */ 35 | "alert" => [ 36 | "sound" => env("FCM_ALERT_SOUND"), 37 | ] 38 | ]; 39 | -------------------------------------------------------------------------------- /database/migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-fcm/d0b78888e4590d47265517b0518c27b4b3bbcbe3/database/migrations/.gitkeep -------------------------------------------------------------------------------- /database/migrations/2022_05_29_032309_create_user_has_notifications_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | 19 | //If Selected Record On the model 20 | $table->string('model_type'); 21 | $table->unsignedBigInteger('model_id'); 22 | 23 | $table->string('provider')->default('fcm-web')->nullable(); 24 | $table->string('provider_token')->nullable(); 25 | 26 | $table->timestamps(); 27 | }); 28 | } 29 | 30 | /** 31 | * Reverse the migrations. 32 | * 33 | * @return void 34 | */ 35 | public function down() 36 | { 37 | Schema::dropIfExists('user_has_notifications'); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /resources/views/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-fcm/d0b78888e4590d47265517b0518c27b4b3bbcbe3/resources/views/.gitkeep -------------------------------------------------------------------------------- /resources/views/firebase-base.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 54 | -------------------------------------------------------------------------------- /resources/views/firebase.blade.php: -------------------------------------------------------------------------------- 1 | @livewire(\TomatoPHP\FilamentFcm\Livewire\Firebase::class) 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/Console/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-fcm/d0b78888e4590d47265517b0518c27b4b3bbcbe3/src/Console/.gitkeep -------------------------------------------------------------------------------- /src/Console/FilamentFcmInstall.php: -------------------------------------------------------------------------------- 1 | info('Install FCM Worker'); 42 | $this->generateStubs( 43 | __DIR__ . '/../../stubs/firebase.stub', 44 | public_path('firebase-messaging-sw.js'), 45 | [ 46 | 'apiKey' => config('filament-fcm.project.apiKey'), 47 | 'authDomain' => config('filament-fcm.project.authDomain'), 48 | 'databaseURL' => config('filament-fcm.project.databaseURL'), 49 | 'projectId' => config('filament-fcm.project.projectId'), 50 | 'storageBucket' => config('filament-fcm.project.storageBucket'), 51 | 'messagingSenderId' => config('filament-fcm.project.messagingSenderId'), 52 | 'appId' => config('filament-fcm.project.appId'), 53 | 'measurementId' => config('filament-fcm.project.measurementId'), 54 | 'sound' => config('filament-fcm.alert.sound') ? "var audio = new Audio('".config('filament-fcm.alert.sound')."');\n audio.play();": null 55 | ] 56 | ); 57 | $this->info('Filament Alerts FCM installed successfully.'); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/FilamentFcmPlugin.php: -------------------------------------------------------------------------------- 1 | env('FIREBASE_CREDENTIALS', public_path('storage/' . setting('fcm_cr'))), 41 | 'database' => [ 42 | 'url' => env('FIREBASE_DATABASE_URL', setting('fcm_database_url')), 43 | ] 44 | ]); 45 | 46 | } catch (\Exception $e) { 47 | \Log::error($e); 48 | } 49 | 50 | FilamentView::registerRenderHook( 51 | PanelsRenderHook::BODY_END, 52 | function (){ 53 | return view('filament-fcm::firebase'); 54 | }, 55 | ); 56 | } 57 | 58 | public static function make(): static 59 | { 60 | return new static(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/FilamentFcmServiceProvider.php: -------------------------------------------------------------------------------- 1 | commands([ 20 | \TomatoPHP\FilamentFcm\Console\FilamentFcmInstall::class, 21 | ]); 22 | 23 | //Register Config file 24 | $this->mergeConfigFrom(__DIR__.'/../config/filament-fcm.php', 'filament-fcm'); 25 | 26 | //Publish Config 27 | $this->publishes([ 28 | __DIR__.'/../config/filament-fcm.php' => config_path('filament-fcm.php'), 29 | ], 'filament-fcm-config'); 30 | 31 | //Register Migrations 32 | $this->loadMigrationsFrom(__DIR__.'/../database/migrations'); 33 | 34 | //Publish Migrations 35 | $this->publishes([ 36 | __DIR__.'/../database/migrations' => database_path('migrations'), 37 | ], 'filament-fcm-migrations'); 38 | 39 | //Register views 40 | $this->loadViewsFrom(__DIR__.'/../resources/views', 'filament-fcm'); 41 | 42 | //Publish Views 43 | $this->publishes([ 44 | __DIR__.'/../resources/views' => resource_path('views/vendor/filament-fcm'), 45 | ], 'filament-fcm-views'); 46 | 47 | Livewire::component(Firebase::class); 48 | } 49 | 50 | public function boot(): void 51 | { 52 | 53 | Notification::macro('sendToFCM', function (Model $user, array $data=[], ?bool $sendToDatabase=true, ?string $type ='fcm-web'): static 54 | { 55 | /** @var Notification $this */ 56 | $user->notifyFCMSDK( 57 | title: $this->title, 58 | message: $this->body, 59 | type: $type, 60 | url: count($this->actions)? $this->actions[0]->getUrl() : null, 61 | icon: $this->icon, 62 | data: [ 63 | 'url' => count($this->actions)? $this->actions[0]->getUrl() : null, 64 | 'id' => $this->getId(), 65 | 'actions' => array_map(fn (Action | ActionGroup $action): array => $action->toArray(), $this->getActions()), 66 | 'body' => $this->getBody(), 67 | 'color' => $this->getColor(), 68 | 'duration' => $this->getDuration(), 69 | 'icon' => $this->getIcon(), 70 | 'iconColor' => $this->getIconColor(), 71 | 'status' => $this->getStatus(), 72 | 'title' => $this->getTitle(), 73 | 'view' => $this->getView(), 74 | 'viewData' => $this->getViewData(), 75 | 'data'=> $data 76 | ], 77 | sendToDatabase: $sendToDatabase 78 | ); 79 | 80 | return $this; 81 | }); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Http/Controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-fcm/d0b78888e4590d47265517b0518c27b4b3bbcbe3/src/Http/Controllers/.gitkeep -------------------------------------------------------------------------------- /src/Http/Middleware/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-fcm/d0b78888e4590d47265517b0518c27b4b3bbcbe3/src/Http/Middleware/.gitkeep -------------------------------------------------------------------------------- /src/Http/Requests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-fcm/d0b78888e4590d47265517b0518c27b4b3bbcbe3/src/Http/Requests/.gitkeep -------------------------------------------------------------------------------- /src/Jobs/NotifyFCMJob.php: -------------------------------------------------------------------------------- 1 | user = $arrgs['user']; 40 | $this->title = $arrgs['title']; 41 | $this->message = $arrgs['message']; 42 | $this->icon = $arrgs['icon']; 43 | $this->url = $arrgs['url']; 44 | $this->image = $arrgs['image']; 45 | $this->type = $arrgs['type']; 46 | $this->data = $arrgs['data']; 47 | $this->sendToDatabase = $arrgs['sendToDatabase']; 48 | } 49 | 50 | /** 51 | * Execute the job. 52 | * 53 | * @return void 54 | */ 55 | public function handle() 56 | { 57 | $this->user->setFCM($this->type)->notify(new FCMNotificationService( 58 | $this->message, 59 | $this->type, 60 | $this->title, 61 | $this->icon, 62 | $this->image, 63 | $this->url, 64 | $this->data, 65 | $this->sendToDatabase, 66 | )); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Livewire/Firebase.php: -------------------------------------------------------------------------------- 1 | user()){ 18 | $user = auth()->user(); 19 | $getToken = $user->setFCM($detect->isMobile() ? 'fcm-api' :'fcm-web')->userTokensFcm()->where('provider', $detect->isMobile() ? 'fcm-api' :'fcm-web')->first(); 20 | if($getToken){ 21 | $getToken->provider_token = $token; 22 | $getToken->save(); 23 | } 24 | else { 25 | $user->setFCM($detect->isMobile() ? 'fcm-api' :'fcm-web')->userTokensFcm()->create([ 26 | 'provider' => $detect->isMobile() ? 'fcm-api' :'fcm-web', 27 | 'provider_token' => $token 28 | ]); 29 | } 30 | } 31 | } 32 | 33 | #[On('fcm-notification')] 34 | public function fcmNotification(mixed $data) 35 | { 36 | $actions = []; 37 | if(isset($data['data'])){ 38 | if(isset($data['data']['actions']) && is_object(json_decode($data['data']['actions']))){ 39 | foreach (json_decode($data['data']['actions']) as $action){ 40 | $actions[] = Action::make($action->name) 41 | ->color($action->color) 42 | ->eventData($action->eventData) 43 | ->icon($action->icon) 44 | ->iconPosition($action->iconPosition) 45 | ->iconSize($action->iconSize) 46 | ->outlined($action->isOutlined) 47 | ->disabled($action->isDisabled) 48 | ->label($action->label) 49 | ->url($action->url) 50 | ->close($action->shouldClose) 51 | ->size($action->size) 52 | ->tooltip($action->tooltip) 53 | ->view($action->view) 54 | ->markAsUnread($action->shouldMarkAsUnRead??false) 55 | ->markAsRead($action->shouldMarkAsRead??false); 56 | } 57 | } 58 | } 59 | 60 | if(isset($data['data']['sendToDatabase']) && $data['data']['sendToDatabase'] === true){ 61 | Notification::make($data['data']['id']) 62 | ->title($data['data']['title']) 63 | ->actions($actions) 64 | ->body($data['data']['body']) 65 | ->icon($data['data']['icon']) 66 | ->iconColor($data['data']['iconColor']) 67 | ->color($data['data']['color']) 68 | ->duration($data['data']['duration']) 69 | ->send() 70 | ->sendToDatabase(auth()->user()); 71 | } 72 | else { 73 | Notification::make($data['data']['id']) 74 | ->title($data['data']['title']) 75 | ->actions($actions) 76 | ->body($data['data']['body']) 77 | ->icon($data['data']['icon']) 78 | ->iconColor($data['data']['iconColor']) 79 | ->color($data['data']['color']) 80 | ->duration($data['data']['duration']) 81 | ->send(); 82 | } 83 | } 84 | 85 | public function render() 86 | { 87 | return view('filament-fcm::firebase-base'); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-fcm/d0b78888e4590d47265517b0518c27b4b3bbcbe3/src/Models/.gitkeep -------------------------------------------------------------------------------- /src/Models/UserToken.php: -------------------------------------------------------------------------------- 1 | title, 46 | body: $this->message, 47 | image: $this->image ?? null 48 | ), 49 | data: [ 50 | 'id' => $this->data['id'], 51 | 'actions' => json_encode($this->data['actions']), 52 | 'body' => $this->data['body'], 53 | 'color' => $this->data['color'], 54 | 'duration' => $this->data['duration'], 55 | 'icon' => $this->data['icon'], 56 | 'iconColor' => $this->data['iconColor'], 57 | 'status' => $this->data['status'], 58 | 'title' => $this->data['title'], 59 | 'view' => $this->data['view'], 60 | 'viewData' => json_encode($this->data['viewData']), 61 | 'data' => json_encode($this->data['data']), 62 | 'sendToDatabase' =>$this->sendToDatabase 63 | ], 64 | custom: [ 65 | 'android' => [ 66 | 'notification' => [ 67 | 'color' => '#0A0A0A', 68 | ], 69 | 'fcm_options' => [ 70 | 'analytics_label' => 'analytics', 71 | ], 72 | ], 73 | 'apns' => [ 74 | 'fcm_options' => [ 75 | 'analytics_label' => 'analytics', 76 | ], 77 | ], 78 | ] 79 | ) 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Traits/InteractsWithFCM.php: -------------------------------------------------------------------------------- 1 | $this, 27 | 'title' => $title, 28 | 'message' => $message, 29 | 'icon' => $icon, 30 | 'image' => $image, 31 | 'url' => $url, 32 | 'type' => $type, 33 | 'data' => $data, 34 | 'sendToDatabase' => $sendToDatabase, 35 | ])); 36 | } 37 | 38 | public function initializeUseNotifications() 39 | { 40 | $this->appends[] = 'fcm'; 41 | $this->appends[] = 'fcmID'; 42 | } 43 | 44 | public function setFcmAttribute($value) 45 | { 46 | $this->fcm = $value; 47 | } 48 | 49 | public function getFcmAttribute() 50 | { 51 | return 'fcm-web'; 52 | } 53 | 54 | public function setFcmIdAttribute($value) 55 | { 56 | $this->fcmId = $value; 57 | } 58 | 59 | public function getFcmIdAttribute() 60 | { 61 | return $this->id; 62 | } 63 | 64 | public function setFCM(?string $type='fcm-web'): static 65 | { 66 | $this->fcm = $type; 67 | $this->fcmId = $this->id; 68 | return $this; 69 | } 70 | 71 | public function userTokensFcm() 72 | { 73 | return $this->morphOne(UserToken::class, 'model')->where('provider', $this->fcm); 74 | } 75 | 76 | public function routeNotificationForFcm() 77 | { 78 | return $this->userTokensFcm ? $this->userTokensFcm->provider_token : ''; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /stubs/firebase.stub: -------------------------------------------------------------------------------- 1 | // Give the service worker access to Firebase Messaging. 2 | // Note that you can only use Firebase Messaging here. Other Firebase libraries 3 | // are not available in the service worker. 4 | importScripts('https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js'); 5 | importScripts('https://www.gstatic.com/firebasejs/8.10.1/firebase-messaging.js'); 6 | 7 | try 8 | { 9 | // Initialize the Firebase app in the service worker by passing in 10 | // your app's Firebase config object. 11 | // https://firebase.google.com/docs/web/setup#config-object 12 | firebase.initializeApp({ 13 | apiKey: "{{ apiKey }}", 14 | authDomain: "{{ authDomain }}", 15 | databaseURL: "{{ databaseURL }}", 16 | projectId: "{{ projectId }}", 17 | storageBucket: "{{ storageBucket }}", 18 | messagingSenderId: "{{ messagingSenderId }}", 19 | appId: "{{ appId }}", 20 | measurementId: "{{ measurementId }}" 21 | }); 22 | 23 | 24 | // Retrieve an instance of Firebase Messaging so that it can handle background 25 | // messages. 26 | const messaging = firebase.messaging(); 27 | 28 | messaging.onBackgroundMessage((payload) => { 29 | // 30 | {{ sound }} 31 | 32 | let options = { 33 | body: "", 34 | icon: "", 35 | image: "", 36 | tag: "alert", 37 | }; 38 | 39 | if(payload.data.body){ 40 | options.body = payload.data.body; 41 | } 42 | 43 | if(payload.data.image){ 44 | options.icon = payload.data.image; 45 | } 46 | 47 | let notification = self.registration.showNotification( 48 | payload.data.title, 49 | options 50 | ); 51 | 52 | if(payload.data.url){ 53 | // link to page on clicking the notification 54 | notification.onclick = (payload) => { 55 | window.open(payload.data.url); 56 | }; 57 | } 58 | }); 59 | } 60 | catch(e) { 61 | console.log(e) 62 | } 63 | -------------------------------------------------------------------------------- /tests/Feature/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-fcm/d0b78888e4590d47265517b0518c27b4b3bbcbe3/tests/Feature/.gitkeep -------------------------------------------------------------------------------- /tests/Unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-fcm/d0b78888e4590d47265517b0518c27b4b3bbcbe3/tests/Unit/.gitkeep --------------------------------------------------------------------------------