From 9db59c071f210edcd24f603a5471ada8bd3d1e46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Geonizeli?= Date: Fri, 22 Jul 2022 12:53:57 -0300 Subject: [PATCH] use google user avatar as profile phone --- .../users/omniauth_callbacks_controller.rb | 19 +++-- app/graphql/types/user_type.rb | 1 + .../__generated__/graphql-schema.ts | 1 + app/javascript/__generated__/schema.graphql | 1 + .../components/AvatarEditor/AvatarEditor.tsx | 63 ---------------- .../components/AvatarEditor/PhotoCrop.tsx | 49 ------------ .../components/AvatarEditor/index.ts | 1 - .../components/UserAvatar/UserAvatar.tsx | 19 +++-- app/javascript/components/index.ts | 1 - app/javascript/contexts/UserContext.tsx | 63 +++++++--------- app/javascript/pages/session/Profile.tsx | 12 +-- app/models/user.rb | 7 ++ .../20220722153417_add_avatar_url_to_user.rb | 5 ++ db/schema.rb | 3 +- package.json | 2 - spec/factories/users.rb | 1 + spec/models/user_spec.rb | 1 + yarn.lock | 75 ------------------- 18 files changed, 72 insertions(+), 252 deletions(-) delete mode 100644 app/javascript/components/AvatarEditor/AvatarEditor.tsx delete mode 100644 app/javascript/components/AvatarEditor/PhotoCrop.tsx delete mode 100644 app/javascript/components/AvatarEditor/index.ts create mode 100644 db/migrate/20220722153417_add_avatar_url_to_user.rb diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb index b7e0224..e014b0d 100644 --- a/app/controllers/users/omniauth_callbacks_controller.rb +++ b/app/controllers/users/omniauth_callbacks_controller.rb @@ -2,15 +2,20 @@ module Users class OmniauthCallbacksController < Devise::OmniauthCallbacksController def google_oauth2 # You need to implement the method below in your model (e.g. app/models/user.rb) - @user = User.find_by!(email: request.env['omniauth.auth'].info['email']) + @user = User.from_omniauth( + request.env['omniauth.auth'].info['email'], + request.env['omniauth.auth'].info['image'] + ) - flash[:notice] = I18n.t 'devise.omniauth_callbacks.success', kind: 'Google' - sign_in_and_redirect @user, event: :authentication - rescue ActiveRecord::RecordNotFound => e - # Removing extra as it can overflow some session stores - session['devise.google_data'] = request.env['omniauth.auth'].except('extra') + if @user + flash[:notice] = I18n.t 'devise.omniauth_callbacks.success', kind: 'Google' - redirect_to new_user_registration_url, alert: e.message + sign_in_and_redirect @user, event: :authentication + else + session['devise.google_data'] = request.env['omniauth.auth'].except('extra') + + redirect_to new_user_registration_url, alert: 'User not found.' + end end end end \ No newline at end of file diff --git a/app/graphql/types/user_type.rb b/app/graphql/types/user_type.rb index 92a4cdf..93e9560 100644 --- a/app/graphql/types/user_type.rb +++ b/app/graphql/types/user_type.rb @@ -8,6 +8,7 @@ module Types field :name, String, null: false field :email, String, null: false field :roles, [Enums::RoleEnum], null: false + field :avatar_url, String, null: true def roles object.roles.map(&:name) diff --git a/app/javascript/__generated__/graphql-schema.ts b/app/javascript/__generated__/graphql-schema.ts index 05780fb..a983db9 100644 --- a/app/javascript/__generated__/graphql-schema.ts +++ b/app/javascript/__generated__/graphql-schema.ts @@ -454,6 +454,7 @@ export type UpdateQuestionPayload = { export type User = { __typename?: 'User'; + avatarUrl?: Maybe; email: Scalars['String']; id: Scalars['ID']; name: Scalars['String']; diff --git a/app/javascript/__generated__/schema.graphql b/app/javascript/__generated__/schema.graphql index f830026..0d8db9b 100644 --- a/app/javascript/__generated__/schema.graphql +++ b/app/javascript/__generated__/schema.graphql @@ -614,6 +614,7 @@ type UpdateQuestionPayload { } type User { + avatarUrl: String email: String! id: ID! name: String! diff --git a/app/javascript/components/AvatarEditor/AvatarEditor.tsx b/app/javascript/components/AvatarEditor/AvatarEditor.tsx deleted file mode 100644 index 1389f97..0000000 --- a/app/javascript/components/AvatarEditor/AvatarEditor.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, { FC, useState } from "react"; -import axios from "axios"; - -import { Alert } from "../Alert"; -import { Button } from "../Button"; -import { PhotoCrop } from "./PhotoCrop"; -import { useCurrentUser } from "../../contexts"; -import { Modal } from "../Modal"; - -type Props = { - isOpen: boolean - setIsOpen: (value: boolean) => void; -}; - -export const AvatarEditor: FC = ({ isOpen, setIsOpen }) => { - const [croppedImage, setCroppedImage] = useState() - const [alert, setAlert] = useState() - const { refetch, authToken } = useCurrentUser() - - const instance = axios.create({ - }); - - instance.defaults.headers.common.Authorization = `Bearer ${authToken}`; - - const onSubmit = () => { - instance - .post("/update_avatar", { - upload: croppedImage, - }) - .then((res) => { - if (res.status === 200) { - setIsOpen(false) - refetch() - } else { - setAlert(true); - } - }) - .catch(() => { - setAlert(true); - }); - }; - - return ( - - - - - } - > - {alert && Algo deu errado, tente novamente mais tarde.} - - - ); -}; diff --git a/app/javascript/components/AvatarEditor/PhotoCrop.tsx b/app/javascript/components/AvatarEditor/PhotoCrop.tsx deleted file mode 100644 index 1825b84..0000000 --- a/app/javascript/components/AvatarEditor/PhotoCrop.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React, { FC, useState } from "react"; -import PhotoCropper from "react-avatar-edit"; - -type Props = { - callback: (value: any) => void -} - -const borderStyle: React.CSSProperties = { - textAlign: 'center', - margin: 'auto', - borderStyle: 'dotted', - borderWidth: '0.3rem', - borderRadius: '0.3rem', -} - -export const PhotoCrop: FC = ({ callback }) => { - const [result, setResult] = useState(); - const onCrop = (cropped: any) => { - setResult(cropped); - callback(result); - }; - - const onClose = () => { - setResult(null); - }; - - const onBeforeFileLoad = (elem: any) => { - if (elem.target.files[0].size > 1000000) { - elem.target.value = ""; - alert("A imagem selecionada é grande de mais!") - } - }; - - const dimention = 300; - - return ( - onCrop(e)} - onClose={() => onClose()} - onBeforeFileLoad={onBeforeFileLoad} - /> - ); -}; diff --git a/app/javascript/components/AvatarEditor/index.ts b/app/javascript/components/AvatarEditor/index.ts deleted file mode 100644 index 2c68b59..0000000 --- a/app/javascript/components/AvatarEditor/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { AvatarEditor } from "./AvatarEditor"; diff --git a/app/javascript/components/UserAvatar/UserAvatar.tsx b/app/javascript/components/UserAvatar/UserAvatar.tsx index 92f8a6e..d497903 100644 --- a/app/javascript/components/UserAvatar/UserAvatar.tsx +++ b/app/javascript/components/UserAvatar/UserAvatar.tsx @@ -10,12 +10,19 @@ type Props = { export const UserAvatar: FC = ({user, className}) => { return (
- + {user.avatarUrl ? + {`Avatar + : + }
) }; diff --git a/app/javascript/components/index.ts b/app/javascript/components/index.ts index 52025ef..c0e25a1 100644 --- a/app/javascript/components/index.ts +++ b/app/javascript/components/index.ts @@ -4,7 +4,6 @@ export * from "./Button"; export * from "./Card"; export * from "./CardGrid"; export * from "./InputGroup"; -export * from "./AvatarEditor"; export * from "./Navegator"; export * from "./UserAvatar"; export * from "./Dialog"; diff --git a/app/javascript/contexts/UserContext.tsx b/app/javascript/contexts/UserContext.tsx index 39e6774..dea1226 100644 --- a/app/javascript/contexts/UserContext.tsx +++ b/app/javascript/contexts/UserContext.tsx @@ -1,23 +1,18 @@ -import React, { - createContext, useContext, useState, FC -} from "react"; -import { useQuery, gql } from "@apollo/client"; +import { gql, useQuery } from "@apollo/client"; +import React, { createContext, FC, useContext } from "react"; -import { Query, UserRole } from "../__generated__/graphql-schema"; import { UnauthorizedAccess } from "../pages/session"; import { Loading } from "../pages/shared"; +import { Query, UserRole } from "../__generated__/graphql-schema"; export type UserContext = { - user?: Query['currentUser'] - refetch: () => void - isOnlyTeacher: boolean -} + user?: Query["currentUser"]; + isOnlyTeacher: boolean; +}; const Context = createContext({ - refetch: () => { - }, isOnlyTeacher: false, -}) +}); export const useCurrentUser = (): UserContext => { const context = useContext(Context); @@ -30,41 +25,33 @@ export const useCurrentUser = (): UserContext => { }; const CurrentUserQuery = gql` - query CurrentUserQuery { - currentUser { - id - name - email - roles - } + query CurrentUserQuery { + currentUser { + id + name + email + roles + avatarUrl } + } `; type Props = { - children: any -} + children: any; +}; export const UserContext: FC = ({ children }) => { - const [user, setUser] = useState(); - const isOnlyTeacher = !!(user?.roles.includes(UserRole.Teacher) && user?.roles.length === 1) + const { loading, data } = useQuery(CurrentUserQuery); + const user = data?.currentUser; + const isOnlyTeacher = !!( + user?.roles.includes(UserRole.Teacher) && user?.roles.length === 1 + ); - const { refetch: refetchUserQuery, loading } = useQuery(CurrentUserQuery, { - onCompleted: ({ currentUser }) => { - setUser(currentUser) - } - }) - - const refetch = async () => { - const { data: { currentUser } } = await refetchUserQuery() - setUser(currentUser) - } - - if (loading) return - - if (!user) return + if (loading) return ; + if (!user?.roles.length) return ; return ( - + {children} ); diff --git a/app/javascript/pages/session/Profile.tsx b/app/javascript/pages/session/Profile.tsx index fa70011..08a64d3 100644 --- a/app/javascript/pages/session/Profile.tsx +++ b/app/javascript/pages/session/Profile.tsx @@ -1,27 +1,21 @@ -import React, { useState } from "react"; +import React from "react"; +import { CurrentUserAvatar, Navigator } from "../../components"; import { useCurrentUser } from "../../contexts"; -import { AvatarEditor, Navigator, CurrentUserAvatar } from "../../components"; export const Profile = () => { - const [avatarEditorIsOpen, setAvatarEditorIsOpen] = useState(false); const { user } = useCurrentUser(); return ( <> -
setAvatarEditorIsOpen(true)} >
diff --git a/app/models/user.rb b/app/models/user.rb index 9c33d5e..b825d38 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -3,6 +3,7 @@ # Table name: users # # id :bigint not null, primary key +# avatar_url :string # email :string default(""), not null # encrypted_password :string default(""), not null # name :string not null @@ -29,4 +30,10 @@ class User < ApplicationRecord has_and_belongs_to_many :roles validates :name, presence: true + + def self.from_omniauth(email, avatar_url) + @user = User.find_by!(email: email) + @user.update(avatar_url: avatar_url) + @user + end end diff --git a/db/migrate/20220722153417_add_avatar_url_to_user.rb b/db/migrate/20220722153417_add_avatar_url_to_user.rb new file mode 100644 index 0000000..a418565 --- /dev/null +++ b/db/migrate/20220722153417_add_avatar_url_to_user.rb @@ -0,0 +1,5 @@ +class AddAvatarUrlToUser < ActiveRecord::Migration[7.0] + def change + add_column(:users, :avatar_url, :string, null: true) + end +end diff --git a/db/schema.rb b/db/schema.rb index 1946a39..f622139 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2022_07_21_133421) do +ActiveRecord::Schema[7.0].define(version: 2022_07_22_153417) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -152,6 +152,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_07_21_133421) do t.string "name", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "avatar_url" t.index ["email"], name: "index_users_on_email", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end diff --git a/package.json b/package.json index 4242664..181fafc 100644 --- a/package.json +++ b/package.json @@ -10,13 +10,11 @@ "@nivo/core": "^0.79.0", "@nivo/pie": "^0.79.1", "@reduxjs/toolkit": "^1.8.3", - "axios": "^0.27.2", "boring-avatars": "^1.7.0", "ckeditor5-mathtype": "^1.0.4", "esbuild": "^0.14.49", "graphql": "^16.5.0", "react": "^18.2.0", - "react-avatar-edit": "^1.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.33.1", "react-icons": "^4.4.0", diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 6c29de0..62ad826 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -3,6 +3,7 @@ # Table name: users # # id :bigint not null, primary key +# avatar_url :string # email :string default(""), not null # encrypted_password :string default(""), not null # name :string not null diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index b763df3..b150fb6 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -3,6 +3,7 @@ # Table name: users # # id :bigint not null, primary key +# avatar_url :string # email :string default(""), not null # encrypted_password :string default(""), not null # name :string not null diff --git a/yarn.lock b/yarn.lock index 55b4a87..9821ee2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -484,11 +484,6 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - at-least-node@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" @@ -507,14 +502,6 @@ autoprefixer@^9.4.5, autoprefixer@^9.8.5: postcss "^7.0.32" postcss-value-parser "^4.1.0" -axios@^0.27.2: - version "0.27.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" - integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== - dependencies: - follow-redirects "^1.14.9" - form-data "^4.0.0" - "babel-plugin-styled-components@>= 1.12.0": version "2.0.7" resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.7.tgz#c81ef34b713f9da2b7d3f5550df0d1e19e798086" @@ -541,11 +528,6 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -blueimp-load-image@^5.14.0: - version "5.16.0" - resolved "https://registry.yarnpkg.com/blueimp-load-image/-/blueimp-load-image-5.16.0.tgz#16b763f57e6725f8865517bca8eb7c3dc7d41e09" - integrity sha512-3DUSVdOtlfNRk7moRZuTwDmA3NnG8KIJuLcq3c0J7/BIr6X3Vb/EpX3kUH1joxUhmoVF4uCpDfz7wHkz8pQajA== - boring-avatars@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/boring-avatars/-/boring-avatars-1.7.0.tgz#70ac7146bbf37d8e69a35544b24f1d75558f868a" @@ -706,13 +688,6 @@ color@^3.1.2: color-convert "^1.9.3" color-string "^1.6.0" -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - commander@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" @@ -853,11 +828,6 @@ defined@^1.0.0: resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" integrity sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ== -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - dependency-graph@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.9.0.tgz#11aed7e203bc8b00f48356d92db27b265c445318" @@ -1037,11 +1007,6 @@ esprima@^4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -exif-js@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/exif-js/-/exif-js-2.3.0.tgz#9d10819bf571f873813e7640241255ab9ce1a814" - integrity sha512-1Og9pAzG2FZRVlaavH8bB8BTeHcjMdJhKmeQITkX+uLRCD0xPtKAdZ2clZmQdJ56p9adXtJ8+jwrGp/4505lYg== - fast-glob@^3.2.9: version "3.2.11" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" @@ -1075,20 +1040,6 @@ find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -follow-redirects@^1.14.9: - version "1.15.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" - integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== - -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - fs-extra@^8.0.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -1372,11 +1323,6 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -konva@^8.3.5: - version "8.3.10" - resolved "https://registry.yarnpkg.com/konva/-/konva-8.3.10.tgz#66e93815878c2d48f1b6076d5a06b18ec4eb3217" - integrity sha512-5zOynjWBG9wWgpA634SDH+764eyoISpmHLTOCfQ3GFN8OBVd83Genk6H0R4D3hXV0kEGIFAv7RDcSVDtQpPOMw== - locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -1416,18 +1362,6 @@ micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - mini-create-react-context@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e" @@ -1729,15 +1663,6 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -react-avatar-edit@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/react-avatar-edit/-/react-avatar-edit-1.2.0.tgz#0ac169868367470a59e5a743ba1ee3093e6f343e" - integrity sha512-xTn/hC8KOTFvMagkAiLNq2yWTosCL88ZX34/CTb7dyadGSR27tc56JYBeYcLojTn2y/Hin8azT9aNVvQytAQ/w== - dependencies: - blueimp-load-image "^5.14.0" - exif-js "^2.3.0" - konva "^8.3.5" - react-dom@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"