Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sharing images from whatsapp to angular app via Share_Target not working

I am trying to share images from WhatsApp to my app. Whenever I share a media Item from WhatsApp to my app it hits the webmanifest file and targets the /nav/emergencyRegister route. I am sure it's going to the target route because when I share an image from WhatsApp it opens the same route in the front end.

Here is the code from my manifest.webmanifest file.

{
  "name": "Apoms",
  "short_name": "Apoms",
  "theme_color": "#1976d2",
  "background_color": "#fafafa",
  "display": "standalone",
  "scope": "/",
  "start_url": "/",
  "gcm_sender_id": "275309609166",
  "share_target": {
    "action": "/nav/emergency-register",
    "method": "POST",
    "enctype": "multipart/form-data",
    "params": {
      "title": "name",
      "text": "description",
      "url": "link",
      "files": [
        {
          "name": "images",
          "accept": "image/*"
        },
        {
          "name": "videos",
          "accept": "video/*"
        }
      ]
    }
  }

But in the EmergencyRegisterComponent.ts file, I don't know how to access the image Which should be in the parameters of the route.

Here is my code from the emergency-register-page.component.ts file.

import { Component, OnInit } from '@angular/core';
import { PrintTemplateService } from '../../print-templates/services/print-template.service';
import { CaseService } from '../services/case.service';
import { EmergencyRegisterTabBarService } from '../services/emergency-register-tab-bar.service';
import { Router, ActivatedRoute } from '@angular/router';

@Component({
    // tslint:disable-next-line:component-selector
    selector: 'emergency-register-page',
    templateUrl: './emergency-register-page.component.html',
    styleUrls: ['./emergency-register-page.component.scss'],
})
export class EmergencyRegisterPageComponent implements OnInit {
    constructor(private printService: PrintTemplateService,
        private route: ActivatedRoute,
        private caseServie: CaseService,
        private tabBar: EmergencyRegisterTabBarService) {}

    ngOnInit() {

        this.printService.initialisePrintTemplates();

// I printed the this.route and tried to find the image in there but failed.
// Also used params,querParams from route.
        const data = this.route.snapshot.paramMap.get('files');
        console.log(data);

    }
}

I also tried to extend the existing service worker from here. I created a new service worker file apoms-sw.js

importScripts('./ngsw-worker.js');


self.addEventListener('fetch', event=>{
  console.log(event);
});

In my app.module.ts

@NgModule({
    declarations: [AppComponent, ConfirmationDialog, TreatmentRecordComponent],
    imports: [
        BrowserModule,
        BrowserAnimationsModule,
        AppRoutingModule,
        MatDialogModule,
        NavModule,
        HttpClientModule,
        MaterialModule,
        ServiceWorkerModule.register('apoms-sw.js', { enabled: environment.production }),
        AngularFireDatabaseModule,
        AngularFireAuthModule,
        AngularFireMessagingModule,
        AngularFireStorageModule,
        AngularFireModule.initializeApp(environment.firebase)
    ],
    exports: [],
    providers: [
        DatePipe,
        { provide: HTTP_INTERCEPTORS, useClass: HttpConfigInterceptor, multi: true },
        // { provide: LOCALE_ID, useValue: 'it-IT' },
        { provide: ErrorHandler, useClass: UIErrorHandler }
    ],
    bootstrap: [AppComponent],
})

I can see that the file is being hit while the remote debugging process but it's not firing the fetch event listener. So if anyone can tell me how I can access the image which is coming with the route. It would be great.

like image 486
Arpit Trivedi Avatar asked Nov 16 '20 11:11

Arpit Trivedi


1 Answers

So one of the issues here is that Angular creates its own service worker. And an application can only have one service worker. You can read more about it in this question.

Service workers fire duplicate functions one after the other until one of them calls respondWith. And the fetch function defined by the original service (ngsw-worker.js) worker has a respondWith in it, meaning that no further messages are passed on. This means you need to define any functions before the fetch function in the ngsw-worker fire. You can read more in this question.

So the webmanifest should look like this, which belongs in the root of your webmanifest:

"share_target": {
    "action": "/nav/emergency-register",
    "method": "POST",
    "enctype": "multipart/form-data",
    "params": {
      "files": [
        {
          "name": "image",
          "accept": "image/*"
        },
        {
          "name": "video",
          "accept": "video/*"
        }
      ]
    }
  },

And then you need a new file that will contain your fetch function and import the service worker. It is important to define your fetch function first:

self.addEventListener('fetch', event => {

  // Handle the share_target shares
  if (event.request.method === 'POST') {

    // Make sure we're only getting shares to the emergency-register route
    const path = event.request.url.split("/").pop();

    if(path === "emergency-register"){

        //Get the images and videos from the share request
        event.request.formData().then(formData => {

            // Find the correct client in order to share the results.
            const clientId = event.resultingClientId !== "" ? event.resultingClientId : event.clientId;
            self.clients.get(clientId).then(client => {

                // Get the images and video
                const image = formData.getAll('image');
                const video = formData.getAll('video');

                // Send them to the client
                client.postMessage(
                    {
                        message: "newMedia",
                        image: image,
                        video: video
                    }
                );
            });
        });
    }
  }
});


importScripts('./ngsw-worker.js');

Then in your app.component you can define the function that will catch the posted message, be careful if you are using firebase messaging or other functions that post messages to the client, you'll need to differentiate between them somehow:

// Set up to receive messages from the service worker when the app is in the background.
navigator.serviceWorker.addEventListener('message', (event:MessageEvent) => {

    if(event.data.hasOwnProperty('image')){

        this.emergencyTabBar.receiveSharedMediaItem(event.data);
    }

    if(event.hasOwnProperty('data')){

        this.messagingService.receiveBackgroundMessage(event.data?.firebaseMessaging?.payload);
   }

});
like image 163
Arpit Trivedi Avatar answered Oct 22 '22 23:10

Arpit Trivedi