Sending data to an imported module in React Native - firebase

I have a module called Chat.js that imports Fire.js in order to send data (message comes into Chat.js, and Fire.js handles storage).
I have a recipient's user ID that is only currently available in Chat.js, but it is important to get to Fire.js in order to store appropriately.
I removed some info for brevity, this is my current Chat.js:
import Fire from './Fire';
class Chat extends React.Component<Props> {
state = {
messages: [],
};
get user() {
return {
name: this.props.navigation.state.params.name,
_id: Fire.shared.uid,
};
}
render() {
return (
<GiftedChat
messages={this.state.messages}
onSend={Fire.shared.send}
user={this.user}
/>
);
}
componentDidMount() {
Fire.shared.on(message =>
this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, message),
}))
);
}
componentWillUnmount() {
Fire.shared.off();
}
}
export default Chat;
And this is my current Fire.js:
import firebase from 'react-native-firebase';
class Fire {
constructor() {
}
get ref() {
var recipient = 'recipientId'
return firebase.database().ref('messages/' + this.uid + '/' + recipient);
}
parse = snapshot => {
const { timestamp: numberStamp, text, user } = snapshot.val();
const { key: _id } = snapshot;
const timestamp = new Date(numberStamp);
const message = {
_id,
timestamp,
text,
user,
};
return message;
};
on = callback =>
this.ref
.limitToLast(20)
.on('child_added', snapshot => callback(this.parse(snapshot)));
// send the message to the Backend
send = messages => {
for (let i = 0; i < messages.length; i++) {
const { text, user } = messages[i];
const message = {
text,
user,
timestamp: this.timestamp,
};
this.append(message);
}
};
append = message => this.ref.push(message);
// close the connection to the Backend
off() {
this.ref.off();
}
}
Fire.shared = new Fire();
export default Fire;
I currently need to get the recipient ID, which is available in chat.js under
this.props.navigation.state.params.uid
Into the Fire.js lines:
get ref()
{
var recipient = 'recipientId'
I can't seem to get this uid into get ref()

Use getter and setters in Fire.js.
In Fire.js
setRecipient (id){
this.recipientId = id;
}
get getRecipientId () {
return this.recipientId;
}
And then call Fire.setRecipient(yourId) in Chat.js.

Related

How to create a function that returns new session format with extra key value pair

I am using NextJS with NextAuth with google and email providers. Unfortunately, the session returns only few fields that does not include userId of the user from the database.
I created however a function that I intend to use with every getServerSideProps request. The function returns the following:
{
user: {
name: 'daniel sas',
email: 'emailofuser#gmail.com',
image: 'https://lh3.gooleusercontent.com/a/AEdFTp6r44ZwqcfJORNnuYtbVv_LYbab-wv5Uyxk=s96-c',
userId: 'clbcpc0hi0002sb1wsiea3q5d'
},
expires: '2022-12-17T20:18:52.580Z'
}
The problem is I am getting an error that does not allow me to pass the props in the page:
Error: Your `getServerSideProps` function did not return an object. Did you forget to add a `return`?
In the function I get the user by the email, and attach the userId.
import { getSession } from "next-auth/react";
import prisma from './prisma'
// This function get the email and returns a new session object that includes
// the userId
export const requireAuthentication = async context => {
const session = await getSession(context);
const errorOrUserNotFound = () => {
return {
redirect: {
destination: '/signup',
permanent: false
}
}
}
// If there is no user or there is an error ret to signup page
if (!session) {
errorOrUserNotFound();
}
// If the user is not found return same redirect to signup
else {
try {
const user = await prisma.user.findUnique({where: { email: session.user.email }});
if (!user) return errorOrUserNotFound();
// Must return a new session here that contains the userId...
else {
const newSession = {
user: {
...session.user,
userId: user.id
},
expires: session.expires
};
console.log(newSession);
return {
props: {
session: newSession
}
}
}
}
catch (error) {
if (error) {
console.log(error);
}
}
}
}
The react component looks like this. In the getServerSideProps i return the await function. The problem is that when I log the prop in the serverside, I get the following:
{
props: { session: { user: [Object], expires: '2022-12-17T20:18:52.580Z' } }
}
However, if i log the props in the clientside, I get an empty object...
//Clientside compoen
import { getSession } from "next-auth/react"
import { Fragment, useState } from "react";
import { requireAuthentication } from "../../lib/requireAuthentication";
import CreateListModal from "./CreateListModal";
const DashboardPage = props => {
const [loading, setloading] = useState(false);
console.log(props);
return (
<section className="border-4 border-orange-800 max-w-5xl mx-auto">
<CreateListModal userId={props.userId} loading={loading} setloading={setloading} />
</section>
)
}
export const getServerSideProps = async context => {
const session = await getSession(context);
const reqAuth = await requireAuthentication(context);
console.log(reqAuth);
return reqAuth
}
export default DashboardPage;

How can I abstract fetch in sveltkit for ssr?

I'm trying to pass in fetch which apparently isn't defined in my api libraries when using ssr:
<script context="module">
import setup from '$api/create-api';
import Jobs from '$api/jobs';
export async function load({ fetch }) {
setup(fetch);
const jobs = await Jobs.getAll();
return {
props: { jobs }
};
}
</script>
create-api.js
import { browser } from '$app/env';
let fetch = fetch || null;
async function api(path, body = {}, opts = {}) {
path = import.meta.env.VITE_API_ENDPOINT + path;
body = JSON.stringify(body);
const method = opts.method || 'GET';
const headers = {};
if (browser) {
const token = localStorage.getItem('token');
headers.Authorization = token ? 'Bearer ' + token : '';
}
const res = await fetch(path, {
method: opts.method || 'GET',
body: method === 'GET' ? null : body,
headers
});
if (res.ok) {
return await (opts.raw ? res.text() : res.json());
}
throw res;
}
export default api;
export const setup = (f) => {
fetch = f;
};
jobs.js
import api from './create-api';
class Jobs {
static async getAll() {
return await api('/jobs');
}
static async getAllMine() {
return await api('/jobs/me');
}
static async create(job) {
return await api('/jobs', job, { method: 'POST' });
}
static async update(job) {
return await api('/jobs/' + job.id, job, { method: 'PUT' });
}
static async deleteById(id) {
return await api('/jobs/' + id, {}, { method: 'DELETE' });
}
static async getById(id) {
console.log(id);
return await api('/jobs/' + id, {}, { method: 'GET' });
}
}
export default Jobs;
It seems you have to use the fetch passed into the script module for some reason. I tried installing node-fetch but got a ton of errors.

Query data from Dynamo DB using Global secondary index

I am setting a serverless application using AWS Amplify
My frontend app has the following code
import React, { Component } from 'react';
import './App.css';
import Layout from './Containers/Layout';
import { Amplify, API } from 'aws-amplify';
import aws_exports from './aws-exports';
Amplify.configure(aws_exports);
const apiName = 'top3DynamoDBAPI';
let path = '/listings/';
let partitionKey = 'Restaurant';
class App extends Component {
componentDidMount() {
API.get(apiName, path + partitionKey).then(response => {
console.log(response)
});
}
state = {
listings: {
}
}
render() {
return (
<div className="App">
<Layout />
</div>
);
}
}
export default App;
in my backend API the get method to retrieve items from the table is as follows
/********************************
* HTTP Get method for list objects *
********************************/
app.get(path + hashKeyPath, function(req, res) {
var condition = {}
condition[partitionKeyName] = {
ComparisonOperator: 'EQ'
}
if (userIdPresent && req.apiGateway) {
condition[partitionKeyName]['AttributeValueList'] = [req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH ];
} else {
try {
condition[partitionKeyName]['AttributeValueList'] = [ convertUrlType(req.params[partitionKeyName], partitionKeyType) ];
} catch(err) {
res.statusCode = 500;
res.json({error: 'Wrong column type ' + err});
}
}
let queryParams = {
TableName: tableName,
KeyConditions: condition
}
dynamodb.query(queryParams, (err, data) => {
if (err) {
res.statusCode = 500;
res.json({error: 'Could not load items: ' + err});
} else {
res.json(data.Items);
}
});
});
In my Dynamo DB table, I have a primary partition which has categories and one of them is called 'Restaurant' . So in my App.js I set some variables and call the API to get the items in ComponentDidMount
const apiName = 'top3DynamoDBAPI';
let path = '/listings/';
let partitionKey = 'Restaurant';
componentDidMount() {
API.get(apiName, path + partitionKey).then(response => {
console.log(response)
});
this returns all the items from the table where the primary partition matches a value called 'Restaurant'
Now I have global Secondary Partition called 'Listing_Location'
which currently has two values -- Sydney and Brisbane.
The backend API uses DynamoDB's Document Client and has the following variable initialised
const userIdPresent = false; // TODO: update in case is required to use that definition
const partitionKeyName = "Listing_Category";
const partitionKeyType = "S";
const sortKeyName = "Listing_Id";
const sortKeyType = "S";
const hasSortKey = sortKeyName !== "";
const path = "/listings";
const UNAUTH = 'UNAUTH';
const hashKeyPath = '/:' + partitionKeyName;
const sortKeyPath = hasSortKey ? '/:' + sortKeyName : '';
I am stuck at trying to figure out how to pass the secondary partition to my backend so I can lookup items based on location. Please can you help with this.
I was able to solve it with a combination of info from
DynamoDb how to query a Global Secondary Index? and
https://medium.com/#ole.ersoy/sending-an-email-parameter-with-amplify-api-get-request-4c1c8dc0c952
Now, my App.js looks like
componentDidMount() {
let params = {
'queryStringParameters': {
location: 'Brisbane'
}
}
API.get(apiName, path, params).then(response => {
this.setState({
listings: response
})
console.log(response)
});
}
New get function is
/* NEW GET ATTEMPT*/
app.get(path, function (req, res) {
if (userIdPresent) {
req.body['userId'] = req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH;
}
const location = req.query.location;
var queryItemParams = {
TableName: tableName,
IndexName: "ListingGSI",
KeyConditionExpression: "#location = :v_location",
ExpressionAttributeNames: {
"#location": "Listing_Location"
},
ExpressionAttributeValues: {
":v_location": location
}
};
dynamodb.query(queryItemParams, (err, data) => {
if (err) {
res.statusCode = 500;
res.json({ error: 'Could not load items: ' + err });
} else {
res.json(data.Items);
}
});
});

React Native Firebase Notifications aren't always shown

We have a React-Native app on iOS and Android. We use RN 0.61.5 and react-native-firebase 5.5.6.
We use react-native-firebase/notifications to add local notifications on the device. When we test this these notifications are planned correct but we get the feedback from some of our users that they don’t receive the notifications. I have added our notifications.ts file and our NotificationListener.tsx file below.
Does someone have an idea, possible reason of tips for debug/improve/fix our notifications?
Code
notifications.ts:
// Firebase Notifications
// ---
// Custom implementation on top of the Firebase Notifications library.
// This class is responsable for creating, scheduling and re-scheduling notifications.
// ------------------------------------------------------------------------- /
import firebase from "react-native-firebase";
import { Notification } from "react-native-firebase/notifications";
import { Recycle } from "#shd-types/fp-recycle-api";
import { isEmpty } from "ramda";
import { Store } from "../../store";
import { IS_ANDROID, IS_IOS } from "../../utils/device-info";
import i18n, { manualTranslate } from "../../utils/i18n";
import { NotificationsState } from "../../features/notifications/store/notifications.types";
import { CollectionRoundsState } from "../../features/collection-rounds/store/collectionRounds.types";
import { ProfileState } from "../../features/profile/store/profile.types";
const MAX_ALLOWED_NOTIFICATIONS = 64;
class FirebaseNotifications {
private permissionsGranted: boolean = false;
private language: string;
private notificationState: NotificationsState;
private collectionRoundsState: CollectionRoundsState;
private profileState: ProfileState;
constructor() {
// Ask for permission to send notifications
this.init();
const store: any = Store.getState();
this.language = store.global.language;
this.notificationState = store.notifications;
this.collectionRoundsState = store.collectionRounds;
this.profileState = store.profile;
// Create an Android Notification Channel
// More info on this here: https://rnfirebase.io/docs/v5.x.x/notifications/android-channels
this.initChannels();
}
async init(): Promise<void> {
const store: any = Store.getState();
this.language = store.global.language;
this.notificationState = store.notifications;
this.collectionRoundsState = store.collectionRounds;
this.profileState = store.profile;
if (this.permissionsGranted) {
return Promise.resolve();
}
const enabled = await firebase.messaging().hasPermission();
if (!enabled) {
try {
await firebase.messaging().requestPermission();
this.permissionsGranted = true;
} catch (error) {
// User has rejected permissions
this.permissionsGranted = false;
return Promise.reject();
}
} else {
this.permissionsGranted = true;
}
return Promise.resolve();
}
async initChannels() {
// Create the channel groups
await firebase.notifications().android.createChannelGroups([
// Main channel group
new firebase.notifications.Android.ChannelGroup("general", "General"),
// Channel group for each address
new firebase.notifications.Android.ChannelGroup("collections-home", "Home"),
]);
// Build a channel
const channel = new firebase.notifications.Android.Channel("collection-rounds-channel", "Collection Rounds", firebase.notifications.Android.Importance.Max)
.setDescription("Notifications for Collection Rounds")
.setGroup("collections-home");
// Create the channel
firebase.notifications().android.createChannel(channel);
}
// Collection Build Methods
// ------------------------------------------------------------------------- /
setAndroidNotificationProps(notification: Notification, channelId?: string, group?: string) {
// https://rnfirebase.io/docs/v5.x.x/notifications/reference/AndroidNotification
notification
.android.setLocalOnly(true)
.android.setPriority(firebase.notifications.Android.Priority.Max)
// Notification Style
.android.setColor("green") // Sets the accent color to use
.android.setVibrate([500, 500]) // Set notification vibration pattern.
.android.setLights(0xFF97E92A, 500, 500) // Set notification light to green.
.android.setSmallIcon("ic_launcher");
if (channelId) {
notification
.android.setChannelId(channelId);
}
if (group) {
notification
.android.setGroup(group)
.android.setGroupAlertBehaviour(firebase.notifications.Android.GroupAlert.All);
}
}
setIOSNotificationProps(notification: Notification, category?: string) {
// https://rnfirebase.io/docs/v5.x.x/notifications/reference/AndroidNotification
notification
.ios.setHasAction(false); // IOS 9 only.
if (category) {
notification
.ios.setCategory(category);
}
}
buildOpenAppNotification(fireDate: number): Notification {
const notification = new firebase.notifications.Notification()
.setNotificationId(`general-open-app-reminder-${fireDate}`)
.setTitle(i18n._("notifications.appRefresh.title"))
.setBody(i18n._("notifications.appRefresh.body"))
.setSound("default")
.setData({
schedule: {
fireDate,
},
});
if (IS_ANDROID) {
this.setAndroidNotificationProps(notification, "general");
}
if (IS_IOS) {
this.setIOSNotificationProps(notification, "general");
notification
.ios.setBadge(1);
}
return notification;
}
buildCollectionNotification(collection: Recycle.Collection.CollectionRound, fireDate?: number, sameDay = false): Notification {
const collectionTypeNames = collection.types.map((collectionType: Recycle.Type.Item) => manualTranslate(collectionType.name, this.language));
const notificationTitle = sameDay
? `${i18n._("notifications.collectionSameDay.title")}: ${collectionTypeNames.join(", ")}`
: `${i18n._("notifications.collectionDayBefore.title")}: ${collectionTypeNames.join(", ")}`;
// TODO: Map this to the location, if multiple
const notificationSubtitle = i18n._(this.profileState.address[0].alias);
const notificationBody = sameDay
? i18n._("notifications.collectionSameDay.body")
: i18n._("notifications.collectionDayBefore.body");
const notificationBodyExtended = sameDay
? i18n._("notifications.collectionSameDay.bodyExtended")
: i18n._("notifications.collectionDayBefore.bodyExtended");
const notification = new firebase.notifications.Notification()
.setNotificationId(`notification-collection-${collection.id}-${fireDate}`)
.setTitle(notificationTitle)
.setSubtitle(notificationSubtitle)
.setBody(notificationBody)
.setData({
...collection,
schedule: {
fireDate,
},
})
.setSound("default");
if (IS_ANDROID) {
this.setAndroidNotificationProps(notification, "collection-rounds-channel", "collections");
notification.android
.setBigText(
notificationBodyExtended,
notificationTitle,
notificationBody
);
}
if (IS_IOS) {
this.setIOSNotificationProps(notification, "collections");
}
return notification;
}
resetNotificationBadge() {
if (IS_IOS) {
const notifications = firebase.notifications();
notifications.setBadge(0);
}
}
// Scheduling Methods
// ------------------------------------------------------------------------- /
async rescheduleNotifications(collections?: Recycle.Collection.CollectionRound[]) {
// Clear all scheduled notifications
const notifications = await firebase.notifications().getScheduledNotifications();
if (notifications) {
notifications
.forEach((notification: Notification) => {
firebase.notifications().cancelNotification(notification.notificationId);
});
}
collections = collections ? collections : this.collectionRoundsState.data;
if (!collections) {
return;
}
// Check if the user has given permission to receive notifications
try {
await this.init();
} catch (err) {
return;
}
// Fetch the notificationsSettings
const homeNotificationSettings = this.notificationState.settings.home;
// TODO
// 1. Account for the amount of notifications (e.g. the day before AND the current day)
// 2. Account for the amount of addresses to schedule for.
//
// We will simulate this for now. (simplified)
const numberOfNotificationsPerCollection = Object.values(homeNotificationSettings.time).filter((timeSetting) => timeSetting.enabled).length;
const numberOfAddresses = 1;
const numberOfCollectionsToSchedule = Math.floor(MAX_ALLOWED_NOTIFICATIONS / (numberOfAddresses * numberOfNotificationsPerCollection)) - 1;
// TODO
// 1. Account for the time when we want the notification.
let lastNotificationTime = 0;
let collectionTypesToNotNotify: string[] = [];
// Filter out the collectionTypes we don't want a notification for.
if (homeNotificationSettings) {
collectionTypesToNotNotify = Object.values(homeNotificationSettings.categories)
.filter((category) => !category.enabled )
.map((category) => category.key);
}
// Reschedule all collections
collections
.filter((collection) => {
// Filter out collections that have passed
if (new Date(collection.date).valueOf() < new Date().valueOf()) {
return false;
}
// Filter out collections that are not wanted
if (collectionTypesToNotNotify.length) {
const allow = [];
const localCollectionTypes = collection.types.map((type) => type.id);
for (const collectionType of localCollectionTypes) {
allow.push(!collectionTypesToNotNotify.includes(collectionType));
}
// The collection has at least 1 item we want notifications for.
return allow.includes(true);
}
return true;
})
.map((collection) => {
// Filter out the collections the user does not want.
if (!collectionTypesToNotNotify.length) {
return collection;
}
const localCollectionTypes = collection.types.map((type) => type.id);
return {
...collection,
types: localCollectionTypes.filter((collectionType) => {
return !collectionTypesToNotNotify.includes(collectionType);
}),
};
})
.slice(0, numberOfCollectionsToSchedule)
.forEach((collection, index, arr) => {
const collectionDay = new Date(collection.date);
let collectionNotificationTime = 0;
let collectionHour = 0;
let collectionMinutes = 0;
if (homeNotificationSettings.time.dayBefore.enabled) {
// Get the day before
const collectionNotificationDate = new Date(collectionDay).setDate(collectionDay.getDate() - 1);
// Set the notification hour
collectionHour = homeNotificationSettings.time.dayBefore.hours;
collectionMinutes = homeNotificationSettings.time.dayBefore.minutes;
collectionNotificationTime = new Date(collectionNotificationDate).setHours(collectionHour, collectionMinutes);
// Schedule the notification if it triggers in the future
this.scheduleCollectionNotification(collection, collectionNotificationTime);
}
if (homeNotificationSettings.time.dayOf.enabled) {
// Set the notification hour
collectionHour = homeNotificationSettings.time.dayOf.hours;
collectionMinutes = homeNotificationSettings.time.dayOf.minutes;
collectionNotificationTime = new Date(collectionDay).setHours(collectionHour, collectionMinutes);
// Schedule the notification if it triggers in the future
this.scheduleCollectionNotification(collection, collectionNotificationTime, true);
}
// Schedule the "open your app please" notification after 10 notifcations
// And after the last one
if ((index !== 0 && index % 10 === 0) || (index === arr.length - 1)) {
// Plan the reminder 1 day after the previous notification
const collectionDate = new Date(collectionDay).setDate(collectionDay.getDate() + 1);
lastNotificationTime = new Date(collectionDate).setHours(collectionHour, collectionMinutes);
firebase.notifications().scheduleNotification(
this.buildOpenAppNotification(lastNotificationTime),
{ fireDate: lastNotificationTime }
);
}
});
}
scheduleCollectionNotification(collection: Recycle.Collection.CollectionRound, fireDate: number, sameDay = false) {
// Schedule the notification if it triggers in the future
if (fireDate > new Date().valueOf() && !isEmpty(this.notificationState.settings.home.categories)) {
firebase.notifications().scheduleNotification(
this.buildCollectionNotification(collection, fireDate, sameDay),
{ fireDate }
);
}
}
}
export default new FirebaseNotifications();
NotificationListener.tsx:
import React from "react";
import firebase from "react-native-firebase";
import { Notification, NotificationOpen } from "react-native-firebase/notifications";
interface Props {
children: any;
}
class NotificationListener extends React.Component<Props> {
private notificationDisplayedListener: (notification?: Notification) => void;
private notificationListener: (notification?: Notification) => void;
private notificationOpenedListener: (notificationOpen?: NotificationOpen) => void;
constructor(props: Props) {
super(props);
this.notificationDisplayedListener = firebase.notifications().onNotificationDisplayed(this.onNotificationDisplayed);
this.notificationListener = firebase.notifications().onNotification(this.onNotification);
this.notificationOpenedListener = firebase.notifications().onNotificationOpened(this.onNotificationOpened);
this.onNotificationDisplayed = this.onNotificationDisplayed.bind(this);
this.onNotification = this.onNotification.bind(this);
this.onNotificationOpened = this.onNotificationOpened.bind(this);
}
// Lifecycle Methods
// ------------------------------------------------------------------------- /
// https://rnfirebase.io/docs/v5.x.x/notifications/receiving-notifications#App-Closed
async componentDidMount() {
const notificationOpen: NotificationOpen = await firebase.notifications().getInitialNotification();
if (notificationOpen) {
// App was opened by a notification
this.onNotificationOpened(notificationOpen);
}
}
componentWillUnmount() {
this.notificationDisplayedListener();
this.notificationListener();
this.notificationOpenedListener();
}
// Callback Methods
// ------------------------------------------------------------------------- /
// Triggered when a particular notification has been displayed
onNotificationDisplayed(notification: Notification) {
// ANDROID: Remote notifications do not contain the channel ID. You will have to specify this manually if you'd like to re-display the notification.
// Leave this for debugging purposes
// console.log("Notification Displayed!:", notification);
}
// Triggered when a particular notification has been received, but not displayed
onNotification(notification: Notification) {
// Leave this for debugging purposes
// console.log("Notification received, but not displayed!:", notification);
// Force the notification in the OS NotificationBar
firebase.notifications().displayNotification(notification);
}
// Triggered when a particular notification has been clicked / tapped / opened whent he app is in the foreground or background.
onNotificationOpened(notificationOpen: NotificationOpen) {
// Get the action triggered by the notification being opened
const action = notificationOpen.action;
// Get information about the notification that was opened
const notification: Notification = notificationOpen.notification;
// Leave this for debugging purposes
// console.log("Notification Opened!:", notificationOpen);
// Remove the notification once it has been openend.
firebase.notifications().removeDeliveredNotification(notificationOpen.notification.notificationId);
}
// Render Methods
// ------------------------------------------------------------------------- /
render() {
return this.props.children;
}
}
export default NotificationListener;```

Expected Argument Error for .doc() when called on Firestore Collection

I want to create a new user document in my Cloud Firestore database whenever a new user logs in. Each doc should have a unique id and I want a "uid" property for each user to match the unique auto-generated id for the doc. At first, I just always ran an update on the user, but I figured it could be helpful to separate my create and update logic. As you can see I haven't worked out how to query if a user exists, but I figured I should test the createUser function before continuing.
Anyway, while I was testing my createUser function I ran into a compilation error.
ERROR in src/app/services/auth.service.ts(64,22): error TS2554:
Expected 1 arguments, but got 0.
UPDATE:
When I try to run the function from localhost after compilation I get this error in the console.
Function CollectionReference.doc() requires its first argument to be
of type string, but it was: undefined
Here is my proposed solution:
import { Injectable } from '#angular/core';
import { User } from './../models/user.model';
import { PermissionsService } from './permissions.service';
import { auth } from 'firebase/app';
import { AngularFireAuth } from 'angularfire2/auth';
import {
AngularFirestore,
AngularFirestoreDocument,
AngularFirestoreCollection,
} from 'angularfire2/firestore';
import { Observable, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';
#Injectable({
providedIn: 'root',
})
export class AuthService {
usersCollection: AngularFirestoreCollection<User> = null;
user: Observable<User>;
constructor(
private afAuth: AngularFireAuth,
private db: AngularFirestore,
private permissionsService: PermissionsService,
) {
this.usersCollection = db.collection('users');
this.user = this.afAuth.authState.pipe(
switchMap((user) => {
if (user) {
return this.db
.doc<User>(`users/${user.uid}`)
.valueChanges();
} else {
return of(null);
}
}),
);
}
loginGoogle() {
const provider = new auth.GoogleAuthProvider();
return this.oAuthLogin(provider);
}
loginFacebook() {
const provider = new auth.FacebookAuthProvider();
return this.oAuthLogin(provider);
}
loginTwitter() {
const provider = new auth.TwitterAuthProvider();
return this.oAuthLogin(provider);
}
oAuthLogin(provider) {
return this.afAuth.auth.signInWithPopup(provider).then((credential) => {
//if(the user exists already)
//this.updateUserData(credential.user);
//else
this.createUser();
});
}
createUser() {
const newUserRef = this.usersCollection.doc<User>(); // Error here
let newUser: User;
this.user.subscribe((userData) => {
newUser = {
uid: newUserRef.id,
email: userData.email,
photoURL: userData.photoURL,
displayName: userData.displayName,
roles: {
member: true,
},
permissions: this.permissionsService.memberPermissions;
};
});
newUserRef
.set(newUser)
.then(() => {
console.log('created user');
})
.catch((err) => {
console.log('Error adding user: ' + err);
});
}
updateUserData(user) {
const userRef: AngularFirestoreDocument<any> = this.db.doc(
`users/${user.uid}`,
);
const userPermissions = this.addPermissions(userRef);
console.log(userPermissions); // This works
const data: User = {
uid: user.uid,
email: user.email,
photoURL: user.photoURL,
displayName: user.displayName,
roles: {
member: true,
}, // I need to make sure this keeps current user roles
permissions: userPermissions,
};
console.log(data); // This works
userRef
.set(data)
.then(() => {
console.log('Success: Data for userDoc overwritten');
})
.catch((err) => {
console.error('Error writing to userDoc: ' + err);
});
}
addPermissions(userRef) {
const tempPermissions = [];
userRef.valueChanges().subscribe((userdata) => {
if (userdata.roles.reader === true) {
tempPermissions.push(this.permissionsService.memberPermissions);
}
if (userdata.roles.author === true) {
tempPermissions.push(this.permissionsService.authorPermissions);
}
if (userdata.roles.admin === true) {
tempPermissions.push(this.permissionsService.adminPermissions);
}
});
return tempPermissions;
}
checkPermissions(permission: string) {
if (!this.user) {
return false;
} else {
this.user.subscribe((data) => {
for (const p of data.permissions) {
if (p === permission) {
return true;
}
}
return false;
});
}
}
logout() {
this.afAuth.auth.signOut();
this.user = null;
}
}
I checked the documentation on the .doc() function and it should work fine with 0 arguments. It should be returning an empty doc reference. However, it keeps throwing the error saying it expects 1 argument. Any idea why this isn't working?

Resources