Uploading Blob images in firebase storage using putString in AngularFire - javascript

I created an Electron - Angular project where I can do a screenshot remotely, this generate this blob (I name it as ssUrl / this.ssUrl in the code below)
I don't want it to be saved locally but instead to be uploaded directly to firebase storage, I've been following some documentations and altering it a bit to fit my codebase, however this generates an error.
Here is my code:
import {
AngularFirestore,
AngularFirestoreCollection,
} from '#angular/fire/compat/firestore';
import { Observable, concatWith } from 'rxjs';
import {
AngularFireStorage,
AngularFireUploadTask,
} from '#angular/fire/compat/storage';
#Component({
selector: 'app-autoupload',
templateUrl: './autoupload.component.html',
styleUrls: ['./autoupload.component.scss'],
})
export class LoginComponent implements OnInit {
constructor(
private afs: AngularFirestore,
private afsU: AngularFireStorage
) {}
checkSS() {
if (this.ssRequest == true) {
this.getScreenshot(); // executes IpcServices to get screenshot using electron
console.log('ready to upload', this.ssUrl); // generated URL from electron
// Uploading to Firebase storage
this.imagePathName = 'gallery' + Math.random();
this.imageRef = this.afsU.ref(this.imagePathName);
this.imageRef.putString(this.ssUrl, 'base64', {contentType: 'image/png'});
this.imageSub = this.imageBaseRef
.snapshotChanges()
.pipe(concatWith(this.imageRef.getDownloadURL()))
.subscribe((url: Observable<string>) => {
this.downloadUrl = url;
this.galleryUrl = this.downloadUrl;
this.galleryLogDate = new Date();
this.afs.collection('gallery').add({
ssUrl: this.galleryUrl,
profileId: this.loggedProfileId,
userName: this.loggedUsername,
});
});
}
}
}
generates two error
I know there's something wrong on how I upload it using putString or maybe I'm using the wrong method on storage ref. I tried other approaches but also ending up getting more errors
this.imageBaseRef = this.afsU.upload(this.imagePathName, this.ssUrl);
Hopefully you could correct what me or guide me on how to fix.

Related

In Angular, i need help managing loading/error state of an api call from a service, and also help in general code structure

im building an app and i need alittle bit of guidance in terms of app structure and logic
ill greatly appreciate any help!
so in the app i am making a server call and i fetch documents from there(parcels),
now i want to minimize the amount of calls as much as possible because i assume it will improve my app performance.
am i right? im going to fetch all documents and then do any filtering/splice or whatever needed in the client side every time a parcel was deleted and etc, i tried server side handling(so server side deleting a parcel for example and returns the updated parcels array after the delete) but it was pretty slow because the parcels array is quite large, and it makes a call to mongoDB so it also takes time(specially the non "onstock" one).
so my idea was to make the api call in a service as soon as it is initialized and store the parcels (and also store another array of only the parcels that are onstock) in subjects.
but i have abit of a problem,
i dont know how to display errors/loading screen for proper user experience because my api call is in a service,
so i tired to make a subject representing the loading state(i use it in a component to display a loading spinner) but now i also need a subject representing the error state(if the api call has an error i want to display it to the user) and it becomes cumbersome,
2.in the service there are going to be more methods and they are going to have to manipulate the parcels subjects aswell so i wonder if i should subscribe in a top level component and drill the subjects inside to sub component or can i just subscribe many times in sub components and it wont affect performance?
sorry for the long post as im lacking the best practice knowledge.
the service code:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { BehaviorSubject} from 'rxjs';
import { Parcel } from 'src/app/models/Parcel.model';
#Injectable({
providedIn: 'root',
})
export class ParcelsService {
apiUrl: string = 'http://localhost:5000';
allParcels$ = new BehaviorSubject<Parcel[]>([]);
stockParcels$ = new BehaviorSubject<Parcel[]>([]);
isLoading$ = new BehaviorSubject<boolean>(true);
constructor(private http: HttpClient) {
this.http.get<Parcel[]>(`${this.apiUrl}/parcels`).subscribe((response) => {
this.allParcels$.next(response);
this.stockParcels$.next(
response.filter((parcel) => parcel.isOnStock === true)
);
});
this.isLoading$.next(false)
}
}
the only component currently that uses the subjects (there will be more)
import { Component, OnInit, OnDestroy } from '#angular/core';
import { ParcelsService } from 'src/app/services/parcels/parcels.service';
import { Parcel } from 'src/app/models/Parcel.model';
import { Subject, Subscription, takeUntil } from 'rxjs';
#Component({
selector: 'app-parcels-management-page',
templateUrl: './parcels-management-page.component.html',
styleUrls: ['./parcels-management-page.component.css'],
})
export class ParcelsManagementPageComponent implements OnInit, OnDestroy {
private ngUnsubscribe = new Subject<void>();
isFetching = true;
allParcels: Parcel[] = [];
stockParcel: Parcel[] = [];
constructor(private parcelsService: ParcelsService) {}
ngOnInit(): void {
this.parcelsService.isLoading$
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((response) => {
this.isFetching = response;
console.log(this.isFetching);
});
this.parcelsService.allParcels$
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((response) => {
this.allParcels = response;
console.log(this.allParcels);
});
this.parcelsService.stockParcels$
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((response) => {
this.stockParcel = response;
console.log(this.stockParcel);
});
}
ngOnDestroy() {
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}
}

