Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 46 additions & 16 deletions src/components/Auth/index.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,49 @@
import * as React from 'react'
import { useStore } from 'effector-react'

import {gSignIn} from '../../models/auth'
import {$signInForm, gSignIn, signIn, updateSignInForm} from '../../models/auth'

export const Auth: React.FC = () => (
<div
style={{
height: '100vh',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
}}
>
<h1> Remo Coding Challenge Join Room </h1>
<button id='email_signin'>Login with email</button>
<button id='gapi_signin' onClick={() => gSignIn()}> Login with Google </button>
</div>
)
export const Auth: React.FC = () => {
const [visibility, setVisibility] = React.useState<boolean>(false)
const { email = '', password = '' } = useStore($signInForm)

return (
<div
style={{
height: '100vh',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
}}
>
<h1> Remo Coding Challenge Join Room </h1>
<button id='email_signin' onClick={() => setVisibility(!visibility)}>Login with email</button>
{
visibility
? <form>
<label htmlFor='email'>Email</label>
<input
id='email'
type='email'
onChange={event => updateSignInForm({value: event.target.value, fieldName: 'email'})}
value={email}
/>
<label htmlFor='password'>Password</label>
<input
id='password'
type='password'
onChange={event => updateSignInForm({value: event.target.value, fieldName: 'password'})}
value={password}
/>
<input type='submit' onClick={(e) => {
e.preventDefault()
signIn({email, password})
}}/>
</form>
: null
}
<button id='gapi_signin' onClick={() => gSignIn()}> Login with Google </button>
</div>
)
}
22 changes: 21 additions & 1 deletion src/components/Theater/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,25 @@ import './Theater.css'
import MapImage from '../../assets/conference-map.svg'
import {tables, width, height} from './tableConfig.json'

import {$user, logout} from '../../models/auth'
import { $user, logout } from '../../models/auth'
import { $users, changeUserTable } from '../../models/users'

const Seats:React.FC<{ id: string; seats: {
x: number;
y: number;
}[]}> = ({id, seats}) => {
const users = useStore($users)
const seatedUsers = users.filter(({tableID}) => tableID === id);
return (
<>
{seatedUsers.map((user, index) => (
<div key={index} className='rt-seat' style={{ top: seats[index].y, left: seats[index].x }}>
<img className='seat-person' src={user.avatar} alt='person avatar' />
</div>
))}
</>
)
}

const Rooms:React.FC = () => (
<>
Expand All @@ -15,7 +33,9 @@ const Rooms:React.FC = () => (
key={index}
className='rt-room'
style={{ width, height, top: y, left: x }}
onClick={() => changeUserTable(id)}
>
<Seats id={id} seats={seats}/>
<div className='rt-room-name'>{id}</div>
</div>
))}
Expand Down
15 changes: 14 additions & 1 deletion src/models/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
import { createEvent, createEffect, createStore } from 'effector'

import { User } from './types'
import { User, Credentials } from './types'

export const gSignIn = createEvent()

export const signIn = createEvent<Credentials>()

export const logout = createEvent<string>()

export const updateSignInForm = createEvent<{ value: string; fieldName: string }>()

export const manageGmailProviderFx = createEffect<void, User>()

export const manageEmailProviderFx = createEffect<Credentials, User>()

export const signUpViaEmailFx = createEffect<Credentials, { email: string }>()

export const $user = createStore<User>({
email: ''
})

export const $signInForm = createStore<Credentials>({
email: '',
password: ''
})
55 changes: 52 additions & 3 deletions src/models/auth/init.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import { forward } from 'effector'
import { forward, sample } from 'effector'
import { auth } from 'firebase'

import {
gSignIn,
signIn,
logout,
updateSignInForm,
manageGmailProviderFx,
$user
manageEmailProviderFx,
signUpViaEmailFx,
$user,
$signInForm
} from './'

import {
addUserFx,
updateUsersTableFx
} from '../users'

const gProvider = new auth.GoogleAuthProvider()

