Skip to content

Commit

Permalink
feat(auth): update current profile info
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Nov 18, 2022
1 parent 294c57c commit ae90bed
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 6 deletions.
3 changes: 3 additions & 0 deletions playground/src/firebase.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { initializeApp } from 'firebase/app'
import { GoogleAuthProvider } from 'firebase/auth'

export function createFirebaseApp() {
return initializeApp({
Expand All @@ -12,3 +13,5 @@ export function createFirebaseApp() {
measurementId: 'G-RL4BTWXKJ7',
})
}

export const googleAuthProvider = new GoogleAuthProvider()
59 changes: 57 additions & 2 deletions playground/src/pages/authentication.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script lang="ts" setup>
import { googleAuthProvider } from '@/firebase'
import {
createUserWithEmailAndPassword,
EmailAuthProvider,
Expand All @@ -8,20 +9,31 @@ import {
signInWithPopup,
signInWithRedirect,
signOut,
GoogleAuthProvider,
updateCurrentUser,
updateProfile,
AuthCredential,
getRedirectResult,
} from 'firebase/auth'
import { ref } from 'vue'
import { useCurrentUser, useFirebaseAuth } from 'vuefire'
import {
updateCurrentUserProfile,
useCurrentUser,
useFirebaseAuth,
} from 'vuefire'
const auth = useFirebaseAuth()
const user = useCurrentUser()
let credential: AuthCredential | null = null
// new user
const email = ref('')
const password = ref('')
function signUp() {
// link to an existing anonymous account
if (user.value?.isAnonymous) {
const credential = EmailAuthProvider.credential(email.value, password.value)
credential = EmailAuthProvider.credential(email.value, password.value)
return linkWithCredential(user.value, credential).then(() => {
return signInWithEmailAndPassword(auth, email.value, password.value)
})
Expand All @@ -30,13 +42,48 @@ function signUp() {
// create a regular account
return createUserWithEmailAndPassword(auth, email.value, password.value)
}
function signinPopup() {
return signInWithPopup(auth, googleAuthProvider).then((result) => {
const googleCredential = GoogleAuthProvider.credentialFromResult(result)
credential = googleCredential
const token = googleCredential?.accessToken
console.log('Got Google token', token)
console.log('Got googleCredential', googleCredential)
})
}
async function changeUserImage() {
if (user.value) {
await updateCurrentUserProfile({
photoURL: 'https://i.pravatar.cc/150?u=' + Date.now(),
})
// updateCurrentUserEmail('hello@esm.dev')
}
}
function signinRedirect() {
signInWithRedirect(auth, googleAuthProvider)
}
getRedirectResult(auth).then((creds) => {
console.log('got creds', creds)
if (creds) {
// credential = creds.user.
}
})
</script>

<template>
<main>
<h1>Auth playground</h1>
<button @click="signOut(auth)">SignOut</button>
<button @click="signInAnonymously(auth)">Anonymous signIn</button>
<button @click="signinPopup()">Signin Google (popup)</button>
<button @click="signinRedirect()">Signin Google (redirect)</button>
<button @click="changeUserImage">Change User picture</button>

<form @submit.prevent="signUp()">
<fieldset>
<legend>New User</legend>
Expand Down Expand Up @@ -64,6 +111,14 @@ function signUp() {
<button>Signin</button>
</fieldset>
</form>

<p v-if="user">
Name: {{ user.displayName }} <br />
<img v-if="user.photoURL" :src="user.photoURL" />
</p>

<hr />

<p>Current User:</p>
<pre>{{ user }}</pre>
</main>
Expand Down
10 changes: 8 additions & 2 deletions src/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ import { getAuth, User } from 'firebase/auth'
import { App, ref, shallowRef } from 'vue-demi'
import { useFirebaseApp } from '../app'
import { getGlobalScope } from '../globals'
import { _Nullable } from '../shared'
import { AuthUserInjectSymbol, setupOnAuthStateChanged } from './user'

export { useCurrentUser, getCurrentUser } from './user'
export {
useCurrentUser,
getCurrentUser,
updateCurrentUserProfile,
updateCurrentUserEmail,
} from './user'

/**
* VueFire Auth Module to be added to the `VueFire` Vue plugin options.
Expand Down Expand Up @@ -36,7 +42,7 @@ modules: [VueFireAuth()]`)

return (firebaseApp: FirebaseApp, app: App) => {
const user = getGlobalScope(firebaseApp, app).run(() =>
shallowRef<User | null | undefined>()
ref<_Nullable<User>>()
)!
// userMap.set(app, user)
app.provide(AuthUserInjectSymbol, user)
Expand Down
44 changes: 42 additions & 2 deletions src/auth/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import {
} from 'firebase/auth'
import { inject, InjectionKey, Ref } from 'vue-demi'
import { useFirebaseApp } from '../app'
import type { _Nullable } from '../shared'
import type { _MaybeRef, _Nullable } from '../shared'

export const AuthUserInjectSymbol: InjectionKey<Ref<User | null | undefined>> =
export const AuthUserInjectSymbol: InjectionKey<Ref<_Nullable<User>>> =
Symbol('user')

/**
Expand All @@ -24,6 +24,46 @@ export function useCurrentUser() {
return inject(AuthUserInjectSymbol)!
}

/**
* Updates the current user profile and updates the current user state. This function internally calls `updateProfile()`
* from 'firebase/auth' and then updates the current user state.
*
* @param user - the user to update
* @param profile - the new profile information
*/
export function updateCurrentUserProfile(profile: {
displayName?: _Nullable<string>
photoURL?: _Nullable<string>
}) {
return getCurrentUser().then((user) => {
if (user) {
return updateProfile(user, profile).then(() => user.reload())
}
})
}

export function updateCurrentUserEmail(
newEmail: string,
credential: AuthCredential
) {
return getCurrentUser()
.then((user) => {
if (user) {
// TODO: Maybe this whole function should be dropped since it depends on reauthenticating first or we should let the user do it. Otherwise, we need a way to retrieve the credential token when logging in
reauthenticateWithCredential(user, credential)
}
return user
})
.then((user) => {
if (user) {
return updateEmail(user, newEmail).then(() => {
// @ts-expect-error: readonly property
user.email = newEmail
})
}
})
}

// @internal
type _UserState =
// state 1 waiting for the initial load
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export {
VueFireAuth,
useFirebaseAuth,
getCurrentUser,
updateCurrentUserProfile,
} from './auth'

// SSR
Expand Down
7 changes: 7 additions & 0 deletions src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,3 +247,10 @@ export interface _DataSourceOptions {
*/
wait?: boolean
}

/**
* Make all properties in T writable.
*/
export type _Mutable<T> = {
-readonly [P in keyof T]: T[P]
}

0 comments on commit ae90bed

Please sign in to comment.