Toast message (#155)
This commit is contained in:
parent
c06c0253bc
commit
7d90ad9ae4
|
@ -12,6 +12,7 @@ import { Members } from "./Members/Members";
|
||||||
import { CommunityModal } from "./Modals/CommunityModal";
|
import { CommunityModal } from "./Modals/CommunityModal";
|
||||||
import { EditModal } from "./Modals/EditModal";
|
import { EditModal } from "./Modals/EditModal";
|
||||||
import { ProfileModal } from "./Modals/ProfileModal";
|
import { ProfileModal } from "./Modals/ProfileModal";
|
||||||
|
import { ToastMessageList } from "./ToastMessages/ToastMessageList";
|
||||||
|
|
||||||
function Modals() {
|
function Modals() {
|
||||||
return (
|
return (
|
||||||
|
@ -45,6 +46,7 @@ export function Chat() {
|
||||||
{showMembers && !narrow && state === ChatState.ChatBody && <Members />}
|
{showMembers && !narrow && state === ChatState.ChatBody && <Members />}
|
||||||
{state === ChatState.ChatCreation && <ChatCreation />}
|
{state === ChatState.ChatCreation && <ChatCreation />}
|
||||||
<Modals />
|
<Modals />
|
||||||
|
<ToastMessageList />
|
||||||
</ChatWrapper>
|
</ChatWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -53,6 +55,7 @@ const ChatWrapper = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
position: relative;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ChannelsWrapper = styled.div`
|
const ChannelsWrapper = styled.div`
|
||||||
|
|
|
@ -5,6 +5,7 @@ import styled from "styled-components";
|
||||||
import { useActivities } from "../../contexts/activityProvider";
|
import { useActivities } from "../../contexts/activityProvider";
|
||||||
import { useIdentity } from "../../contexts/identityProvider";
|
import { useIdentity } from "../../contexts/identityProvider";
|
||||||
import { useModal } from "../../contexts/modalProvider";
|
import { useModal } from "../../contexts/modalProvider";
|
||||||
|
import { useToasts } from "../../contexts/toastProvider";
|
||||||
import { useManageContact } from "../../hooks/useManageContact";
|
import { useManageContact } from "../../hooks/useManageContact";
|
||||||
import { copy } from "../../utils";
|
import { copy } from "../../utils";
|
||||||
import { buttonStyles } from "../Buttons/buttonStyle";
|
import { buttonStyles } from "../Buttons/buttonStyle";
|
||||||
|
@ -42,6 +43,8 @@ export const ProfileModal = () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const { setActivities } = useActivities();
|
const { setActivities } = useActivities();
|
||||||
|
const { setToasts } = useToasts();
|
||||||
|
const { setModal } = useModal(ProfileModalName);
|
||||||
|
|
||||||
const identity = useIdentity();
|
const identity = useIdentity();
|
||||||
const isUser = useMemo(
|
const isUser = useMemo(
|
||||||
|
@ -160,6 +163,7 @@ export const ProfileModal = () => {
|
||||||
maxLength={280}
|
maxLength={280}
|
||||||
onInput={(e) => setRequest(e.currentTarget.value)}
|
onInput={(e) => setRequest(e.currentTarget.value)}
|
||||||
required
|
required
|
||||||
|
autoFocus
|
||||||
/>
|
/>
|
||||||
</RequestSection>
|
</RequestSection>
|
||||||
)}
|
)}
|
||||||
|
@ -200,8 +204,16 @@ export const ProfileModal = () => {
|
||||||
requestType: "outcome",
|
requestType: "outcome",
|
||||||
status: "sent",
|
status: "sent",
|
||||||
},
|
},
|
||||||
|
]),
|
||||||
|
setToasts((prev) => [
|
||||||
|
...prev,
|
||||||
|
{
|
||||||
|
id: id + request,
|
||||||
|
type: "request",
|
||||||
|
},
|
||||||
]),
|
]),
|
||||||
setRequestCreation(false),
|
setRequestCreation(false),
|
||||||
|
setModal(false),
|
||||||
setRequest("");
|
setRequest("");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { ActivityProvider } from "../contexts/activityProvider";
|
||||||
import { FetchMetadataProvider } from "../contexts/fetchMetadataProvider";
|
import { FetchMetadataProvider } from "../contexts/fetchMetadataProvider";
|
||||||
import { ModalProvider } from "../contexts/modalProvider";
|
import { ModalProvider } from "../contexts/modalProvider";
|
||||||
import { NarrowProvider } from "../contexts/narrowProvider";
|
import { NarrowProvider } from "../contexts/narrowProvider";
|
||||||
|
import { ToastProvider } from "../contexts/toastProvider";
|
||||||
import { Metadata } from "../models/Metadata";
|
import { Metadata } from "../models/Metadata";
|
||||||
import { GlobalStyle } from "../styles/GlobalStyle";
|
import { GlobalStyle } from "../styles/GlobalStyle";
|
||||||
import { Theme } from "../styles/themes";
|
import { Theme } from "../styles/themes";
|
||||||
|
@ -30,11 +31,13 @@ export function ReactChat({
|
||||||
<FetchMetadataProvider fetchMetadata={fetchMetadata}>
|
<FetchMetadataProvider fetchMetadata={fetchMetadata}>
|
||||||
<ModalProvider>
|
<ModalProvider>
|
||||||
<ActivityProvider>
|
<ActivityProvider>
|
||||||
|
<ToastProvider>
|
||||||
<Wrapper ref={ref}>
|
<Wrapper ref={ref}>
|
||||||
<GlobalStyle />
|
<GlobalStyle />
|
||||||
<ChatLoader communityKey={communityKey} />
|
<ChatLoader communityKey={communityKey} />
|
||||||
<div id="modal-root" />
|
<div id="modal-root" />
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
|
</ToastProvider>
|
||||||
</ActivityProvider>
|
</ActivityProvider>
|
||||||
</ModalProvider>
|
</ModalProvider>
|
||||||
</FetchMetadataProvider>
|
</FetchMetadataProvider>
|
||||||
|
|
|
@ -23,8 +23,7 @@ const LoadingBlock = styled.div`
|
||||||
padding: 4px 5px 4px 7px;
|
padding: 4px 5px 4px 7px;
|
||||||
background: ${({ theme }) => theme.bodyBackgroundColor};
|
background: ${({ theme }) => theme.bodyBackgroundColor};
|
||||||
color: ${({ theme }) => theme.primary};
|
color: ${({ theme }) => theme.primary};
|
||||||
box-shadow: 0px 2px 4px rgba(0, 34, 51, 0.16),
|
box-shadow: ${({ theme }) => theme.shadow};
|
||||||
0px 4px 12px rgba(0, 34, 51, 0.08);
|
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
import React from "react";
|
||||||
|
import styled, { keyframes } from "styled-components";
|
||||||
|
|
||||||
|
import { useToasts } from "../../contexts/toastProvider";
|
||||||
|
import { Toast } from "../../models/Toast";
|
||||||
|
import { CheckSvg } from "../Icons/CheckIcon";
|
||||||
|
import { CrossIcon } from "../Icons/CrossIcon";
|
||||||
|
import { textSmallStyles } from "../Text";
|
||||||
|
|
||||||
|
export function AnimationToastMessage() {
|
||||||
|
return keyframes`
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-100%); }
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0); }
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToastMessageProps = {
|
||||||
|
toast: Toast;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ToastMessage({ toast }: ToastMessageProps) {
|
||||||
|
const { setToasts } = useToasts();
|
||||||
|
|
||||||
|
const closeToast = () => {
|
||||||
|
setToasts((prev) => prev.filter((e) => e != toast));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToastWrapper>
|
||||||
|
<ToastBlock>
|
||||||
|
{toast.type !== "verification" && (
|
||||||
|
<CheckWrapper>
|
||||||
|
<CheckSvg width={20} height={20} className="accept" />
|
||||||
|
</CheckWrapper>
|
||||||
|
)}
|
||||||
|
<ToastText>
|
||||||
|
{toast.type === "request"
|
||||||
|
? "Contact Request Sent"
|
||||||
|
: "Verification Request Sent"}
|
||||||
|
</ToastText>
|
||||||
|
</ToastBlock>
|
||||||
|
<CloseButton onClick={closeToast}>
|
||||||
|
<CrossIcon />
|
||||||
|
</CloseButton>
|
||||||
|
</ToastWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ToastWrapper = styled.div`
|
||||||
|
width: 343px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin-top: 8px;
|
||||||
|
background: ${({ theme }) => theme.bodyBackgroundColor};
|
||||||
|
box-shadow: ${({ theme }) => theme.shadow};
|
||||||
|
border-radius: 8px;
|
||||||
|
animation: ${AnimationToastMessage} 2s ease;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ToastBlock = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: ${({ theme }) => theme.primary};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ToastText = styled.p`
|
||||||
|
font-weight: 500;
|
||||||
|
${textSmallStyles};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CheckWrapper = styled.div`
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 12px;
|
||||||
|
background: rgba(78, 188, 96, 0.1);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CloseButton = styled.button`
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
`;
|
|
@ -0,0 +1,31 @@
|
||||||
|
import React, { useMemo } from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
import { useToasts } from "../../contexts/toastProvider";
|
||||||
|
|
||||||
|
import { ToastMessage } from "./ToastMessage";
|
||||||
|
|
||||||
|
export function ToastMessageList() {
|
||||||
|
const { toasts } = useToasts();
|
||||||
|
|
||||||
|
const shownToasts = useMemo(() => toasts, [toasts, toasts.length]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToastsWrapper>
|
||||||
|
{shownToasts.map((toast) => (
|
||||||
|
<ToastMessage key={toast.id} toast={toast} />
|
||||||
|
))}
|
||||||
|
</ToastsWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ToastsWrapper = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
bottom: 56px;
|
||||||
|
right: 16px;
|
||||||
|
width: 343px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 999;
|
||||||
|
`;
|
|
@ -0,0 +1,26 @@
|
||||||
|
import React, { createContext, useContext, useState } from "react";
|
||||||
|
|
||||||
|
import { Toast } from "../models/Toast";
|
||||||
|
|
||||||
|
const ToastContext = createContext<{
|
||||||
|
toasts: Toast[];
|
||||||
|
setToasts: React.Dispatch<React.SetStateAction<Toast[]>>;
|
||||||
|
}>({
|
||||||
|
toasts: [],
|
||||||
|
setToasts: () => undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function useToasts() {
|
||||||
|
return useContext(ToastContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ToastProviderProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ToastProvider({ children }: ToastProviderProps) {
|
||||||
|
const [toasts, setToasts] = useState<Toast[]>([]);
|
||||||
|
return (
|
||||||
|
<ToastContext.Provider value={{ toasts, setToasts }} children={children} />
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
export type Toast = {
|
||||||
|
id: string;
|
||||||
|
type: "request" | "incomeRequest" | "verification";
|
||||||
|
};
|
|
@ -24,6 +24,7 @@ export type Theme = {
|
||||||
mentionHover: string;
|
mentionHover: string;
|
||||||
mentionBg: string;
|
mentionBg: string;
|
||||||
mentionBgHover: string;
|
mentionBgHover: string;
|
||||||
|
shadow: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const lightTheme: Theme = {
|
export const lightTheme: Theme = {
|
||||||
|
@ -52,6 +53,8 @@ export const lightTheme: Theme = {
|
||||||
mentionHover: "#BDE7F2",
|
mentionHover: "#BDE7F2",
|
||||||
mentionBg: "#E5F8FD",
|
mentionBg: "#E5F8FD",
|
||||||
mentionBgHover: "#D4F3FA",
|
mentionBgHover: "#D4F3FA",
|
||||||
|
shadow:
|
||||||
|
"0px 2px 4px rgba(0, 34, 51, 0.16), 0px 4px 12px rgba(0, 34, 51, 0.08)",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const darkTheme: Theme = {
|
export const darkTheme: Theme = {
|
||||||
|
@ -80,6 +83,8 @@ export const darkTheme: Theme = {
|
||||||
mentionHover: "#004E60",
|
mentionHover: "#004E60",
|
||||||
mentionBg: "#004050",
|
mentionBg: "#004050",
|
||||||
mentionBgHover: "#002D39",
|
mentionBgHover: "#002D39",
|
||||||
|
shadow:
|
||||||
|
"0px 2px 4px rgba(0, 34, 51, 0.16), 0px 4px 12px rgba(0, 34, 51, 0.08)",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default { lightTheme, darkTheme };
|
export default { lightTheme, darkTheme };
|
||||||
|
|
Loading…
Reference in New Issue