refactor: add typescript support to project
This commit is contained in:
@@ -10,10 +10,8 @@ import InviteModal from "../../../common/component/InviteModal";
|
||||
import Tooltip from "../../../common/component/Tooltip";
|
||||
import IconSetting from "../../../assets/icons/setting.svg";
|
||||
import IconInvite from "../../../assets/icons/invite.from.channel.svg";
|
||||
// import { useDebounce} from "rooks";
|
||||
import { useReadMessageMutation } from "../../../app/services/message";
|
||||
import { useUpdateMuteSettingMutation } from "../../../app/services/contact";
|
||||
|
||||
import StyledLink from "./styled";
|
||||
import ChannelIcon from "../../../common/component/ChannelIcon";
|
||||
import { getUnreadCount } from "../utils";
|
||||
@@ -1,12 +1,11 @@
|
||||
import { useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
import DeleteConfirmModal from "../../settingChannel/DeleteConfirmModal";
|
||||
import NavItem from "./NavItem";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
|
||||
export default function ChannelList({ setDropFiles }) {
|
||||
const [currId, setCurrId] = useState(null);
|
||||
const { channelIds } = useSelector((store) => {
|
||||
const { channelIds } = useAppSelector((store) => {
|
||||
return { channelIds: store.channels.ids };
|
||||
});
|
||||
|
||||
@@ -82,4 +82,5 @@ const Styled = styled(NavLink)`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default Styled;
|
||||
@@ -3,7 +3,6 @@ import { useSelector } from "react-redux";
|
||||
import { useDebounce } from "rooks";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import FavList from "../FavList";
|
||||
|
||||
import Tooltip from "../../../common/component/Tooltip";
|
||||
import FavIcon from "../../../assets/icons/bookmark.svg";
|
||||
// import searchIcon from "../../../assets/icons/search.svg?url";
|
||||
@@ -16,6 +15,7 @@ import { StyledHeader, StyledDMChat } from "./styled";
|
||||
import LoadMore from "../LoadMore";
|
||||
import { renderMessageFragment } from "../utils";
|
||||
import useMessageFeed from "../../../common/hook/useMessageFeed";
|
||||
|
||||
export default function DMChat({ uid = "", dropFiles = [] }) {
|
||||
const {
|
||||
list: msgIds,
|
||||
@@ -1,11 +1,15 @@
|
||||
// import React from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
import { FC } from "react";
|
||||
import { getUnreadCount } from "../utils";
|
||||
|
||||
import NavItem from "./NavItem";
|
||||
export default function DMList({ uids, setDropFiles }) {
|
||||
const { userMessage, messageData, readUsers, loginUid } = useSelector((store) => {
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
|
||||
interface Props {
|
||||
uids: number[];
|
||||
setDropFiles: () => void;
|
||||
}
|
||||
|
||||
const DMList: FC<Props> = ({ uids, setDropFiles }) => {
|
||||
const { userMessage, messageData, readUsers, loginUid } = useAppSelector((store) => {
|
||||
return {
|
||||
loginUid: store.authData.uid,
|
||||
readUsers: store.footprint.readUsers,
|
||||
@@ -31,14 +35,20 @@ export default function DMList({ uids, setDropFiles }) {
|
||||
return { lastMid, unreads, uid };
|
||||
});
|
||||
// console.log("temp uids", sessions);
|
||||
return sessions
|
||||
.sort((s1, s2) => {
|
||||
if (!s1.lastMid) return s2.lastMid - Infinity;
|
||||
return s2.lastMid - s1.lastMid;
|
||||
})
|
||||
.map(({ lastMid, uid, unreads }) => {
|
||||
return (
|
||||
<NavItem key={uid} uid={uid} mid={lastMid} unreads={unreads} setFiles={setDropFiles} />
|
||||
);
|
||||
});
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{sessions
|
||||
.sort((s1, s2) => {
|
||||
if (!s1.lastMid) return s2.lastMid - Infinity;
|
||||
return s2.lastMid - s1.lastMid;
|
||||
})
|
||||
.map(({ lastMid, uid, unreads }) => {
|
||||
return (
|
||||
<NavItem key={uid} uid={uid} mid={lastMid} unreads={unreads} setFiles={setDropFiles} />
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DMList;
|
||||
@@ -6,6 +6,7 @@ import IconSurprise from "../../assets/icons/emoji.suprise.svg";
|
||||
// import IconForward from "../../../assets/icons/forward.svg";
|
||||
import IconRemove from "../../assets/icons/close.svg";
|
||||
import useFavMessage from "../../common/hook/useFavMessage";
|
||||
|
||||
const Styled = styled.div`
|
||||
padding: 16px;
|
||||
background: #f9fafb;
|
||||
@@ -83,6 +84,7 @@ const Styled = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function FavList({ cid = null, uid = null }) {
|
||||
const { favorites, removeFavorite } = useFavMessage({ cid, uid });
|
||||
const handleRemove = (evt) => {
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useKey } from "rooks";
|
||||
import toast from "react-hot-toast";
|
||||
import useDeleteMessage from "../../../common/hook/useDeleteMessage";
|
||||
import useFavMessage from "../../../common/hook/useFavMessage";
|
||||
import { updateSelectMessages } from "../../../app/slices/ui";
|
||||
@@ -10,8 +10,9 @@ import IconBookmark from "../../../assets/icons/bookmark.svg";
|
||||
import IconDelete from "../../../assets/icons/delete.svg";
|
||||
import IconClose from "../../../assets/icons/close.circle.svg";
|
||||
import ForwardModal from "../../../common/component/ForwardModal";
|
||||
import toast from "react-hot-toast";
|
||||
import DeleteMessageConfirmModal from "../../../common/component/DeleteMessageConfirm";
|
||||
import { useAppDispatch, useAppSelector } from "../../../app/store";
|
||||
|
||||
const Styled = styled.div`
|
||||
position: relative;
|
||||
padding: 16px;
|
||||
@@ -40,16 +41,19 @@ const Styled = styled.div`
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Operations({ context, id }) {
|
||||
const [deleteModalVisible, setDeleteModalVisible] = useState(false);
|
||||
const { canDelete } = useDeleteMessage();
|
||||
const { addFavorite } = useFavMessage({});
|
||||
const mids = useSelector((store) => store.ui.selectMessages[`${context}_${id}`]);
|
||||
const mids = useAppSelector((store) => store.ui.selectMessages[`${context}_${id}`]);
|
||||
const [forwardModalVisible, setForwardModalVisible] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleClose = () => {
|
||||
dispatch(updateSelectMessages({ context, id, operation: "reset" }));
|
||||
};
|
||||
|
||||
const handleFav = async () => {
|
||||
const added = await addFavorite(mids);
|
||||
if (added) {
|
||||
@@ -60,6 +64,7 @@ export default function Operations({ context, id }) {
|
||||
toast.error("Operation Failed!");
|
||||
}
|
||||
};
|
||||
|
||||
const toggleDeleteModal = (isSuccess = false) => {
|
||||
setDeleteModalVisible((prev) => !prev);
|
||||
if (isSuccess) {
|
||||
@@ -67,14 +72,17 @@ export default function Operations({ context, id }) {
|
||||
toast.success("Messages Deleted!");
|
||||
}
|
||||
};
|
||||
|
||||
const toggleForwardModal = () => {
|
||||
setForwardModalVisible((prev) => !prev);
|
||||
};
|
||||
|
||||
useKey("Escape", (evt) => {
|
||||
console.log("Escape keypress", evt);
|
||||
dispatch(updateSelectMessages({ context, id, operation: "reset" }));
|
||||
});
|
||||
const canDel = canDelete(mids);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Styled>
|
||||
@@ -1,7 +1,5 @@
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { useState, useRef, useEffect, FC, ReactElement } from "react";
|
||||
import { useDrop } from "react-dnd";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
import { NativeTypes } from "react-dnd-html5-backend";
|
||||
import ImagePreviewModal from "../../../common/component/ImagePreviewModal";
|
||||
import Send from "../../../common/component/Send";
|
||||
@@ -9,8 +7,19 @@ import Styled from "./styled";
|
||||
import Operations from "./Operations";
|
||||
import useUploadFile from "../../../common/hook/useUploadFile";
|
||||
import { ChatPrefixs } from "../../../app/config";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
|
||||
export default function Layout({
|
||||
interface Props {
|
||||
children: ReactElement;
|
||||
header: ReactElement;
|
||||
aside: ReactElement | null;
|
||||
contacts: ReactElement | null;
|
||||
dropFiles: [];
|
||||
context: string;
|
||||
to: number | null;
|
||||
}
|
||||
|
||||
const Layout: FC<Props> = ({
|
||||
children,
|
||||
header,
|
||||
aside = null,
|
||||
@@ -18,11 +27,11 @@ export default function Layout({
|
||||
dropFiles = [],
|
||||
context = "channel",
|
||||
to = null
|
||||
}) {
|
||||
}) => {
|
||||
const { addStageFile } = useUploadFile({ context, id: to });
|
||||
const messagesContainer = useRef(undefined);
|
||||
const messagesContainer = useRef<HTMLDivElement>(null);
|
||||
const [previewImage, setPreviewImage] = useState(null);
|
||||
const { selects, channelsData, contactsData } = useSelector((store) => {
|
||||
const { selects, channelsData, contactsData } = useAppSelector((store) => {
|
||||
return {
|
||||
selects: store.ui.selectMessages[`${context}_${to}`],
|
||||
channelsData: store.channels.byId,
|
||||
@@ -49,6 +58,7 @@ export default function Layout({
|
||||
}),
|
||||
[context, to]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (dropFiles?.length) {
|
||||
const filesData = dropFiles.map((file) => {
|
||||
@@ -63,6 +73,7 @@ export default function Layout({
|
||||
const closePreviewModal = () => {
|
||||
setPreviewImage(null);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const container = messagesContainer?.current;
|
||||
if (container) {
|
||||
@@ -85,6 +96,7 @@ export default function Layout({
|
||||
}
|
||||
}, []);
|
||||
const name = context == "channel" ? channelsData[to]?.name : contactsData[to]?.name;
|
||||
|
||||
return (
|
||||
<>
|
||||
{previewImage && <ImagePreviewModal data={previewImage} closeModal={closePreviewModal} />}
|
||||
@@ -112,4 +124,6 @@ export default function Layout({
|
||||
</Styled>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
+5
-1
@@ -1,4 +1,3 @@
|
||||
// import React from 'react'
|
||||
import Tippy from "@tippyjs/react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useNavigate, useLocation, useMatch } from "react-router-dom";
|
||||
@@ -30,9 +29,11 @@ export default function SessionContextMenu({
|
||||
channelMuted: context == "channel" ? store.footprint.muteChannels[id] : false
|
||||
};
|
||||
});
|
||||
|
||||
const handleChannelSetting = () => {
|
||||
navigateTo(`/setting/channel/${id}?f=${pathname}`);
|
||||
};
|
||||
|
||||
const handleReadAll = () => {
|
||||
// console.log("last mid", mids, lastMid);
|
||||
if (mid) {
|
||||
@@ -41,16 +42,19 @@ export default function SessionContextMenu({
|
||||
updateReadIndex(param);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveSession = () => {
|
||||
dispatch(removeUserSession(id));
|
||||
if (pathMatched) {
|
||||
navigateTo("/chat");
|
||||
}
|
||||
};
|
||||
|
||||
const handleChannelMute = () => {
|
||||
const data = channelMuted ? { remove_groups: [id] } : { add_groups: [{ gid: id }] };
|
||||
muteChannel(data);
|
||||
};
|
||||
|
||||
const items =
|
||||
context == "user"
|
||||
? [
|
||||
@@ -13,7 +13,10 @@ import IconLock from "../../../assets/icons/lock.svg";
|
||||
import useContextMenu from "../../../common/hook/useContextMenu";
|
||||
import { useNavigate, NavLink } from "react-router-dom";
|
||||
import useUploadFile from "../../../common/hook/useUploadFile";
|
||||
|
||||
// todo: move to root file
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
export default function Session({
|
||||
type = "user",
|
||||
id,
|
||||
@@ -23,6 +26,7 @@ export default function Session({
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
const { addStageFile } = useUploadFile({ context: type, id });
|
||||
|
||||
const [{ isActive }, drop] = useDrop(
|
||||
() => ({
|
||||
accept: [NativeTypes.FILE],
|
||||
@@ -44,6 +48,7 @@ export default function Session({
|
||||
}),
|
||||
[type, id]
|
||||
);
|
||||
|
||||
const { visible: contextMenuVisible, handleContextMenuEvent, hideContextMenu } = useContextMenu();
|
||||
const [data, setData] = useState(null);
|
||||
const { messageData, contactData, channelData, readIndex, loginUid, mids, muted } = useSelector(
|
||||
@@ -60,10 +65,12 @@ export default function Session({
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const handleImageError = (evt) => {
|
||||
evt.target.classList.add("channel_default");
|
||||
evt.target.src = iconChannel;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const tmp = type == "user" ? contactData[id] : channelData[id];
|
||||
if (!tmp) return;
|
||||
@@ -81,6 +88,7 @@ export default function Session({
|
||||
messageData,
|
||||
loginUid
|
||||
});
|
||||
|
||||
return (
|
||||
<li className="session">
|
||||
<ContextMenu
|
||||
@@ -1,15 +1,16 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import Styled from "./styled";
|
||||
import Session from "./Session";
|
||||
import DeleteChannelConfirmModal from "../../settingChannel/DeleteConfirmModal";
|
||||
import InviteModal from "../../../common/component/InviteModal";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
|
||||
export default function SessionList({ tempSession = null }) {
|
||||
const [deleteId, setDeleteId] = useState(null);
|
||||
const [inviteChannelId, setInviteChannelId] = useState(null);
|
||||
const [sessions, setSessions] = useState([]);
|
||||
const { channelIDs, DMs, readChannels, readUsers, channelMessage, userMessage, loginUid } =
|
||||
useSelector((store) => {
|
||||
useAppSelector((store) => {
|
||||
return {
|
||||
loginUid: store.authData.uid,
|
||||
channelIDs: store.channels.ids,
|
||||
@@ -20,6 +21,7 @@ export default function SessionList({ tempSession = null }) {
|
||||
readUsers: store.footprint.readUsers
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const cSessions = channelIDs.map((id) => {
|
||||
const mids = channelMessage[id];
|
||||
@@ -51,6 +53,7 @@ export default function SessionList({ tempSession = null }) {
|
||||
userMessage,
|
||||
tempSession
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Styled>
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
// import { useUpdateChannelMutation } from "../../app/services/channel";
|
||||
import { useUpdateMuteSettingMutation } from "../../app/services/contact";
|
||||
import { useReadMessageMutation } from "../../app/services/message";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
|
||||
export default function useSession({ type, id }) {
|
||||
const [inviteModalVisible, setInviteModalVisible] = useState(false);
|
||||
const [muteChannel] = useUpdateMuteSettingMutation();
|
||||
const [updateReadIndex] = useReadMessageMutation();
|
||||
const { messageData, contactData, channelData } = useSelector((store) => {
|
||||
const { messageData, contactData, channelData } = useAppSelector((store) => {
|
||||
return {
|
||||
messageData: store.message,
|
||||
contactData: store.contacts.byId,
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import dayjs from "dayjs";
|
||||
import styled from "styled-components";
|
||||
import reactStringReplace from "react-string-replace";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { isImage } from "../../common/utils";
|
||||
import { ContentTypes } from "../../app/config";
|
||||
@@ -9,7 +10,7 @@ import Divider from "../../common/component/Divider";
|
||||
import Message from "../../common/component/Message";
|
||||
import { updateSelectMessages } from "../../app/slices/ui";
|
||||
import Mention from "../../common/component/Message/Mention";
|
||||
import reactStringReplace from "react-string-replace";
|
||||
|
||||
// function debounce(callback, wait = 2000, immediate = false) {
|
||||
// let timeout = null;
|
||||
// return function () {
|
||||
@@ -22,6 +23,7 @@ import reactStringReplace from "react-string-replace";
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
|
||||
export function getUnreadCount({ mids = [], messageData = {}, loginUid = 0, readIndex = 0 }) {
|
||||
// console.log({ mids, loginUid, readIndex });
|
||||
// 先过滤掉空信息和from自己的
|
||||
@@ -124,6 +126,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const MessageWrapper = ({ selectMode = false, context, id, mid, children, ...rest }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
@@ -142,6 +145,7 @@ const MessageWrapper = ({ selectMode = false, context, id, mid, children, ...res
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export const renderMessageFragment = ({
|
||||
selectMode = false,
|
||||
isFirst = false,
|
||||
@@ -1,8 +1,9 @@
|
||||
// import React from 'react';
|
||||
import { FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import Avatar from "../../common/component/Avatar";
|
||||
import { useSelector } from "react-redux";
|
||||
import { NavLink, useLocation } from "react-router-dom";
|
||||
import Avatar from "../../common/component/Avatar";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
padding: 10px 12px;
|
||||
.avatar {
|
||||
@@ -16,10 +17,16 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export default function User({ uid = null }) {
|
||||
|
||||
interface Props {
|
||||
uid: number;
|
||||
}
|
||||
|
||||
const User: FC<Props> = ({ uid }) => {
|
||||
const { pathname } = useLocation();
|
||||
const user = useSelector((store) => store.contacts.byId[uid]);
|
||||
const user = useAppSelector((store) => store.contacts.byId[uid]);
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<NavLink to={`/setting?nav=my_account&f=${pathname}`}>
|
||||
@@ -29,4 +36,6 @@ export default function User({ uid = null }) {
|
||||
</NavLink>
|
||||
</StyledWrapper>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default User;
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import initCache, { useRehydrate } from "../../app/cache";
|
||||
import { useLazyGetFavoritesQuery } from "../../app/services/message";
|
||||
import { useLazyGetContactsQuery } from "../../app/services/contact";
|
||||
import { useLazyGetServerQuery } from "../../app/services/server";
|
||||
import useStreaming from "../../common/hook/useStreaming";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
// pollingInterval: 0,
|
||||
// const querySetting = {
|
||||
// refetchOnMountOrArgChange: true,
|
||||
@@ -12,7 +12,7 @@ import useStreaming from "../../common/hook/useStreaming";
|
||||
// let request = null;
|
||||
export default function usePreload() {
|
||||
const { rehydrate, rehydrated } = useRehydrate();
|
||||
const { loginUid, token } = useSelector((store) => {
|
||||
const { loginUid, token } = useAppSelector((store) => {
|
||||
return { loginUid: store.authData.uid, token: store.authData.token };
|
||||
});
|
||||
const { setStreamingReady } = useStreaming();
|
||||
@@ -1,16 +1,18 @@
|
||||
// import { useState, useEffect } from "react";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
import styled from "styled-components";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
|
||||
const StyledMagicButton = styled(Button)`
|
||||
width: 100%;
|
||||
margin-bottom: 16px;
|
||||
`;
|
||||
|
||||
export default function MagicLinkLogin() {
|
||||
const navigate = useNavigate();
|
||||
const handleGoogleLogin = () => {
|
||||
navigate("/send_magic_link");
|
||||
// signIn();
|
||||
};
|
||||
|
||||
return <StyledMagicButton onClick={handleGoogleLogin}>Sign in with Magic Link</StyledMagicButton>;
|
||||
}
|
||||
@@ -13,6 +13,7 @@ export default function OidcLoginEntry({ issuer }) {
|
||||
redirect_uri: `${location.origin}/#/login`
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
console.log("wtf", data);
|
||||
@@ -21,6 +22,7 @@ export default function OidcLoginEntry({ issuer }) {
|
||||
}
|
||||
}, [data, isSuccess]);
|
||||
console.log(issuer);
|
||||
|
||||
return (
|
||||
<StyledSocialButton disabled={isLoading} onClick={handleSolidLogin}>
|
||||
{Boolean(issuer.favicon) && <img src={issuer.favicon} className="icon" alt="icon" />}
|
||||
@@ -21,11 +21,13 @@ const StyledSignUpLink = styled.p`
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function MagicLinkLogin() {
|
||||
const navigate = useNavigate();
|
||||
const handleSignUp = () => {
|
||||
navigate("/register");
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledSignUpLink>
|
||||
<span>Don’t have an account?</span>
|
||||
@@ -16,6 +16,7 @@ import useGoogleAuthConfig from "../../common/hook/useGoogleAuthConfig";
|
||||
import useGithubAuthConfig from "../../common/hook/useGithubAuthConfig";
|
||||
import GoogleLoginButton from "../../common/component/GoogleLoginButton";
|
||||
import GithubLoginButton from "../../common/component/GithubLoginButton";
|
||||
|
||||
export default function LoginPage() {
|
||||
const { data: enableSMTP } = useGetSMTPStatusQuery();
|
||||
const [login, { isSuccess, isLoading, error }] = useLoginMutation();
|
||||
@@ -26,6 +27,7 @@ export default function LoginPage() {
|
||||
email: "",
|
||||
password: ""
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const query = new URLSearchParams(location.search);
|
||||
// const githubCode = query.get("gcode");
|
||||
@@ -121,6 +123,7 @@ export default function LoginPage() {
|
||||
};
|
||||
const { email, password } = input;
|
||||
if (!loginConfigSuccess) return null;
|
||||
|
||||
const {
|
||||
magic_link,
|
||||
github: enableGithubLogin,
|
||||
@@ -129,10 +132,12 @@ export default function LoginPage() {
|
||||
oidc = [],
|
||||
who_can_sign_up: whoCanSignUp
|
||||
} = loginConfig;
|
||||
|
||||
const enableMagicLink = enableSMTP && magic_link;
|
||||
const googleLogin = enableGoogleLogin && clientId;
|
||||
const hasDivider =
|
||||
enableMagicLink || googleLogin || enableMetamaskLogin || oidc.length > 0 || enableGithubLogin;
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="form">
|
||||
@@ -1,5 +1,6 @@
|
||||
import styled from "styled-components";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
|
||||
export const StyledSocialButton = styled(Button)`
|
||||
width: 100%;
|
||||
margin-bottom: 16px;
|
||||
@@ -15,6 +16,7 @@ export const StyledSocialButton = styled(Button)`
|
||||
height: 24px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -1,4 +1,5 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -7,7 +8,7 @@ const StyledWrapper = styled.div`
|
||||
.form {
|
||||
padding: 36px 40px 32px 40px;
|
||||
/* border: 1px solid #eee; */
|
||||
box-shadow: 0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06);
|
||||
box-shadow: 0 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06);
|
||||
border-radius: 12px;
|
||||
.tips {
|
||||
display: flex;
|
||||
@@ -41,7 +42,7 @@ const StyledWrapper = styled.div`
|
||||
width: 360px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #d0d5dd;
|
||||
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
|
||||
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
|
||||
border-radius: 8px;
|
||||
padding: 10px 14px;
|
||||
font-weight: normal;
|
||||
@@ -61,7 +62,7 @@ const StyledWrapper = styled.div`
|
||||
padding: 10px;
|
||||
background: #1fe1f9;
|
||||
border: 1px solid #1fe1f9;
|
||||
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
|
||||
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
|
||||
border-radius: 8px;
|
||||
&.google {
|
||||
color: #344054;
|
||||
@@ -1,5 +1,5 @@
|
||||
// import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Styled = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -22,6 +22,7 @@ const Styled = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function EmailNextTip() {
|
||||
return (
|
||||
<Styled>
|
||||
@@ -1,5 +1,6 @@
|
||||
// import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Styled = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -22,6 +23,7 @@ const Styled = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function ExpiredTip() {
|
||||
return (
|
||||
<Styled>
|
||||
@@ -1,12 +1,9 @@
|
||||
import { useState, useEffect } from "react";
|
||||
// import { useDispatch } from "react-redux";
|
||||
import { useState, useEffect, FormEvent, ChangeEvent } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
// import toast from "react-hot-toast";
|
||||
import Input from "../../common/component/styled/Input";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
import { useSendRegMagicLinkMutation } from "../../app/services/auth";
|
||||
import EmailNextTip from "./EmailNextStepTip";
|
||||
// import { useSendLoginMagicLinkMutation } from "../../app/services/auth";
|
||||
|
||||
export default function Reg() {
|
||||
const [sendRegMagicLink, { isLoading, data, isSuccess }] = useSendRegMagicLinkMutation();
|
||||
@@ -17,6 +14,7 @@ export default function Reg() {
|
||||
password: "",
|
||||
confirmPassword: ""
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const query = new URLSearchParams(location.search);
|
||||
// const githubCode = query.get("gcode");
|
||||
@@ -25,6 +23,7 @@ export default function Reg() {
|
||||
setMagicToken(token);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess && data) {
|
||||
const { new_magic_token, mail_is_sent } = data;
|
||||
@@ -35,7 +34,7 @@ export default function Reg() {
|
||||
}
|
||||
}, [isSuccess, data]);
|
||||
|
||||
const handleReg = (evt) => {
|
||||
const handleReg = (evt: FormEvent<HTMLFormElement>) => {
|
||||
evt.preventDefault();
|
||||
const { email, password } = input;
|
||||
sendRegMagicLink({
|
||||
@@ -46,7 +45,7 @@ export default function Reg() {
|
||||
// sendMagicLink(email);
|
||||
};
|
||||
|
||||
const handleInput = (evt) => {
|
||||
const handleInput = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
const { type } = evt.target.dataset;
|
||||
const { value } = evt.target;
|
||||
// console.log(type, value);
|
||||
@@ -55,6 +54,7 @@ export default function Reg() {
|
||||
return { ...prev };
|
||||
});
|
||||
};
|
||||
|
||||
const { email, password } = input;
|
||||
if (data?.mail_is_sent) return <EmailNextTip />;
|
||||
return (
|
||||
@@ -20,6 +20,7 @@ const StyledSignInLink = styled.p`
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function SignInLink() {
|
||||
const navigate = useNavigate();
|
||||
const handleSignIn = () => {
|
||||
@@ -1,4 +1,5 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -7,7 +8,7 @@ const StyledWrapper = styled.div`
|
||||
.form {
|
||||
max-width: 440px;
|
||||
padding: 36px 40px 32px 40px;
|
||||
box-shadow: 0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06);
|
||||
box-shadow: 0 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06);
|
||||
border-radius: 12px;
|
||||
.tips {
|
||||
display: flex;
|
||||
@@ -1,16 +1,15 @@
|
||||
// import React from 'react'
|
||||
// import { useSelector } from "react-redux";
|
||||
import styled from "styled-components";
|
||||
import CheckSign from "../../../assets/icons/check.sign.svg";
|
||||
import ChannelIcon from "../../../common/component/ChannelIcon";
|
||||
import Search from "../Search";
|
||||
import useFilteredChannels from "../../../common/hook/useFilteredChannels";
|
||||
|
||||
const Styled = styled.div`
|
||||
padding: 0 4px 4px 4px;
|
||||
background: #ffffff;
|
||||
max-height: 400px;
|
||||
overflow: auto;
|
||||
box-shadow: 0px 24px 48px -12px rgba(16, 24, 40, 0.18);
|
||||
box-shadow: 0 24px 48px -12px rgba(16, 24, 40, 0.18);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -52,11 +51,13 @@ const Styled = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Channel({ select = "", updateFilter }) {
|
||||
const { input, updateInput, channels } = useFilteredChannels();
|
||||
const handleClick = (gid) => {
|
||||
updateFilter({ channel: gid });
|
||||
};
|
||||
|
||||
return (
|
||||
<Styled>
|
||||
<div className="search">
|
||||
@@ -1,5 +1,3 @@
|
||||
// import React from 'react'
|
||||
// import { useSelector } from "react-redux";
|
||||
import styled from "styled-components";
|
||||
import CheckSign from "../../../assets/icons/check.sign.svg";
|
||||
|
||||
@@ -39,6 +37,7 @@ const Styled = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const Dates = {
|
||||
today: {
|
||||
title: "Today",
|
||||
@@ -57,6 +56,7 @@ export const Dates = {
|
||||
title: "Last 12 months"
|
||||
}
|
||||
};
|
||||
|
||||
export default function Date({ select = "", updateFilter }) {
|
||||
// const { input, updateInput, contacts } = useFilteredUsers();
|
||||
// const contacts=useSelector(store=>store.contacts);
|
||||
@@ -66,6 +66,7 @@ export default function Date({ select = "", updateFilter }) {
|
||||
const handleClick = (dur) => {
|
||||
updateFilter({ date: dur });
|
||||
};
|
||||
|
||||
return (
|
||||
<Styled>
|
||||
<ul className="list">
|
||||
@@ -1,17 +1,15 @@
|
||||
// import React from 'react'
|
||||
// import { useSelector } from "react-redux";
|
||||
import styled from "styled-components";
|
||||
import Search from "../Search";
|
||||
import CheckSign from "../../../assets/icons/check.sign.svg";
|
||||
|
||||
import Contact from "../../../common/component/Contact";
|
||||
import useFilteredUsers from "../../../common/hook/useFilteredUsers";
|
||||
|
||||
const Styled = styled.div`
|
||||
padding: 0 4px 4px 4px;
|
||||
background: #ffffff;
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
box-shadow: 0px 24px 48px -12px rgba(16, 24, 40, 0.18);
|
||||
box-shadow: 0 24px 48px -12px rgba(16, 24, 40, 0.18);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -49,6 +47,7 @@ const Styled = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function From({ select = "", updateFilter }) {
|
||||
const { input, updateInput, contacts } = useFilteredUsers();
|
||||
// const contacts=useSelector(store=>store.contacts);
|
||||
@@ -58,6 +57,7 @@ export default function From({ select = "", updateFilter }) {
|
||||
const handleClick = (uid) => {
|
||||
updateFilter({ from: uid });
|
||||
};
|
||||
|
||||
return (
|
||||
<Styled>
|
||||
<div className="search">
|
||||
@@ -1,5 +1,3 @@
|
||||
// import React from 'react'
|
||||
// import { useSelector } from "react-redux";
|
||||
import styled from "styled-components";
|
||||
import IconPdf from "../../../assets/icons/file.pdf.svg";
|
||||
import IconAudio from "../../../assets/icons/file.audio.svg";
|
||||
@@ -16,7 +14,7 @@ const Styled = styled.div`
|
||||
min-width: 200px;
|
||||
/* max-height: 230px; */
|
||||
overflow: auto;
|
||||
box-shadow: 0px 24px 48px -12px rgba(16, 24, 40, 0.18);
|
||||
box-shadow: 0 24px 48px -12px rgba(16, 24, 40, 0.18);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -33,7 +31,6 @@ const Styled = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #616161;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import { useSelector } from "react-redux";
|
||||
import Avatar from "../../../common/component/Avatar";
|
||||
import FilterDate, { Dates } from "./Date";
|
||||
import FilterFrom from "./From";
|
||||
import FilterChannel from "./Channel";
|
||||
import FilterType, { FileTypes } from "./Type";
|
||||
import ArrowDown from "../../../assets/icons/arrow.down.svg";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
|
||||
const Styled = styled.div`
|
||||
/* padding: 20px 0; */
|
||||
display: flex;
|
||||
@@ -20,7 +21,7 @@ const Styled = styled.div`
|
||||
gap: 8px;
|
||||
border: 1px solid #d0d5dd;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
|
||||
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
|
||||
border-radius: var(--br);
|
||||
padding: 7px 12px;
|
||||
font-weight: 500;
|
||||
@@ -49,24 +50,25 @@ export default function Filter({ filter, updateFilter }) {
|
||||
from: false,
|
||||
type: false
|
||||
});
|
||||
|
||||
const toggleFilterVisible = (obj) => {
|
||||
setFiltersVisible((prev) => {
|
||||
return { ...prev, ...obj };
|
||||
});
|
||||
};
|
||||
|
||||
const handleUpdateFilter = (data) => {
|
||||
updateFilter(data);
|
||||
let _key = Object.keys(data)[0];
|
||||
let tmp = {
|
||||
[_key]: false
|
||||
};
|
||||
console.log("wtffff", tmp);
|
||||
toggleFilterVisible(tmp);
|
||||
};
|
||||
const { contactMap, channelMap } = useSelector((store) => {
|
||||
const { contactMap, channelMap } = useAppSelector((store) => {
|
||||
return { contactMap: store.contacts.byId, channelMap: store.channels.byId };
|
||||
});
|
||||
console.log("maps", contactMap, filter);
|
||||
|
||||
const { from, channel, type, date } = filter;
|
||||
const {
|
||||
channel: channelVisible,
|
||||
@@ -74,6 +76,7 @@ export default function Filter({ filter, updateFilter }) {
|
||||
type: typeVisible,
|
||||
from: fromVisible
|
||||
} = filtersVisible;
|
||||
|
||||
return (
|
||||
<Styled>
|
||||
<Tippy
|
||||
@@ -1,10 +1,10 @@
|
||||
// import React from "react";
|
||||
import styled from "styled-components";
|
||||
import iconSearch from "../../assets/icons/search.svg?url";
|
||||
|
||||
const Styled = styled.div`
|
||||
width: 100%;
|
||||
padding: 6px 16px;
|
||||
box-shadow: 0px 1px 0px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
|
||||
&.embed {
|
||||
padding: 6px 8px;
|
||||
box-shadow: none;
|
||||
@@ -23,12 +23,14 @@ const Styled = styled.div`
|
||||
background-position: 8px center;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Search({ value = "", updateSearchValue = null, embed = false }) {
|
||||
const handleChange = (evt) => {
|
||||
if (updateSearchValue) {
|
||||
updateSearchValue(evt.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Styled className={embed ? "embed" : ""}>
|
||||
<input value={value} onChange={handleChange} className="search" placeholder="Search..." />
|
||||
@@ -1,14 +1,15 @@
|
||||
// import React from 'react'
|
||||
import { MouseEvent } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { updateFileListView } from "../../app/slices/ui";
|
||||
import { Views } from "../../app/config";
|
||||
import IconList from "../../assets/icons/file.list.svg";
|
||||
import IconGrid from "../../assets/icons/file.grid.svg";
|
||||
|
||||
const Styled = styled.ul`
|
||||
display: flex;
|
||||
border: 1px solid #d0d5dd;
|
||||
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
|
||||
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
|
||||
border-radius: 8px;
|
||||
box-sizing: border-box;
|
||||
|
||||
@@ -23,7 +24,7 @@ const Styled = styled.ul`
|
||||
&.item .item,
|
||||
&.grid .grid {
|
||||
border: 1px solid #52edff;
|
||||
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
|
||||
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
|
||||
border-radius: 8px;
|
||||
svg {
|
||||
transition: all 0.2s ease;
|
||||
@@ -36,11 +37,12 @@ const Styled = styled.ul`
|
||||
|
||||
export default function View({ view = Views.item }) {
|
||||
const dispatch = useDispatch();
|
||||
const handleChangeView = (evt) => {
|
||||
const handleChangeView = (evt: MouseEvent<HTMLLIElement>) => {
|
||||
const { view: clickView } = evt.currentTarget.dataset;
|
||||
if (clickView == view) return;
|
||||
dispatch(updateFileListView(view == Views.item ? Views.grid : Views.item));
|
||||
};
|
||||
|
||||
return (
|
||||
<Styled className={view}>
|
||||
<li className="view item" data-view={Views.item} onClick={handleChangeView}>
|
||||
@@ -1,15 +1,14 @@
|
||||
import { useState, useEffect, useRef, memo } from "react";
|
||||
import Styled from "./styled";
|
||||
import { useSelector } from "react-redux";
|
||||
import Masonry from "masonry-layout";
|
||||
// import waterfall from "waterfall.js/src/waterfall";
|
||||
import Styled from "./styled";
|
||||
import { Views } from "../../app/config";
|
||||
import View from "./View";
|
||||
import Search from "./Search";
|
||||
import Filter from "./Filter";
|
||||
import FileBox from "../../common/component/FileBox";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
|
||||
const checkFilter = (data, filter, channelMessage) => {
|
||||
console.log("filter data", data);
|
||||
let selected = true;
|
||||
const { mid, file_type, created_at, from_uid, properties } = data;
|
||||
const {
|
||||
@@ -35,11 +34,12 @@ const checkFilter = (data, filter, channelMessage) => {
|
||||
}
|
||||
return selected;
|
||||
};
|
||||
|
||||
let msnry = null;
|
||||
function ResourceManagement({ fileMessages }) {
|
||||
const listContainerRef = useRef(null);
|
||||
const [filter, setFilter] = useState({});
|
||||
const { message, view, channelMessage } = useSelector((store) => {
|
||||
const { message, view, channelMessage } = useAppSelector((store) => {
|
||||
return {
|
||||
message: store.message,
|
||||
channelMessage: store.channelMessage,
|
||||
@@ -52,11 +52,13 @@ function ResourceManagement({ fileMessages }) {
|
||||
return { ...prev, ...data };
|
||||
});
|
||||
};
|
||||
|
||||
const handleUpdateSearch = (val) => {
|
||||
setFilter((prev) => {
|
||||
return { ...prev, name: val };
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (view == Views.grid && listContainerRef) {
|
||||
const container = listContainerRef.current;
|
||||
@@ -84,7 +86,6 @@ function ResourceManagement({ fileMessages }) {
|
||||
// }
|
||||
}, [view, filter]);
|
||||
|
||||
console.log("files", fileMessages);
|
||||
return (
|
||||
<Styled>
|
||||
<Search value={filter.name} updateSearchValue={handleUpdateSearch} />
|
||||
@@ -119,7 +120,9 @@ function ResourceManagement({ fileMessages }) {
|
||||
</Styled>
|
||||
);
|
||||
}
|
||||
|
||||
const equals = (a, b) => a.length === b.length && a.every((v, i) => v == b[i]);
|
||||
|
||||
export default memo(ResourceManagement, (prevs, nexts) => {
|
||||
return equals(prevs.fileMessages, nexts.fileMessages);
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const Styled = styled.div`
|
||||
height: 100vh;
|
||||
overflow-y: scroll;
|
||||
@@ -1,6 +1,12 @@
|
||||
// import React from "react";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
export default function SentTip({ email, reset }) {
|
||||
import { FC, MouseEvent } from "react";
|
||||
|
||||
interface Props {
|
||||
email: string;
|
||||
reset?: (e: MouseEvent<HTMLButtonElement>) => void;
|
||||
}
|
||||
|
||||
const SentTip: FC<Props> = ({ email, reset }) => {
|
||||
return (
|
||||
<div className="tips">
|
||||
<h2 className="title">Check your email</h2>
|
||||
@@ -12,4 +18,6 @@ export default function SentTip({ email, reset }) {
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default SentTip;
|
||||
@@ -1,6 +1,4 @@
|
||||
/* eslint-disable no-undef */
|
||||
import { useState, useEffect } from "react";
|
||||
// import { useDispatch } from "react-redux";
|
||||
import { useState, useEffect, ChangeEvent, FormEvent } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import toast from "react-hot-toast";
|
||||
import BASE_URL from "../../app/config";
|
||||
@@ -13,10 +11,9 @@ import SentTip from "./SentTip";
|
||||
export default function SendMagicLinkPage() {
|
||||
const [sent, setSent] = useState(false);
|
||||
const [sendMagicLink, { isSuccess, isLoading, error }] = useSendLoginMagicLinkMutation();
|
||||
|
||||
const navigateTo = useNavigate();
|
||||
// const dispatch = useDispatch();
|
||||
const [email, setEmail] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
toast.success("Send Email Successfully!");
|
||||
@@ -48,20 +45,23 @@ export default function SendMagicLinkPage() {
|
||||
const handlePwdPath = () => {
|
||||
navigateTo("/login");
|
||||
};
|
||||
const handleLogin = (evt) => {
|
||||
|
||||
const handleLogin = (evt: FormEvent<HTMLFormElement>) => {
|
||||
evt.preventDefault();
|
||||
sendMagicLink(email);
|
||||
};
|
||||
|
||||
const handleInput = (evt) => {
|
||||
const handleInput = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
const { value } = evt.target;
|
||||
console.log(value);
|
||||
setEmail(value);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setEmail("");
|
||||
setSent(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="form">
|
||||
@@ -1,6 +1,15 @@
|
||||
import { useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import toast from "react-hot-toast";
|
||||
import { hideAll } from "tippy.js";
|
||||
import Input from "../../common/component/styled/Input";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
import {
|
||||
useGetThirdPartySecretQuery,
|
||||
useUpdateThirdPartySecretMutation
|
||||
} from "../../app/services/server";
|
||||
|
||||
const StyledConfirm = styled.div`
|
||||
padding: 12px;
|
||||
border-radius: 10px;
|
||||
@@ -48,19 +57,12 @@ const Styled = styled.div`
|
||||
line-height: 1.5;
|
||||
}
|
||||
`;
|
||||
import Input from "../../common/component/styled/Input";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
import {
|
||||
useGetThirdPartySecretQuery,
|
||||
useUpdateThirdPartySecretMutation
|
||||
} from "../../app/services/server";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export default function APIConfig() {
|
||||
const { data } = useGetThirdPartySecretQuery();
|
||||
const [updateSecret, { data: updatedSecret, isSuccess, isLoading }] =
|
||||
useUpdateThirdPartySecretMutation();
|
||||
console.log("secret", data);
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
hideAll();
|
||||
@@ -1,11 +1,12 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState, MouseEvent } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useSelector } from "react-redux";
|
||||
import toast from "react-hot-toast";
|
||||
import { useUpdateAvatarMutation } from "../../app/services/contact";
|
||||
import AvatarUploader from "../../common/component/AvatarUploader";
|
||||
import ProfileBasicEditModal from "./ProfileBasicEditModal";
|
||||
import UpdatePasswordModal from "./UpdatePasswordModal";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -109,28 +110,34 @@ const EditModalInfo = {
|
||||
intro: "Enter a new email."
|
||||
}
|
||||
};
|
||||
|
||||
export default function MyAccount() {
|
||||
const [passwordModal, setPasswordModal] = useState(false);
|
||||
const [editModal, setEditModal] = useState(null);
|
||||
const [uploadAvatar, { isSuccess: uploadSuccess }] = useUpdateAvatarMutation();
|
||||
const loginUser = useSelector((store) => {
|
||||
const loginUser = useAppSelector((store) => {
|
||||
return store.contacts.byId[store.authData.uid];
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (uploadSuccess) {
|
||||
toast.success("update avatar successfully!");
|
||||
}
|
||||
}, [uploadSuccess]);
|
||||
const handleBasicEdit = (evt) => {
|
||||
|
||||
const handleBasicEdit = (evt: MouseEvent<HTMLButtonElement>) => {
|
||||
const { edit } = evt.target.dataset;
|
||||
setEditModal(edit);
|
||||
};
|
||||
|
||||
const closeBasicEditModal = () => {
|
||||
setEditModal(null);
|
||||
};
|
||||
|
||||
const togglePasswordModal = () => {
|
||||
setPasswordModal((prev) => !prev);
|
||||
};
|
||||
|
||||
if (!loginUser) return null;
|
||||
const { uid, avatar, name, email } = loginUser;
|
||||
return (
|
||||
@@ -1,12 +1,13 @@
|
||||
// import React from "react";
|
||||
import styled from "styled-components";
|
||||
import useNotification from "../../common/hook/useNotification";
|
||||
import StyledToggle from "../../common/component/styled/Toggle";
|
||||
import Label from "../../common/component/styled/Label";
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
export default function Notifications() {
|
||||
const { status, enableNotification } = useNotification();
|
||||
const handleEnableNotify = () => {
|
||||
@@ -14,6 +15,7 @@ export default function Notifications() {
|
||||
enableNotification();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<Label>Notification Setting:</Label>
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useSelector } from "react-redux";
|
||||
import toast from "react-hot-toast";
|
||||
import {
|
||||
useGetServerQuery,
|
||||
useUpdateServerMutation,
|
||||
@@ -13,8 +13,8 @@ import Input from "../../common/component/styled/Input";
|
||||
import Label from "../../common/component/styled/Label";
|
||||
import Textarea from "../../common/component/styled/Textarea";
|
||||
import SaveTip from "../../common/component/SaveTip";
|
||||
import toast from "react-hot-toast";
|
||||
import StyledRadio from "../../common/component/styled/Radio";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
position: relative;
|
||||
@@ -55,7 +55,7 @@ const StyledWrapper = styled.div`
|
||||
background: #ecfeff;
|
||||
border: 1px solid #ecfeff;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
|
||||
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
@@ -97,8 +97,9 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Overview() {
|
||||
const loginUser = useSelector((store) => {
|
||||
const loginUser = useAppSelector((store) => {
|
||||
return store.contacts.byId[store.authData.uid];
|
||||
});
|
||||
const [changed, setChanged] = useState(false);
|
||||
+4
-2
@@ -1,10 +1,13 @@
|
||||
// import React from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import toast from "react-hot-toast";
|
||||
import Input from "../../common/component/styled/Input";
|
||||
import { useUpdateInfoMutation } from "../../app/services/contact";
|
||||
import StyledModal from "../../common/component/styled/Modal";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
import Modal from "../../common/component/Modal";
|
||||
|
||||
const StyledEdit = styled(StyledModal)`
|
||||
.input {
|
||||
margin: 48px 0;
|
||||
@@ -20,8 +23,7 @@ const StyledEdit = styled(StyledModal)`
|
||||
}
|
||||
}
|
||||
`;
|
||||
import Modal from "../../common/component/Modal";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export default function ProfileBasicEditModal({
|
||||
label = "Username",
|
||||
valueKey = "name",
|
||||
+14
-7
@@ -1,10 +1,12 @@
|
||||
// import React from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ChangeEvent, useEffect, useState, FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import toast from "react-hot-toast";
|
||||
import Input from "../../common/component/styled/Input";
|
||||
import { useUpdatePasswordMutation, useGetCredentialsQuery } from "../../app/services/auth";
|
||||
import StyledModal from "../../common/component/styled/Modal";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
import Modal from "../../common/component/Modal";
|
||||
|
||||
const StyledEdit = styled(StyledModal)`
|
||||
.input {
|
||||
margin: 16px 0;
|
||||
@@ -20,9 +22,12 @@ const StyledEdit = styled(StyledModal)`
|
||||
}
|
||||
}
|
||||
`;
|
||||
import Modal from "../../common/component/Modal";
|
||||
import toast from "react-hot-toast";
|
||||
export default function ProfileBasicEditModal({ closeModal }) {
|
||||
|
||||
interface Props {
|
||||
closeModal: () => void;
|
||||
}
|
||||
|
||||
const ProfileBasicEditModal: FC<Props> = ({ closeModal }) => {
|
||||
const { data } = useGetCredentialsQuery();
|
||||
const [input, setInput] = useState({
|
||||
current: "",
|
||||
@@ -31,7 +36,7 @@ export default function ProfileBasicEditModal({ closeModal }) {
|
||||
});
|
||||
// const dispatch = useDispatch();
|
||||
const [updatePassword, { isLoading, isSuccess }] = useUpdatePasswordMutation();
|
||||
const handleChange = (evt) => {
|
||||
const handleChange = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
const { type } = evt.target.dataset;
|
||||
setInput((prev) => {
|
||||
return { ...prev, [type]: evt.target.value };
|
||||
@@ -116,4 +121,6 @@ export default function ProfileBasicEditModal({ closeModal }) {
|
||||
</StyledEdit>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default ProfileBasicEditModal;
|
||||
@@ -1,4 +1,3 @@
|
||||
// import { useState, useEffect } from "react";
|
||||
import StyledContainer from "./StyledContainer";
|
||||
import Input from "../../../common/component/styled/Input";
|
||||
import Textarea from "../../../common/component/styled/Textarea";
|
||||
@@ -6,12 +5,14 @@ import Label from "../../../common/component/styled/Label";
|
||||
import Toggle from "../../../common/component/styled/Toggle";
|
||||
import SaveTip from "../../../common/component/SaveTip";
|
||||
import useConfig from "../../../common/hook/useConfig";
|
||||
|
||||
export default function ConfigAgora() {
|
||||
const { changed, reset, values, setValues, toggleEnable, updateConfig } = useConfig("agora");
|
||||
const handleUpdate = () => {
|
||||
// const { token_url, description } = values;
|
||||
updateConfig(values);
|
||||
};
|
||||
|
||||
const handleChange = (evt) => {
|
||||
const newValue = evt.target.value;
|
||||
const { type } = evt.target.dataset;
|
||||
@@ -29,6 +30,7 @@ export default function ConfigAgora() {
|
||||
rtm_secret,
|
||||
enabled = false
|
||||
} = values ?? {};
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<div className="inputs">
|
||||
+5
-2
@@ -1,4 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { ChangeEvent, useState } from "react";
|
||||
import Select from "../../../../common/component/styled/Select";
|
||||
import Button from "../../../../common/component/styled/Button";
|
||||
import Input from "../../../../common/component/styled/Input";
|
||||
@@ -11,13 +11,16 @@ import IconMinus from "../../../../assets/icons/minus.circle.svg";
|
||||
export default function IssuerList({ issuers = [], onChange }) {
|
||||
const [select, setSelect] = useState({});
|
||||
const [newDomain, setNewDomain] = useState("");
|
||||
const handleNewDomain = (evt) => {
|
||||
|
||||
const handleNewDomain = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
setNewDomain(evt.target.value);
|
||||
};
|
||||
|
||||
const disableBtn =
|
||||
(!newDomain && !select?.value) ||
|
||||
!select?.title ||
|
||||
issuers.some((issuer) => issuer.domain === newDomain);
|
||||
|
||||
return (
|
||||
<Styled>
|
||||
<ul className="issuers">
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const Styled = styled.div`
|
||||
padding: 16px 0;
|
||||
width: 100%;
|
||||
@@ -1,4 +1,3 @@
|
||||
// import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import StyledContainer from "./StyledContainer";
|
||||
import Toggle from "../../../common/component/styled/Toggle";
|
||||
@@ -10,6 +9,7 @@ import Tooltip from "./Tooltip";
|
||||
import IssuerList from "./IssuerList";
|
||||
import useGoogleAuthConfig from "../../../common/hook/useGoogleAuthConfig";
|
||||
import useGithubAuthConfig from "../../../common/hook/useGithubAuthConfig";
|
||||
|
||||
export default function Logins() {
|
||||
const {
|
||||
changed: clientIdChanged,
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
position: relative;
|
||||
width: 512px;
|
||||
@@ -1,10 +1,11 @@
|
||||
import { useState } from "react";
|
||||
// import { useDispatch } from "react-redux";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import StyledSettingContainer from "../../common/component/StyledSettingContainer";
|
||||
import useNavs from "./navs";
|
||||
import LogoutConfirmModal from "./LogoutConfirmModal";
|
||||
let from = null;
|
||||
|
||||
let from: string | null = null;
|
||||
|
||||
export default function Setting() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const navs = useNavs();
|
||||
@@ -26,6 +27,7 @@ export default function Setting() {
|
||||
setLogoutConfirm((prev) => !prev);
|
||||
};
|
||||
const currNav = flatenNavs.find((n) => n.name == navKey) || flatenNavs[0];
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledSettingContainer
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useSelector } from "react-redux";
|
||||
import MyAccount from "./MyAccount";
|
||||
import Overview from "./Overview";
|
||||
import Logins from "./config/Logins";
|
||||
import ConfigFirebase from "./config/Firebase";
|
||||
import ConfigSMTP from "./config/SMTP";
|
||||
// import Notifications from "./Notifications";
|
||||
import APIConfig from "./APIConfig";
|
||||
import ManageMembers from "../../common/component/ManageMembers";
|
||||
import FAQ from "../../common/component/FAQ";
|
||||
import ConfigAgora from "./config/Agora";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
|
||||
const navs = [
|
||||
{
|
||||
title: "General",
|
||||
@@ -93,18 +93,18 @@ const navs = [
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const useNavs = () => {
|
||||
const loginUser = useSelector((store) => {
|
||||
const loginUser = useAppSelector((store) => {
|
||||
return store.contacts.byId[store.authData.uid];
|
||||
});
|
||||
const Navs = navs.filter((nav) => {
|
||||
return navs.filter((nav) => {
|
||||
if (loginUser?.is_admin) {
|
||||
return true;
|
||||
} else {
|
||||
return !nav.admin;
|
||||
}
|
||||
});
|
||||
return Navs;
|
||||
};
|
||||
|
||||
export default useNavs;
|
||||
+12
-4
@@ -1,5 +1,4 @@
|
||||
// import React from "react";
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, FC } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Modal from "../../common/component/Modal";
|
||||
@@ -7,13 +6,19 @@ import { useLazyRemoveChannelQuery } from "../../app/services/channel";
|
||||
import StyledModal from "../../common/component/styled/Modal";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
|
||||
export default function DeleteConfirmModal({ id, closeModal }) {
|
||||
interface Props {
|
||||
id?: number;
|
||||
closeModal: () => void;
|
||||
}
|
||||
|
||||
const DeleteConfirmModal: FC<Props> = ({ id, closeModal }) => {
|
||||
const navigateTo = useNavigate();
|
||||
// const pathMatched = useMatch(`/chat/channel/${id}`);
|
||||
const [deleteChannel, { isLoading, isSuccess }] = useLazyRemoveChannelQuery();
|
||||
const handleDelete = () => {
|
||||
deleteChannel(id);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
toast.success("delete channel successfully!");
|
||||
@@ -21,6 +26,7 @@ export default function DeleteConfirmModal({ id, closeModal }) {
|
||||
navigateTo("/chat");
|
||||
}
|
||||
}, [isSuccess]);
|
||||
|
||||
if (!id) return null;
|
||||
return (
|
||||
<Modal id="modal-modal">
|
||||
@@ -42,4 +48,6 @@ export default function DeleteConfirmModal({ id, closeModal }) {
|
||||
></StyledModal>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default DeleteConfirmModal;
|
||||
@@ -12,7 +12,8 @@ import Label from "../../common/component/styled/Label";
|
||||
import Textarea from "../../common/component/styled/Textarea";
|
||||
import SaveTip from "../../common/component/SaveTip";
|
||||
import channelIcon from "../../assets/icons/channel.svg?url";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
position: relative;
|
||||
width: 512px;
|
||||
@@ -43,7 +44,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
`;
|
||||
export default function Overview({ id = 0 }) {
|
||||
const { loginUser, channel } = useSelector((store) => {
|
||||
const { loginUser, channel } = useAppSelector((store) => {
|
||||
return {
|
||||
loginUser: store.contacts.byId[store.authData.uid],
|
||||
channel: store.channels.byId[id]
|
||||
@@ -58,6 +59,7 @@ export default function Overview({ id = 0 }) {
|
||||
const { name, description } = values;
|
||||
updateChannel({ id, name, description });
|
||||
};
|
||||
|
||||
const handleChange = (evt) => {
|
||||
const newValue = evt.target.value;
|
||||
const { type } = evt.target.dataset;
|
||||
@@ -65,18 +67,22 @@ export default function Overview({ id = 0 }) {
|
||||
return { ...prev, [type]: newValue };
|
||||
});
|
||||
};
|
||||
|
||||
const updateIcon = (image) => {
|
||||
updateChannelIcon({ gid: id, image });
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
console.log("reset", data);
|
||||
setValues(data);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setValues(data);
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data && values) {
|
||||
const { name, description } = values;
|
||||
@@ -99,7 +105,7 @@ export default function Overview({ id = 0 }) {
|
||||
if (!values || !id) return null;
|
||||
const { name, description } = values;
|
||||
const readOnly = !loginUser?.is_admin && channel?.owner != loginUser?.uid;
|
||||
console.log("channel icon", channel);
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<AvatarUploader type="channel" url={channel?.icon} name={name} uploadImage={updateIcon} />
|
||||
@@ -1,14 +1,16 @@
|
||||
import { useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useParams, useNavigate, useSearchParams } from "react-router-dom";
|
||||
import LeaveChannel from "../../common/component/LeaveChannel";
|
||||
import StyledSettingContainer from "../../common/component/StyledSettingContainer";
|
||||
import DeleteConfirmModal from "./DeleteConfirmModal";
|
||||
import useNavs from "./navs";
|
||||
let from = null;
|
||||
import { useAppSelector } from "../../app/store";
|
||||
|
||||
let from: string | null = null;
|
||||
|
||||
export default function ChannelSetting() {
|
||||
const { cid } = useParams();
|
||||
const { isAdmin, loginUid, channel } = useSelector((store) => {
|
||||
const { isAdmin, loginUid, channel } = useAppSelector((store) => {
|
||||
return {
|
||||
loginUid: store.authData.uid,
|
||||
isAdmin: store.contacts.byId[store.authData.uid]?.is_admin,
|
||||
Reference in New Issue
Block a user