Cannot load pdf from local file system (IONIC 4 - Android)

I got a problem where I cannot load a pdf inside a view of my app.
In my demo app for this I got two views, one "home" and one "result". In the home view I got a button which opens an android document picker with function pickDocument(). After a document has been picked the file name and file path get passed to the "result" view.
In my result view I convert the given path of the document picker to a native path in setPath(), but this path gives me a 404 on my device. I can't seem to load the file I want to display there.
The file has the local path (before converting it using setPath():
content://com.android.providers.downloads.documents/document/22
I get the following error:
Dummy-PDF.pdf:1 GET http://localhost/_app_file_/storage/emulated/0/Download/Dummy-PDF.pdf 404 (OK)
Is there anything I am missing? Or is there a better way to do, what I am trying to do? Could someone please help me to fix this issue.
I included the important code parts below. Thanks a lot.
Code of home.page.ts
...
export class HomePage {
constructor(private chooser: Chooser, private router: Router) { }
pickDocument() {
this.chooser.getFile("application/pdf")
.then(file => {
this.addFile(file.name, file.uri);
}, error => {
console.error(error);
});
}
addFile(fileName: string, fileUri: string) {
let navigationExtras: NavigationExtras = {
queryParams: {
"fileName": fileName,
"fileUri": fileUri
}
};
this.router.navigate(["result"], navigationExtras);
}
}
Code of result.page.ts
...
export class ResultPage implements OnInit {
public fileName: string;
public fileUri: string;
public path: string;
private win: any = window;
constructor(private route: ActivatedRoute, private filePath: FilePath) {
this.route.queryParams.subscribe(params => {
this.fileName = params["fileName"];
this.fileUri = params["fileUri"];
this.setPath();
});
}
ngOnInit() {
}
setPath() {
this.filePath.resolveNativePath(this.fileUri).then(path => {
this.path = this.win.Ionic.WebView.convertFileSrc(path);
});
}
}
Code of result.page.html
<ion-content>
<ion-list>
<ion-item>
<ion-img [src]="path"></ion-img>
</ion-item>
<ion-item>FileName: {{fileName}}</ion-item>
<ion-item>FileUri:{{fileUri}}</ion-item>
<ion-item>FilePath:{{path}}</ion-item>
</ion-list>
</ion-content>

Download a text file and assign the text as a string to a variable - Angular 7

I have an API response that contains inside a link to a stored text file in the cloud.
I want to download the text file and use the text as a string inside my Angular application.
This what i did so far(Not much)
getTextDetails(resultId: string) {
this.http.get(TEXT_DETAILS_URL + resultId).subscribe((res: any) => {
this.saveTextFile(res.textFile);
});
}
// The data is a link to the text file!!
saveTextFile(data) {
const blob = new Blob([data], { type: 'text/csv' });
const url = window.URL.createObjectURL(blob);
}
Any help will be appreciated.
[Edit]
Thanks for your help i didn't knew that it's that simple. I got a small issue now that I'm receiving:
SyntaxError: Unexpected token < in JSON at position 0 at JSON.parse (<anonymous>) at XMLHttpRequest.onLoad
From what I understand, there are two API calls. The first API call returns the path to the text file. You use this path to make another API call to get the actual contents of the file.
Please do correct if my understanding is not correct.
If my understanding is correct, give this a try:
import { Component } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { switchMap } from 'rxjs/operators';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular';
constructor(private http: HttpClient) {}
ngOnInit() {
this.getFileData()
.subscribe(response => console.log(response));
}
getFileData() {
return this.http.get('/assets/filePath.json')
.pipe(
switchMap((response: any) => this.http.get(response.pathToFile, {
responseType: 'text'
}))
);
}
}
NOTE: Do understand that I'm using the response from a locally hosted JSON file which again points to a locally hosted text file. Also, you'll have to specify the responseType for the second API call as text.
Here's a Working Sample StackBlitz for your ref.
In order to get the text file. You can do:
text: string;
getTextDetails(resultId: string) {
this.http.get(TEXT_DETAILS_URL + resultId).subscribe((res: any) => {
//Now get the text from url.
this.http.get(res.textFile).subscribe(text: any) {
this.text = text;
}
});
}
Now you can display this text in your html template.
<p ngIf*="text"> {{ text }} </p>

