I have a Client Storage, it is needed to update @client (an analogue of the driver for the database, but used for the Apollo Client), if the token is changed There is a User Storage, all data about users is stored in it, when @client is changed, the UpdateUser action is not launched, or starts but uses the old @client version from ClientStorage
Client Storage:
import {action, computed, makeObservable, observable} from "mobx";
import {setContext} from "@apollo/client/link/context";
import {ApolloClient, ApolloLink, HttpLink, InMemoryCache} from "@apollo/client";
import {onError} from "apollo-link-error";
class Client{
token = localStorage.getItem('token'); //Токен авторизации, самая важная вешь в проекте!
// При запуски он достается из локального хранилища
changeToken(token){ //Функция для того, чтобы при логировании можно было записать новый токен
this.token = token
localStorage.setItem('token', token)
// console.log("CHANGE TOKEN")
}
constructor() {
makeObservable(this, {
token: observable,
changeToken: action,
AutoUpdatedApolloClient: computed({name: "UPDATE CLIENT"})
})
}
get AutoUpdatedApolloClient(){ //Если токен обновился, то эта вычисляемая функция обновляется и
//предоставляет всем элементам системы новый @client, например если пользователь залогинится,
//все последующие запросы и мутации будут происходить от его лица(в частности запрос на
// получение данных о пользователе), так же это позволит в будущем добавлять другие заголоки
//для запросов, если это понадобится
const authLink: any = setContext((_, { headers }) => {
// процесс создания авторизационного заголовка
return {
headers: {
...headers,
authorization: 'JWT '+ this.token,
}
}
});
//Ссылка на бэкенд
const httpLink = new HttpLink({
uri: '### SECRET URL ####'
// Additional options
});
const errorLink: any = onError(({ graphQLErrors }) => {
if (graphQLErrors) graphQLErrors.map(({ message }) => console.log(message))
})
//Конечная сборка @client
const client = new ApolloClient({
link: ApolloLink.from([errorLink, authLink, httpLink]),
cache: new InMemoryCache()
});
console.log("new client")
//Новый клиент собран и расшеривается между всеми, кто его использует
return(client)
}
}
export default new Client
User Storage
import {autorun, makeAutoObservable, reaction} from "mobx"
import {GET_USER_DATA, LOGIN_MUTATION} from "./Struct";
import React from 'react';
import ClientStorage from "../ApolloStorage/ClientStorage";
class User{
username = ''//Имя пользователя, отображается в навигационной панели
mail = ''//Пока нигде не используется, может потом пригодится
isLogin = false //Вошел ли пользователь в систему или нет
userAccessLevel = "STUDENT"//Уровень доступа, если он будет ADMIN или TEACHER, то откроется редактор
doLoginSuccess = false //Результат того, смог ли пользователь залогинется в компоненте Login
doLoginReturnError = false //Нужна, чтобы понимать, что бы понимать, что при логине была получена ошибка
clientStorage = ClientStorage//Получаем прямой доступ и подписку на изменение в хранилище @client
//для Apollo (для Query и Mutation)
constructor() {
makeAutoObservable(this)
autorun(()=>this.UpdateUser())
reaction(()=>ClientStorage.AutoUpdatedApolloClient, ()=>this.UpdateUser())
}
doLogin(mail, password){
//Функция для того, чтобы можно было залогиниться, её основная задача в том,
//чтобы получить новый токен, он отправляется в ClientStorage, где собирается новый
//apollo client. Так же эта функция меняет isLogin, а это уже запускает авторан для
//выкачивания всех данных о пользователе(выкаивание производится уже на новом @client)
ClientStorage.AutoUpdatedApolloClient
.mutate({ mutation: LOGIN_MUTATION, variables:{
pass: password,
mail: mail
}})
.then( result=>{
try{
if(result.data.tokenAuth.success){
// console.log("User Store data ------")
// console.log(result)
ClientStorage.changeToken(result.data.tokenAuth.token)
this.isLogin = true
this.doLoginSuccess = true
this.doLoginReturnError = false
}else{
this.doLoginSuccess = false
this.doLoginReturnError = true
}
}
catch (e){
console.log(e)
}
})
}
doUnLogin(){
//Функция для выхода, при выходе сбрасывает вообще все переменные, связанные с пользователем
ClientStorage.changeToken('')//Уничтожаем старый токен
this.username = ''//Чтобы не осталось имя того, кто был залогинен до этого
this.mail = ''//Обнуляем не всякий случай
this.isLogin = false//Обновит навигацию и роуты
this.userAccessLevel = "STUDENT"//На всякий случай забирем право на использование редактора
this.doLoginSuccess = false //Обнуляем все переменные связанные с прозессом логина
this.doLoginReturnError = false //Обнуляем все переменные связанные с прозессом логина
ClientStorage.changeToken('')//Обновляем токен
}
UpdateUser() {
if(ClientStorage.AutoUpdatedApolloClient){//Важное изменение, необходимо для тригера на изменения
//в @client
//Функция, которая получает всю информацию о пользователе
// if(this.doLoginSuccess || this.isLogin || ClientStorage.token !==''){//Самая важная строчка
//благодаря ней мы тригиремся на любые изменения в токене, логине или ток, как проходит процесс
//логирования в систему
ClientStorage.AutoUpdatedApolloClient
.query({
query: GET_USER_DATA
})
.then(result => {
try{
this.username = result.data.me.username
this.userAccessLevel = result.data.me.userAccessLevel
this.isLogin = true
console.log("TRY TO UPDATE USER DATA")
console.log(result)
}
catch (e) {
console.log(e)
}
})
// }else{
// //На всякий случай при неудачном логирование обнуляем свойства пользователя
// this.isLogin = false
// this.username = ''
// this.userAccessLevel = "STUDENT"
// }
}
console.log("UPDATE USER DATA")
}
}
export default new User