sheva77 3 lat temu
rodzic
commit
e94e98d086

+ 3 - 4
chat_final_bootstrap/src/Actions/ActionLogin.js

@@ -6,7 +6,6 @@ import { gql } from "../Actions";
 export const actionAuthLogin = (jwt) => ({ type: "LOGIN", jwt });
 
 export const actionAuthLogout = () => {
-    //FIXME: надо засунуть router в redux
     history.push("/");
     return { type: "LOGOUT" };
 };
@@ -36,7 +35,7 @@ export const actionUserInfo = (userId) => async (dispatch) => {
                                 owner{_id}
                                 avatar {url}
                                 members{_id login nick avatar{url}}
-                                }
+                            }
                         }
                     }`,
                 { userId: JSON.stringify([{ _id: userId }]) }
@@ -65,7 +64,7 @@ export const actionLogin = (login, password) => async (dispatch) => {
             )
         )
     );
-    console.log("loginData", loginData);
+    // console.log("loginData", loginData);
 
     if (loginData && loginData.data.login) {
         dispatch(actionAuthLogin(loginData.data.login));
@@ -78,7 +77,7 @@ export const actionLogin = (login, password) => async (dispatch) => {
                 (store.getState().promise.login &&
                     store.getState().promise.login.error &&
                     store.getState().promise.login.error.message) ||
-                "Проблемы с сетью/сервером"
+                "Проверьте login password"
             }`
         );
     }

+ 73 - 8
chat_final_bootstrap/src/Actions/ActionsGqlUpsert.js

@@ -2,7 +2,7 @@
 //
 import { urlUploadConst } from "../const";
 import { actionPromise } from "../Reducers";