Property 'map' does not exist on type 'AngularFireList<{}>'

I'm having a tough time trying to figure this one out. I get the following error when I run Ionic Serve:
Property 'map' does not exist on type 'AngularFireList<{}>'.
I have searched for a fix for some time and can't find anything that has worked, so here I am. Current versions are:
Ionic Framework: 3.9.2
Ionic App Scripts: 3.1.9
Angular Core: 5.0.1
Angular Compiler CLI: 5.0.1
Node: 9.11.1
I have worked out all the other bugs and migrated everything to newer versions (changing Firebase listings to Angular.)
The code that throws the error is the .map() object, here:
afDB.list('/feed').map((feeds) =>
This is my code:
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams, ModalController ,
LoadingController} from 'ionic-angular';
import { AngularFireDatabase, AngularFireList} from 'angularfire2/database';
import 'rxjs/add/operator/map'; // you might need to import this, or not
depends on your setup
#IonicPage()
#Component({
selector: 'page-feed',
templateUrl: 'feed.html'
})
export class FeedPage {
feeds: AngularFireList<any[]>;
feedView: string = "activity";
constructor(public navCtrl: NavController, public navParams: NavParams ,public modalCtrl: ModalController,public loadingCtrl: LoadingController, public afDB: AngularFireDatabase) {
let loadingPopup = this.loadingCtrl.create({
spinner: 'crescent',
content: ''
});
loadingPopup.present();
this.feeds = <AngularFireList<any[]>> afDB.list('/feed').map((feeds) => {
return feeds.map((feeds) => {
feeds.images = afDB.list('/feed/'+feeds.$key+'/images')
loadingPopup.dismiss().catch(() => console.log('ERROR CATCH: LoadingController dismiss'));
return feeds
})
})
}
}
I am learning so if the answer is obvious, please explain it. Thanks in advance.
When you call list method on AngularFireDatabase you get back an AngularFireList. Even though there is List in the name it's not an array or a stream that would have the map method.
This is the definition for this type (you can see this by using go to definition on the AngularFireList in your editor or by browsing the code source):
export interface AngularFireList<T> {
query: DatabaseQuery;
valueChanges(events?: ChildEvent[]): Observable<T[]>;
snapshotChanges(events?: ChildEvent[]): Observable<SnapshotAction[]>;
stateChanges(events?: ChildEvent[]): Observable<SnapshotAction>;
auditTrail(events?: ChildEvent[]): Observable<SnapshotAction[]>;
update(item: FirebaseOperation, data: T): Promise<void>;
set(item: FirebaseOperation, data: T): Promise<void>;
push(data: T): ThenableReference;
remove(item?: FirebaseOperation): Promise<void>;
}
In order to get the stream you need to use one of the methods returning an Observable, and assuming you want the values that would be valueChanges.
So your code should be something like:
afDB.list('/feed').valueChanges.map(...)
And the result would be a stream, meaning Observable<any>. This means that in the template you would need to use the Async pipe.
We seem to have fixed it by doing the following - and by we, I mean all credit goes to my friend R. Jackson.
First, I added this to the imports
import {Observable} from 'rxjs/Observable'
Next, under the exports section, I the line:
feeds: AngularFireList;
to
feeds: Observable;
then, putting those changes into play
this.feeds = afDB.list('/feed').valueChanges().map((feeds:any)
This no long throws any errors and serves without any hiccups.

angular js code not working with external plugin

I am trying to use intercom in my app to monitor user activity.
its working fine when I put inside script tag in index.html
but when I try to use in .ts file I am getting below error.
app/components/rocket/rocket-search.ts(63,10): error TS2339: Property 'intercomSettings' does not exist on type 'Window'.
can you tell me how to fix it.
providing my code below
not working code
import { Component, ElementRef, Input, Output, EventEmitter, Inject, OnInit, ViewChild } from '#angular/core';
import { KendoGridComponent } from '../grid/grid.component'
import { Router } from '#angular/router';
import { sportsService } from '../../services/sports.service';
#Component({
selector: 'rocketSearch',
templateUrl: "./app/components/rocket/rocket-search.html",
})
export class rocketSearch {
/* Localization variables */
#Output() rocketSearchEmitter: EventEmitter<any> = new EventEmitter<any>();
private dataSourceVal;
private MainGrid;
private grid;
private titles;
public rocketRef;
constructor( public elementRef: ElementRef, public router: Router, public sportsService: sportsService) {
}
private kendocommand = {
edit: { createAt: "bottom" },
autoBind: false,
excelFileName: {
fileName: "",
allPages: true
}
}
ngOnInit() {
let that = this;
let attributes = this.sportsService.getSeesionStorageValue();
// if (attributes) {
// this.userId = attributes.user_attributes.SSO[0];
// }
//app/components/rocket/rocket-search.ts(63,10): error TS2339: Property 'intercomSettings' does not exist on type 'Window'.
window.intercomSettings = {
app_id: 'irgooiqb',
name: "Jane Doe", // Full name
email: "customer#example.com", // Email address
created_at: 1312182000 // Signup date as a Unix timestamp
};
//console.log(.log(this.userId);
}
}
working code inside index.html
<script>
(function(){var w=window;var ic=w.Intercom;if(typeof ic==="function"){ic('reattach_activator');ic('update',intercomSettings);}else{var d=document;var i=function(){i.c(arguments)};i.q=[];i.c=function(args){i.q.push(args)};w.Intercom=i;function l(){var s=d.createElement('script');s.type='text/javascript';s.async=true;s.src='https://widget.intercom.io/widget/APP_ID';var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);}if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}})()
</script>
<script>
let players=this.sportsService.marksSession();
console.log("players--->" + players);
if(players) {
this.userId = players.user_players.SSO[0];
}
Intercom('trackEvent', 'share-link');
window.intercomSettings = {
app_id: 'APP_ID',
name: "Jane Doe", // Full name
email: "customer#example.com", // Email address
created_at: 1312182000 // Signup date as a Unix timestamp
};
</script>
well it wouldn't work, the code in your html actually fetches the intercom js library from the URL https://widget.intercom.io/widget/APP_ID which the code in your typescript do not.
A better approach is to install the intercom npm package that you can then import in your code, however I don't write typescript so I'm unsure if there would be any issue using the package in a typescript fashion.

Categories