manageGmailProviderFx.use(async () => {
Expand All @@ -21,11 +31,50 @@ manageGmailProviderFx.use(async () => {
return {email, avatar, fullName}
})

manageEmailProviderFx.use(async ({email, password}) => {
await auth().signInWithEmailAndPassword(email, password)
return {email}
});

signUpViaEmailFx.use(async ({email, password}) => {
await auth().createUserWithEmailAndPassword(email, password)
return {email}
})

$user
.reset(logout)
.on(manageGmailProviderFx.doneData, (_, user) => user)
.on([
manageGmailProviderFx.doneData, signUpViaEmailFx.doneData, manageEmailProviderFx.doneData
],
(_, user) => user
)
.on(addUserFx.doneData, (user, [_, id]) => ({
...user,
id
}))

$signInForm
.on(updateSignInForm, (form, {fieldName, value}) => ({
...form,
[fieldName]: value
}))

forward({
from: gSignIn,
to: manageGmailProviderFx
})

forward({
from: signIn,
to: manageEmailProviderFx
})

forward({
from: manageEmailProviderFx.fail.filterMap(({ params, error = {} }) => {
// @ts-ignore
if (error.code && error.code.includes('user-not-found')) {
return params;
}
}),
to: signUpViaEmailFx
})
5 changes: 4 additions & 1 deletion src/models/auth/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ export type User = {
email: string;
avatar?: string;
fullName?: string;
}
id?: string;
}

export type Credentials = { email: string; password: string }
3 changes: 2 additions & 1 deletion src/models/init.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
import './app/init'
import './auth/init'
import './auth/init'
import './users/init'
3 changes: 3 additions & 0 deletions src/models/tables/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import {createStore} from 'effector'

export const $currentConnectID = createStore('first-table')
28 changes: 28 additions & 0 deletions src/models/users/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {createEvent, createEffect, createStore} from 'effector';

import { UsersMap, TableIDUsersMap, FirebaseUser } from './types'

export const changeUserTable = createEvent<string>()
export const updateUsers = createEvent<UsersMap>()

export const fetchUsersFx = createEffect<void, UsersMap>()
export const addUserFx = createEffect<FirebaseUser, string[]>()
export const updateUsersTableFx = createEffect<{ id: string; tableID: string}, unknown>()
export const deleteUserFx = createEffect<string, unknown>()

export const $firebaseUsers = createStore<UsersMap>({});
export const $users = $firebaseUsers.map((fUsers) =>
Object.keys(fUsers).map((id) => fUsers[id])
)
export const $usersByEmail = $firebaseUsers.map((fUsers) => {
return Object.keys(fUsers).reduce((usersByEmail, id) => {
const email = fUsers[id].email;
return {
[email]: {
id,
...fUsers[id]
},
...usersByEmail
}
}, {})
})
80 changes: 80 additions & 0 deletions src/models/users/init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import {forward, sample} from 'effector'

import {database} from 'firebase'

import {
changeUserTable,
updateUsers,
addUserFx,
fetchUsersFx,
deleteUserFx,
updateUsersTableFx,
$firebaseUsers
} from './'

import {
$user,
manageGmailProviderFx,
manageEmailProviderFx,
signUpViaEmailFx
} from '../auth'

import {
$currentConnectID
} from '../tables'

const usersRef = database().ref('users/');
usersRef.on('value', (snapshot) => updateUsers(snapshot.val() === null ? {} : snapshot.val()))

addUserFx.use(async (user) => {
//@ts-ignore
const {path} = await database().ref('users/').push(user)
return path.pieces_
});

fetchUsersFx.use(async () => {
const snapshot = await database().ref('users/').once('value')
return snapshot.val()
})

updateUsersTableFx.use(async ({id, tableID}) => {
await database().ref('users/'+id).update({tableID})
})

deleteUserFx.use(async (id) => {
await database().ref('users/'+id).remove()
})

$firebaseUsers
.on([fetchUsersFx.doneData, updateUsers], (_, users) => users)
.on(updateUsersTableFx.done, (users, {params}) => {
const {id, tableID} = params;
return {
...users,
[id]: {
...users[id],
tableID
}
}
})

sample({
source: $currentConnectID,
clock: [manageGmailProviderFx.doneData, signUpViaEmailFx.doneData, manageEmailProviderFx.doneData],
fn: (id, user) => ({...user, tableID: id}),
target: addUserFx
})

sample({
source: $user,
clock: changeUserTable,
fn: ({id}, tableID) => ({
id, tableID
}),
target: updateUsersTableFx
})

forward({
from: addUserFx.doneData,
to: fetchUsersFx
})
15 changes: 15 additions & 0 deletions src/models/users/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export type FirebaseUser = {
email: string;
avatar?: string;
fullName?: string;
tableID: string;
id?: string;
}

export type UsersMap = {
[key: string]: FirebaseUser
}

export type TableIDUsersMap = {
[key: string]: FirebaseUser[]
}