-import { gql, actionSearchMessagesByChatId, actionUserInfo } from "../Actions";
+import { gql, actionSearchMessagesByChatId, actionUserInfo, actionAuthLogout } from "../Actions";
 import history from "../history";
 
 export const actionMessageUpsert = ({ text, chatId }) => async (dispatch) => {
@@ -83,13 +83,7 @@ export const actionCreateNewChat = ({ _id, title, members, avaFile }) => async (
 
     if (chatData && chatData.data && chatData.data.ChatUpsert && chatData.data.ChatUpsert._id) {
         if (avaFile) {
-            let dataSingl = new FormData();
-            dataSingl.set("media", avaFile);
-            let avaUploadResult = await fetch(`${urlUploadConst}/upload`, {
-                method: "POST",
-                headers: localStorage.authToken ? { Authorization: "Bearer " + localStorage.authToken } : {},
-                body: dataSingl,
-            }).then((res) => res.json());
+            let avaUploadResult = await uploadMedia(avaFile);
 
             await dispatch(actionMediaUpsert({ chatId: chatData.data.ChatUpsert._id, mediaId: avaUploadResult._id }));
         }
@@ -103,3 +97,74 @@ export const actionCreateNewChat = ({ _id, title, members, avaFile }) => async (
         history.goBack();
     }
 };
+
+const uploadMedia = async (media) => {
+    let dataSingl = new FormData();
+    dataSingl.set("media", media);
+    return await fetch(`${urlUploadConst}/upload`, {
+        method: "POST",
+        headers: localStorage.authToken ? { Authorization: "Bearer " + localStorage.authToken } : {},
+        body: dataSingl,
+    }).then((res) => res.json());
+};
+
+export const actionUserUpdate = ({ _id, login, nick, password, avaFile, isNeedLogout }) => async (dispatch) => {
+    // console.log("+++++++ ", isNeedLogout, _id, login, nick, password, avaFile);
+
+    let dataObj = { _id };
+
+    if (login) dataObj.login = login;
+    if (nick) dataObj.nick = nick;
+    if (password) dataObj.password = password;
+
+    if (avaFile) {
+        let avaUploadResult = await uploadMedia(avaFile);
+
+        if (avaUploadResult && avaUploadResult._id) {
+            dataObj.avatar = { _id: avaUploadResult._id };
+        } else {
+            alert("Ошибка загрузки аватарки");
+        }
+    }
+
+    // console.log("dataObj - ", dataObj);
+
+    let userUpserResult = await dispatch(
+        actionPromise(
+            "UserUpsert",
+            gql(
+                `mutation UserUpsert($userData:UserInput){
+                    UserUpsert(user: $userData){
+                        _id
+                        login
+                        nick
+                        avatar {url}
+                        chats {
+                            _id
+                            title
+                            owner{_id}
+                            avatar {url}
+                            members{_id login nick avatar{url}}
+                        }
+                    }
+                }`,
+                { userData: dataObj }
+            )
+        )
+    );
+
+    // console.log("userUpserResult - ", userUpserResult);
+
+    if (!userUpserResult.errors) {
+        if (isNeedLogout) {
+            await dispatch(actionAuthLogout());
+            console.log("upsert success after actionLogin");
+        } else {
+            await dispatch(actionUserInfo(_id));
+        }
+    } else {
+        alert(`Обновить данные пользователя не удалось \n ${userUpserResult.errors}`);
+    }
+
+    history.push("/main");
+};

+ 2 - 1
chat_final_bootstrap/src/Actions/index.js

@@ -8,7 +8,7 @@ import {
 } from "./ActionsGql";
 import { actionMsgNewChat, actionCurChatId, actionMsgInsertInHead } from "./ActionsMsg";
 import { actionAddUserToChatList, actionDelUserFromChatList, actionNewChatList } from "./ActionsChatUsers";
-import { actionCreateNewChat, actionMessageUpsert } from "./ActionsGqlUpsert";
+import { actionCreateNewChat, actionMessageUpsert, actionUserUpdate } from "./ActionsGqlUpsert";
 
 export {
     actionUserInfo,
@@ -29,4 +29,5 @@ export {
     actionAllUsersFind,
     actionCreateNewChat,
     actionMessageUpsert,
+    actionUserUpdate,
 };

+ 15 - 3
chat_final_bootstrap/src/App.scss

@@ -137,8 +137,8 @@ body {
 }
 
 .AllUsersList {
-    max-height: calc(100vh - 285px);
-    min-height: calc(100vh - 285px);
+    max-height: calc(100vh - 215px);
+    min-height: calc(100vh - 215px);
     overflow: auto;
 }
 
@@ -149,7 +149,7 @@ body {
 
 .ChatDashBoard {
     min-height: calc(100vh - 110px);
-    padding: 20px;
+    padding: 10px 20px;
     table td:first-child {
         white-space: nowrap;
         text-align: end;
@@ -157,5 +157,17 @@ body {
     }
 }
 
+.ModalUserDashboard {
+    position: fixed;
+    left: 50%;
+    top: 50%;
+    z-index: 10;
+}
+
 @import "/node_modules/bootstrap/scss/bootstrap";
 @import "./login.scss";
+// @media (prefers-reduced-motion: reduce);
+
+* {
+    transition: all 0.3s linear;
+}

+ 2 - 2
chat_final_bootstrap/src/Components/ButtonCansel.js

@@ -2,9 +2,9 @@ import { Button } from "react-bootstrap";
 import history from "../history";
 import houseSvg from "../icons/house.svg";
 
-export const ButtonCancel = () => (
+export const ButtonCancel = ({ cansel = () => history.goBack() }) => (
     <>
-        <Button className="gradient rounded-3 ms-2 mb-2" variant="secondary btn-sm" onClick={() => history.goBack()}>
+        <Button className="gradient rounded-3 ms-2 mb-2" variant="secondary btn-sm" onClick={cansel}>
             <i className="bi bi-x-square"></i>
             <span className="ms-2">Cansel</span>
         </Button>

+ 7 - 2
chat_final_bootstrap/src/Components/ChatMessages.js

@@ -199,8 +199,13 @@ const Messages = ({ _id = "", chatInfo, messages, getMsg }) => {
                         </Link>
                         <div className="fs-4 fw-bolder ms-2">{`${title}`}</div>
                     </div>
-                    <span className="position-absolute bottom-0 end-0  badge rounded-pill bg-secondary">
-                        {` _chatId: ${_id}`} <span className="visually-hidden">id чата</span>
+                    {/* <span className="position-absolute bottom-0 end-0  badge rounded-pill bg-secondary"> */}
+                    <span className="position-absolute bottom-0 end-0 rounded-pill bg-success">
+                        <Link to={`/newchat/${_id}`} className="noUnderLine text-light">
+                            {/* {` _chatId: ${_id}`} */}
+                            <i className="bi bi-pencil-fill text- mx-3"></i>{" "}
+                            {/* <span className="visually-hidden">id чата</span> */}
+                        </Link>
                     </span>
                 </div>
             )}

+ 0 - 0
chat_final_bootstrap/src/Components/UserDashBoard.js


+ 217 - 17
chat_final_bootstrap/src/Layout/UserInfo.js

@@ -1,23 +1,223 @@
 import myPhoto from "../images/Iam_new2.jpg";
 import personFillIcon from "../icons/person-fill.svg";
-import { CButtonLogout, CLoginInfo } from "../Components";
+import { CButtonLogout, CLoginInfo, ButtonCancel } from "../Components";
 import { Link } from "react-router-dom";
 import { urlUploadConst } from "../const";
 import { connect } from "react-redux";
+import { useEffect, useRef, useState } from "react";
+import { Button, Form, Modal } from "react-bootstrap";
+import { actionUserUpdate } from "../Actions";
 
-const UserInfo = ({ avatarUrl }) => (
-    <div className="bg-gradient bg-success text-white p-1 py-2 mb-2 text-white">
-        <Link to="/dashboard" className="noUnderLine">
-            <span className="m-2  text-nowrap avatarka">
-                <img
-                    className="border border-2 border-success bg-light"
-                    src={avatarUrl ? `${urlUploadConst}/${avatarUrl}` : personFillIcon}
-                />
-
-                <CLoginInfo />
-            </span>
-        </Link>
-    </div>
-);
-
-export const CUserInfo = connect((s) => ({ avatarUrl: s.auth && s.auth.avatarUrl }))(UserInfo);
+const UserInfo = ({ myId, avatarUrl, login, nick, doUpdate = null }) => {
+    // console.log(avatarUrl, login, nick);
+
+    const [show, setShow] = useState(false);
+
+    const [srcAva, setSrcAva] = useState("");
+    const uploadRef = useRef(null); // ссылка на input type=file для авы
+
+    const [inpLogin, setInpLogin] = useState(login);
+    const inpLoginRef = useRef(null);
+
+    const [inpNick, setInpNick] = useState(nick);
+    const inpNickRef = useRef(null);
+    const [tempNick, setTempNick] = useState(nick);
+
+    const [inpPass, setInpPass] = useState("");
+    const inpPassRef = useRef(null);
+
+    const [isNeedLogout, setIsNeedLogout] = useState(false);
+
+    const initialization = () => {
+        setSrcAva("");
+        setInpLogin(login);
+        setInpNick(nick);
+        setInpPass("");
+    };
+
+    useEffect(() => {
+        if (inpLoginRef.current) inpLoginRef.current.value = inpLogin;
+        if (inpNickRef.current) inpNickRef.current.value = inpNick;
+        setTempNick(inpNick ? inpNick : inpLogin);
+    });
+
+    useEffect(() => {
+        setTempNick(inpNick ? inpNick : inpLogin);
+    }, [inpNick, inpLogin]);
+
+    function previewFile() {
+        let file = uploadRef.current && uploadRef.current.files && uploadRef.current.files[0];
+        let reader = new FileReader();
+
+        reader.onloadend = function () {
+            setSrcAva(reader.result);
+        };
+
+        if (file) {
+            // console.log(file);
+            reader.readAsDataURL(file);
+        } else {
+            setSrcAva("");
+        }
+    }
+
+    const handleClose = () => setShow(false);
+
+    const handleShow = () => {
+        initialization();
+        setShow(true);
+    };
+
+    return (
+        <>
+            <div className="position-relative">
+                <div className="bg-gradient bg-success text-white p-1 py-2 mb-2 text-white" onClick={handleShow}>
+                    <span className="m-2 text-nowrap avatarka">
+                        <img
+                            className="border border-2 border-success bg-light"
+                            src={avatarUrl ? `${urlUploadConst}/${avatarUrl}` : personFillIcon}
+                        />
+                        <CLoginInfo />
+                    </span>
+                </div>
+                <span className="position-absolute bottom-0 end-0">
+                    <i className="bi bi-pencil-fill text-light m-2"></i>
+                </span>
+            </div>
+
+            <Modal show={show} onHide={handleClose}>
+                {/* <Modal.Header closeButton> */}
+                <Modal.Header>
+                    <Modal.Title>User dashboard</Modal.Title>
+                </Modal.Header>
+                <Modal.Body>
+                    <Form>
+                        <div className="d-flex align-items-center">
+                            <div className="avatarka mb-4">
+                                {srcAva || avatarUrl ? (
+                                    <img
+                                        src={srcAva || `${urlUploadConst}/${avatarUrl}`}
+                                        className="border border-2 border-success"
+                                    ></img>
+                                ) : (
+                                    <div className="d-flex justify-content-center align-items-center bg-success border border-2 border-success gradient">
+                                        <div className="fs-5 text-light fw-bolder">
+                                            {!!tempNick &&
+                                                `${
+                                                    tempNick.trim().split(" ")[0] &&
+                                                    tempNick.trim().split(" ")[0][0] &&
+                                                    tempNick.trim().split(" ")[0][0].toUpperCase()
+                                                }` +
+                                                    `${
+                                                        (tempNick.trim().split(" ").slice(1).pop() &&
+                                                            tempNick.trim().split(" ").slice(1).pop()[0] &&
+                                                            tempNick
+                                                                .trim()
+                                                                .split(" ")
+                                                                .slice(1)
+                                                                .pop()[0]
+                                                                .toUpperCase()) ||
+                                                        ""
+                                                    }`}
+                                        </div>
+                                    </div>
+                                )}
+                            </div>
+
+                            <label className="btn btn-default btn-file">
+                                <input
+                                    type="file"
+                                    onChange={previewFile}
+                                    ref={uploadRef}
+                                    accept="image/*,image/jpeg"
+                                    className="form-control form-control-sm m-2"
+                                    aria-label="file example"
+                                    style={{ display: "none" }}
+                                />
+                                Browse...
+                            </label>
+                        </div>
+
+                        <Form.Group controlId="fromBasicLogin" className="mb-4">
+                            <Form.Label>Login</Form.Label>
+                            <Form.Control
+                                type="text"
+                                placeholder="Enter login"
+                                ref={inpLoginRef}
+                                onChange={(e) => {
+                                    setInpLogin(e.target.value);
+                                    setIsNeedLogout(true);
+                                }}
+                            />
+                            <Form.Text className="text-muted">If change then need to re-login</Form.Text>
+                        </Form.Group>
+
+                        <Form.Group controlId="fromBasicNick" className="mb-4">
+                            <Form.Label>Nick name</Form.Label>
+                            <Form.Control
+                                type="text"
+                                placeholder="Enter nick name"
+                                ref={inpNickRef}
+                                onChange={(e) => {
+                                    setInpNick(e.target.value);
+                                    setIsNeedLogout(true);
+                                }}
+                            />
+                            {/* <Form.Text className="text-muted">muted text</Form.Text> */}
+                        </Form.Group>
+
+                        <Form.Group controlId="fromBasicPassword" className="mb-4">
+                            <Form.Label>New password (if need)</Form.Label>
+                            <Form.Control
+                                type="password"
+                                placeholder="Enter NEW password"
+                                ref={inpPassRef}
+                                onChange={(e) => {
+                                    setInpPass(e.target.value);
+                                    setIsNeedLogout(true);
+                                }}
+                            />
+                            <Form.Text className="text-muted">If change then need to re-login</Form.Text>
+                        </Form.Group>
+
+                        <Form.Group>
+                            {/* <Form.Check type="checkbox" label="Remeber me" /> */}
+                            {/* <Button onClick={handleClose}>Close</Button> */}
+                            <ButtonCancel cansel={handleClose} />
+                            <Button
+                                className="gradient rounded-3 ms-2 mb-2"
+                                disabled={!inpLogin}
+                                onClick={() => {
+                                    doUpdate({
+                                        _id: myId,
+                                        login: inpLogin && inpLogin.trim(),
+                                        nick: inpNick && inpNick.trim(),
+                                        password: inpPass,
+                                        avaFile:
+                                            uploadRef.current && uploadRef.current.files && uploadRef.current.files[0],
+                                        isNeedLogout,
+                                    });
+                                    handleClose();
+                                }}
+                                variant="success btn-sm"
+                            >
+                                <i className="bi bi-check-square"></i>
+                                <span className="ms-2">Update</span>
+                            </Button>
+                        </Form.Group>
+                    </Form>
+                </Modal.Body>
+            </Modal>
+        </>
+    );
+};
+
+export const CUserInfo = connect(
+    (s) => ({
+        avatarUrl: s.auth && s.auth.avatarUrl,
+        myId: s.auth && s.auth.payloadId,
+        login: s.auth && s.auth.payload,
+        nick: s.auth && s.auth.nick,
+    }),
+    { doUpdate: actionUserUpdate }
+)(UserInfo);

+ 6 - 6
chat_final_bootstrap/src/Pages/PageNewChat.js

@@ -40,11 +40,11 @@ const PageNewChat = ({ doSearchUsers = null, match: { params: { _chatId = "" } =
                 <div className="row g-3">
                     <div className="col-md-4">
                         <div className="maxWidthForSideBar shadow">
-                            <div className="bg-light gradient shadow-sm border-2 rounded-3 flex-grow-1">
-                                <CUserInfo />
-                                <ButtonToMain />
-                                <ButtonCancel />
-
+                            <div className="bg-light gradient shadow-sm border-2 rounded-3 flex-grow-1 pt-2">
+                                {/* <CUserInfo /> */}
+                                {/* <ButtonToMain /> */}
+                                {/* <ButtonCancel /> */}
+                                <h6 className="ms-2 fs-5">Choose members:</h6>
                                 <div className="p-2">
                                     <input
                                         className="form-control mb-2 border border-success border-2"
@@ -55,7 +55,7 @@ const PageNewChat = ({ doSearchUsers = null, match: { params: { _chatId = "" } =
                                     ></input>
                                 </div>
 
-                                <h6 className="ms-2 fs-4">Choose members:</h6>
+                                {/* <h6 className="ms-2 fs-4">Choose members:</h6> */}
 
                                 <CAllUsersList searchUserStr={searchUserStr} />
                             </div>

+ 1 - 1
chat_final_bootstrap/src/Reducers/index.js

@@ -24,7 +24,7 @@ function authReducer(state, action) {
                 token: action.jwt,
                 payload: jwt_decode(action.jwt).sub.login,
                 payloadId: jwt_decode(action.jwt).sub.id,
-                //FIXME: ниже отладочный _userId
+                //FIXME: ниже отладочный _userId Antipmen
                 // payloadId: "5e97105693e2915e617c6fc1",
             };
         } catch (error) {