refactor: add typescript support to project
This commit is contained in:
+1
-1
@@ -34,7 +34,7 @@ function getAdditionalModulePaths(options = {}) {
|
||||
// If the path is equal to the root directory we ignore it here.
|
||||
// We don't want to allow importing from the root directly as source files are
|
||||
// not transpiled outside of `src`. We do allow importing them with the
|
||||
// absolute path (e.g. `src/Components/Button.js`) but we set that up with
|
||||
// absolute path (e.g. `src/Components/Button.tsx`) but we set that up with
|
||||
// an alias.
|
||||
if (path.relative(paths.appPath, baseUrlResolved) === "") {
|
||||
return null;
|
||||
|
||||
@@ -131,6 +131,9 @@
|
||||
"@commitlint/cli": "^17.0.2",
|
||||
"@commitlint/config-conventional": "^17.0.2",
|
||||
"@types/emoji-mart": "^3.0.9",
|
||||
"@types/masonry-layout": "^4.2.5",
|
||||
"@types/react-helmet": "^6.1.5",
|
||||
"@types/react-linkify": "^1.0.1",
|
||||
"@types/styled-components": "^5.1.25",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.1",
|
||||
"@typescript-eslint/parser": "^5.10.1",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import localforage from "localforage";
|
||||
import useRehydrate from "./useRehydrate";
|
||||
import { KEY_UID, CACHE_VERSION } from "../config";
|
||||
|
||||
const tables = [
|
||||
{
|
||||
storeName: "channels",
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import { useDispatch, batch } from "react-redux";
|
||||
import { fullfillReactionMessage } from "../slices/message.reaction";
|
||||
import { fullfillServer } from "../slices/server";
|
||||
@@ -11,6 +10,7 @@ import { fullfillContacts } from "../slices/contacts";
|
||||
import { fullfillFootprint } from "../slices/footprint";
|
||||
import { fullfillFileMessage } from "../slices/message.file";
|
||||
import { fullfillUI } from "../slices/ui";
|
||||
|
||||
const useRehydrate = () => {
|
||||
const [iterated, setIterated] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
@@ -94,4 +94,5 @@ const useRehydrate = () => {
|
||||
};
|
||||
return { rehydrate, rehydrated: iterated };
|
||||
};
|
||||
|
||||
export default useRehydrate;
|
||||
+2
-1
@@ -1,4 +1,4 @@
|
||||
const clearTable = async (table) => {
|
||||
const clearTable = async (table: string) => {
|
||||
const t = window.CACHE[table];
|
||||
if (!t) return;
|
||||
|
||||
@@ -6,4 +6,5 @@ const clearTable = async (table) => {
|
||||
t.removeItem(key);
|
||||
});
|
||||
};
|
||||
|
||||
export default clearTable;
|
||||
+11
-1
@@ -1,5 +1,15 @@
|
||||
import clearTable from "./clear.handler";
|
||||
export default async function handler({ operation, data = {}, payload }) {
|
||||
|
||||
// todo
|
||||
export type BaseOperation = "add" | "remove" | "reset";
|
||||
|
||||
export interface Params {
|
||||
operation: string;
|
||||
data: {};
|
||||
payload: {};
|
||||
}
|
||||
|
||||
export default async function handler({ operation, data = {}, payload }: Params) {
|
||||
const table = window.CACHE["messageChannel"];
|
||||
if (operation.startsWith("reset")) {
|
||||
clearTable("messageChannel");
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
import clearTable from "./clear.handler";
|
||||
|
||||
export default async function handler({ operation, data, payload }) {
|
||||
const table = window.CACHE["channels"];
|
||||
if (operation.startsWith("reset")) {
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
import clearTable from "./clear.handler";
|
||||
|
||||
export default async function handler({ operation, data, payload }) {
|
||||
const table = window.CACHE["contacts"];
|
||||
if (operation.startsWith("reset")) {
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
import clearTable from "./clear.handler";
|
||||
|
||||
export default async function handler({ operation, data = {}, payload }) {
|
||||
const table = window.CACHE["messageDM"];
|
||||
if (operation.startsWith("reset")) {
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
import clearTable from "./clear.handler";
|
||||
|
||||
export default async function handler({ operation, data = {} }) {
|
||||
const table = window.CACHE["messageFile"];
|
||||
if (operation.startsWith("reset")) {
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
import clearTable from "./clear.handler";
|
||||
|
||||
export default async function handler({ operation, data = {}, payload }) {
|
||||
const table = window.CACHE["footprint"];
|
||||
if (operation.startsWith("reset")) {
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
import clearTable from "./clear.handler";
|
||||
|
||||
export default async function handler({ operation, data = {}, payload }) {
|
||||
const table = window.CACHE["message"];
|
||||
if (operation.startsWith("reset")) {
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
import clearTable from "./clear.handler";
|
||||
|
||||
export default async function handler({ operation, data = {}, payload }) {
|
||||
const table = window.CACHE["messageReaction"];
|
||||
if (operation.startsWith("reset")) {
|
||||
+8
-2
@@ -1,6 +1,12 @@
|
||||
// import clearTable from "./clear.handler";
|
||||
import { updateOnline } from "../slices/ui";
|
||||
export default async function handler({ dispatch, operation }) {
|
||||
import { AppDispatch } from "../store";
|
||||
|
||||
interface Params {
|
||||
dispatch: AppDispatch;
|
||||
operation: string;
|
||||
}
|
||||
|
||||
export default async function handler({ dispatch, operation }: Params) {
|
||||
switch (operation) {
|
||||
case "offline":
|
||||
{
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
import clearTable from "./clear.handler";
|
||||
|
||||
export default async function handler({ operation, payload }) {
|
||||
const table = window.CACHE["server"];
|
||||
if (operation.startsWith("reset")) {
|
||||
@@ -1,4 +1,5 @@
|
||||
import clearTable from "./clear.handler";
|
||||
|
||||
export default async function handler({ operation, data = {} }) {
|
||||
const table = window.CACHE["ui"];
|
||||
if (operation.startsWith("reset")) {
|
||||
@@ -10,6 +10,7 @@ import fileMessageHandler from "./handler.file.msg";
|
||||
import reactionHandler from "./handler.reaction";
|
||||
import UIHandler from "./handler.ui";
|
||||
import footprintHandler from "./handler.footprint";
|
||||
|
||||
const operations = [
|
||||
"__rtkq",
|
||||
"channels",
|
||||
@@ -4,6 +4,7 @@ import { ContentTypes } from "../config";
|
||||
import { addChannelMsg, removeChannelMsg } from "../slices/message.channel";
|
||||
import { addUserMsg, removeUserMsg } from "../slices/message.user";
|
||||
import { addMessage, removeMessage } from "../slices/message";
|
||||
|
||||
export const onMessageSendStarted = async (
|
||||
{
|
||||
ignoreLocal = false,
|
||||
@@ -2,7 +2,9 @@ import { createApi } from "@reduxjs/toolkit/query/react";
|
||||
import BASE_URL from "../config";
|
||||
import { updateInfo } from "../slices/server";
|
||||
import baseQuery from "./base.query";
|
||||
|
||||
const defaultExpireDuration = 7 * 24 * 60 * 60;
|
||||
|
||||
export const serverApi = createApi({
|
||||
reducerPath: "serverApi",
|
||||
baseQuery,
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useState } from "react";
|
||||
// import { useSelector } from "react-redux";
|
||||
import styled from "styled-components";
|
||||
import { hideAll } from "tippy.js";
|
||||
import IconInvite from "../../assets/icons/add.person.svg";
|
||||
@@ -12,7 +11,7 @@ import InviteModal from "./InviteModal";
|
||||
const Styled = styled.ul`
|
||||
z-index: 999;
|
||||
user-select: none;
|
||||
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: 12px;
|
||||
color: #616161;
|
||||
background: #fff;
|
||||
@@ -64,7 +63,7 @@ export default function AddEntriesMenu() {
|
||||
return !prevVisible;
|
||||
});
|
||||
};
|
||||
const handleOpenChannelModal = (isPrivate) => {
|
||||
const handleOpenChannelModal = (isPrivate: boolean) => {
|
||||
setIsPrivate(isPrivate);
|
||||
setChannelModalVisible(true);
|
||||
hideAll();
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect, memo } from "react";
|
||||
import { getInitials, getInitialsAvatar } from "../utils";
|
||||
|
||||
const Avatar = ({ url = "", name = "unkonw name", type = "user", ...rest }) => {
|
||||
// console.log("avatar url", url);
|
||||
const [src, setSrc] = useState("");
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import Avatar from "../component/Avatar";
|
||||
import Avatar from "./Avatar";
|
||||
import uploadIcon from "../../assets/icons/upload.image.svg?url";
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
@@ -59,6 +60,7 @@ const StyledWrapper = styled.div`
|
||||
right: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function AvatarUploader({
|
||||
url = "",
|
||||
name = "",
|
||||
+7
-5
@@ -1,14 +1,15 @@
|
||||
import { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import ChannelModal from "../component/ChannelModal";
|
||||
import InviteModal from "../component/InviteModal";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import ChannelModal from "./ChannelModal";
|
||||
import InviteModal from "./InviteModal";
|
||||
import IconChat from "../../assets/icons/placeholder.chat.svg";
|
||||
import IconAsk from "../../assets/icons/placeholder.question.svg";
|
||||
import IconInvite from "../../assets/icons/placeholder.invite.svg";
|
||||
import IconDownload from "../../assets/icons/placeholder.download.svg";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import ContactsModal from "./ContactsModal";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
|
||||
const Styled = styled.div`
|
||||
margin-top: -50px;
|
||||
display: flex;
|
||||
@@ -65,8 +66,9 @@ const Styled = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function BlankPlaceholder({ type = "chat" }) {
|
||||
const server = useSelector((store) => store.server);
|
||||
const server = useAppSelector((store) => store.server);
|
||||
const [inviteModalVisible, setInviteModalVisible] = useState(false);
|
||||
const [createChannelVisible, setCreateChannelVisible] = useState(false);
|
||||
const [contactListVisible, setContactListVisible] = useState(false);
|
||||
@@ -1,8 +1,6 @@
|
||||
// import React from 'react';
|
||||
import styled from "styled-components";
|
||||
import { useSelector } from "react-redux";
|
||||
// import { useNavigate } from "react-router-dom";
|
||||
import Avatar from "./Avatar";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
@@ -23,8 +21,8 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
.avatar {
|
||||
cursor: pointer;
|
||||
width: ${({ size }) => `${size}px`};
|
||||
height: ${({ size }) => `${size}px`};
|
||||
width: ${({ size }: { size: number }) => `${size}px`};
|
||||
height: ${({ size }: { size: number }) => `${size}px`};
|
||||
position: relative;
|
||||
img {
|
||||
border-radius: 50%;
|
||||
@@ -47,8 +45,9 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Channel({ interactive = true, id = "", compact = false, avatarSize = 32 }) {
|
||||
const { channel, totalMemberCount } = useSelector((store) => {
|
||||
const { channel, totalMemberCount } = useAppSelector((store) => {
|
||||
return {
|
||||
channel: store.channels.byId[id],
|
||||
totalMemberCount: store.contacts.ids.length
|
||||
@@ -1,17 +1,27 @@
|
||||
// import React from 'react';
|
||||
import { FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import HashIcon from "../../assets/icons/channel.svg";
|
||||
import LockHashIcon from "../../assets/icons/channel.private.svg";
|
||||
import styled from "styled-components";
|
||||
|
||||
interface Props {
|
||||
personal?: boolean;
|
||||
muted?: boolean;
|
||||
className: string;
|
||||
}
|
||||
|
||||
const Styled = styled.div`
|
||||
display: flex;
|
||||
&.muted path {
|
||||
fill: #d0d5dd;
|
||||
}
|
||||
`;
|
||||
export default function ChannelIcon({ personal = false, muted = false, className }) {
|
||||
|
||||
const ChannelIcon: FC<Props> = ({ personal = false, muted = false, className }) => {
|
||||
return (
|
||||
<Styled className={`${muted ? "muted" : ""} ${className}`}>
|
||||
{personal ? <LockHashIcon /> : <HashIcon />}
|
||||
</Styled>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default ChannelIcon;
|
||||
+3
-3
@@ -1,20 +1,20 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useSelector } from "react-redux";
|
||||
import Modal from "../Modal";
|
||||
import Button from "../styled/Button";
|
||||
import ChannelIcon from "../ChannelIcon";
|
||||
import Contact from "../Contact";
|
||||
import StyledWrapper from "./styled";
|
||||
// import StyledToggle from "../../component/styled/Toggle";
|
||||
import StyledCheckbox from "../../component/styled/Checkbox";
|
||||
import StyledCheckbox from "../styled/Checkbox";
|
||||
import useFilteredUsers from "../../hook/useFilteredUsers";
|
||||
|
||||
import { useCreateChannelMutation } from "../../../app/services/channel";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
|
||||
export default function ChannelModal({ personal = false, closeModal }) {
|
||||
const { conactsData, loginUid } = useSelector((store) => {
|
||||
const { conactsData, loginUid } = useAppSelector((store) => {
|
||||
return { conactsData: store.contacts.byId, loginUid: store.authData.uid };
|
||||
});
|
||||
const navigateTo = useNavigate();
|
||||
-2
@@ -1,6 +1,4 @@
|
||||
// import { useState } from "react";
|
||||
import Tippy from "@tippyjs/react";
|
||||
// import { useDispatch } from "react-redux";
|
||||
import useContactOperation from "../../hook/useContactOperation";
|
||||
import ContextMenu from "../ContextMenu";
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// import React from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import IconOwner from "../../../assets/icons/owner.svg";
|
||||
@@ -8,6 +6,8 @@ import Profile from "../Profile";
|
||||
import ContextMenu from "./ContextMenu";
|
||||
import StyledWrapper from "./styled";
|
||||
import useContextMenu from "../../hook/useContextMenu";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
|
||||
export default function Contact({
|
||||
cid = null,
|
||||
owner = false,
|
||||
@@ -21,7 +21,7 @@ export default function Contact({
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
const { visible: contextMenuVisible, handleContextMenuEvent, hideContextMenu } = useContextMenu();
|
||||
const curr = useSelector((store) => store.contacts.byId[uid]);
|
||||
const curr = useAppSelector((store) => store.contacts.byId[uid]);
|
||||
const handleDoubleClick = () => {
|
||||
navigate(`/chat/dm/${uid}`);
|
||||
};
|
||||
@@ -17,8 +17,8 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
.avatar {
|
||||
cursor: pointer;
|
||||
width: ${({ size }) => `${size}px`};
|
||||
height: ${({ size }) => `${size}px`};
|
||||
width: ${({ size }: { size: number }) => `${size}px`};
|
||||
height: ${({ size }: { size: number }) => `${size}px`};
|
||||
position: relative;
|
||||
img {
|
||||
object-fit: cover;
|
||||
@@ -62,4 +62,5 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useRef } from "react";
|
||||
import { ChangeEvent, useRef, FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import { useOutsideClick } from "rooks";
|
||||
@@ -13,12 +13,12 @@ const StyledWrapper = styled.div`
|
||||
width: 440px;
|
||||
max-height: 402px;
|
||||
background: #fff;
|
||||
box-shadow: 0px 25px 50px rgba(31, 41, 55, 0.25);
|
||||
box-shadow: 0 25px 50px rgba(31, 41, 55, 0.25);
|
||||
border-radius: 8px;
|
||||
transition: all 0.5s ease;
|
||||
/* overflow-y: scroll; */
|
||||
.search {
|
||||
box-shadow: 0px 1px 0px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
|
||||
padding: 8px;
|
||||
width: -webkit-fill-available;
|
||||
input {
|
||||
@@ -51,12 +51,17 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export default function ContactsModal({ closeModal }) {
|
||||
const wrapperRef = useRef();
|
||||
|
||||
interface Props {
|
||||
closeModal: () => void;
|
||||
}
|
||||
|
||||
const ContactsModal: FC<Props> = ({ closeModal }) => {
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
const { contacts, updateInput, input } = useFilteredUsers();
|
||||
useOutsideClick(wrapperRef, closeModal);
|
||||
const handleSearch = (evt) => {
|
||||
console.log("www");
|
||||
|
||||
const handleSearch = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
updateInput(evt.target.value);
|
||||
};
|
||||
|
||||
@@ -83,4 +88,6 @@ export default function ContactsModal({ closeModal }) {
|
||||
</StyledWrapper>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default ContactsModal;
|
||||
@@ -1,7 +1,20 @@
|
||||
// import { useRef } from "react";
|
||||
// import styled from "styled-components";
|
||||
import { FC, MouseEvent } from "react";
|
||||
import StyledMenu from "./styled/Menu";
|
||||
export default function ContextMenu({ items = [], hideMenu = null }) {
|
||||
|
||||
interface Item {
|
||||
title: string;
|
||||
icon: string;
|
||||
handler: (e: MouseEvent) => void;
|
||||
underline?: boolean;
|
||||
danger?: boolean;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
items: Item[];
|
||||
hideMenu?: () => void;
|
||||
}
|
||||
|
||||
const ContextMenu: FC<Props> = ({ items = [], hideMenu = null }) => {
|
||||
return (
|
||||
<StyledMenu>
|
||||
{items.map((item) => {
|
||||
@@ -38,4 +51,6 @@ export default function ContextMenu({ items = [], hideMenu = null }) {
|
||||
})}
|
||||
</StyledMenu>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default ContextMenu;
|
||||
@@ -1,11 +1,11 @@
|
||||
// import { useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useSelector } from "react-redux";
|
||||
import soundIcon from "../../assets/icons/sound.on.svg?url";
|
||||
import micIcon from "../../assets/icons/mic.on.svg?url";
|
||||
import Avatar from "./Avatar";
|
||||
import useConfig from "../hook/useConfig";
|
||||
// import UserGuide from "./UserGuide";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
background-color: #f4f4f5;
|
||||
position: sticky;
|
||||
@@ -61,9 +61,10 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function CurrentUser() {
|
||||
const { values: agoraConfig } = useConfig("agora");
|
||||
const currUser = useSelector((store) => {
|
||||
const currUser = useAppSelector((store) => {
|
||||
return store.contacts.byId[store.authData.uid];
|
||||
});
|
||||
|
||||
+11
-4
@@ -1,11 +1,16 @@
|
||||
// import React from "react";
|
||||
import { useState } from "react";
|
||||
import { FC, useState } from "react";
|
||||
import useDeleteMessage from "../hook/useDeleteMessage";
|
||||
import StyledModal from "./styled/Modal";
|
||||
import Button from "./styled/Button";
|
||||
import Modal from "./Modal";
|
||||
import PreviewMessage from "./Message/PreviewMessage";
|
||||
export default function DeleteMessageConfirmModal({ closeModal, mids = [] }) {
|
||||
|
||||
interface Props {
|
||||
closeModal: (b: boolean) => void;
|
||||
mids?: number[] | number;
|
||||
}
|
||||
|
||||
const DeleteMessageConfirmModal: FC<Props> = ({ closeModal, mids = [] }) => {
|
||||
// const dispatch = useDispatch();
|
||||
const mid_arr = mids ? (Array.isArray(mids) ? mids : [mids]) : [];
|
||||
const [ids] = useState(mid_arr);
|
||||
@@ -39,4 +44,6 @@ export default function DeleteMessageConfirmModal({ closeModal, mids = [] }) {
|
||||
</StyledModal>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default DeleteMessageConfirmModal;
|
||||
@@ -1,5 +1,6 @@
|
||||
// import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { FC } from "react";
|
||||
|
||||
const StyledDivider = styled.hr`
|
||||
display: block;
|
||||
position: relative;
|
||||
@@ -21,7 +22,14 @@ const StyledDivider = styled.hr`
|
||||
color: #78787c;
|
||||
}
|
||||
`;
|
||||
export default function Divider({ content }) {
|
||||
|
||||
interface Props {
|
||||
content: string;
|
||||
}
|
||||
|
||||
const Divider: FC<Props> = ({ content }) => {
|
||||
if (!content) return null;
|
||||
return <StyledDivider data-content={content}></StyledDivider>;
|
||||
}
|
||||
};
|
||||
|
||||
export default Divider;
|
||||
@@ -1,9 +1,13 @@
|
||||
import { useState, useEffect } from "react";
|
||||
// import { NimblePicker } from "emoji-mart";
|
||||
import { useState, useEffect, FC } from "react";
|
||||
import { EmojiData } from "emoji-mart";
|
||||
import "emoji-mart/css/emoji-mart.css";
|
||||
import { Picker } from "emoji-mart";
|
||||
// import data from "emoji-mart/data/";
|
||||
import styled from "styled-components";
|
||||
|
||||
interface Props {
|
||||
onSelect: (emoji: EmojiData) => void;
|
||||
}
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
filter: drop-shadow(0px 25px 50px rgba(31, 41, 55, 0.25));
|
||||
border-radius: 12px;
|
||||
@@ -20,7 +24,8 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export default function EmojiPicker({ onSelect, ...rest }) {
|
||||
|
||||
const EmojiPicker: FC<Props> = ({ onSelect, ...rest }) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
useEffect(() => {
|
||||
const inter = setTimeout(() => {
|
||||
@@ -49,4 +54,6 @@ export default function EmojiPicker({ onSelect, ...rest }) {
|
||||
) : null}
|
||||
</StyledWrapper>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default EmojiPicker;
|
||||
@@ -1,11 +1,12 @@
|
||||
// import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { useGetServerVersionQuery } from "../../app/services/server";
|
||||
|
||||
const Styled = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
`;
|
||||
|
||||
export default function FAQ() {
|
||||
const { data: serverVersion } = useGetServerVersionQuery();
|
||||
console.log("build time", serverVersion);
|
||||
@@ -13,7 +13,10 @@ import {
|
||||
} from "./preview";
|
||||
import { getFileIcon, formatBytes } from "../../utils";
|
||||
import IconDownload from "../../../assets/icons/download.svg";
|
||||
|
||||
// todo
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
const renderPreview = (data) => {
|
||||
const { file_type, name, content } = data;
|
||||
let preview = null;
|
||||
+6
-2
@@ -1,5 +1,6 @@
|
||||
import { useState } from "react";
|
||||
import { ReactEventHandler, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Styled = styled.div`
|
||||
height: 100%;
|
||||
display: flex;
|
||||
@@ -15,12 +16,15 @@ const Styled = styled.div`
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Audio({ url = "" }) {
|
||||
const [err, setErr] = useState(false);
|
||||
const handleError = (err) => {
|
||||
|
||||
const handleError: ReactEventHandler<HTMLAudioElement> = (err) => {
|
||||
console.log("audio err", err);
|
||||
setErr(true);
|
||||
};
|
||||
|
||||
if (!url) return null;
|
||||
return (
|
||||
<Styled>
|
||||
+3
-2
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Styled = styled.div`
|
||||
background-color: #fff;
|
||||
height: 218px;
|
||||
padding: 15px 15px 0 15px;
|
||||
line-height: 1.4;
|
||||
@@ -11,10 +11,11 @@ const Styled = styled.div`
|
||||
background-color: #000;
|
||||
color: #eee;
|
||||
`;
|
||||
|
||||
export default function Code({ url = "" }) {
|
||||
const [content, setContent] = useState("");
|
||||
useEffect(() => {
|
||||
const getContent = async (url) => {
|
||||
const getContent = async (url: string) => {
|
||||
if (!url) return;
|
||||
const resp = await fetch(url);
|
||||
const txt = await resp.text();
|
||||
+3
-1
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Styled = styled.div`
|
||||
background-color: #fff;
|
||||
height: 218px;
|
||||
@@ -9,10 +10,11 @@ const Styled = styled.div`
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
`;
|
||||
|
||||
export default function Doc({ url = "" }) {
|
||||
const [content, setContent] = useState("");
|
||||
useEffect(() => {
|
||||
const getContent = async (url) => {
|
||||
const getContent = async (url: string) => {
|
||||
if (!url) return;
|
||||
const resp = await fetch(url);
|
||||
const txt = await resp.text();
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Styled = styled.div`
|
||||
height: 218px;
|
||||
overflow: hidden;
|
||||
@@ -9,6 +10,7 @@ const Styled = styled.div`
|
||||
object-fit: cover;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Image({ url = "" }) {
|
||||
if (!url) return null;
|
||||
return (
|
||||
+2
-2
@@ -1,6 +1,5 @@
|
||||
// import { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
// import { Document, Page } from "react-pdf";
|
||||
|
||||
const Styled = styled.div`
|
||||
padding: 8px;
|
||||
/* height: 218px; */
|
||||
@@ -10,6 +9,7 @@ const Styled = styled.div`
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Pdf({ url = "" }) {
|
||||
// const [content, setContent] = useState("");
|
||||
// const [pageNumber, setPageNumber] = useState(1);
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Styled = styled.div`
|
||||
height: 218px;
|
||||
video {
|
||||
@@ -8,6 +9,7 @@ const Styled = styled.div`
|
||||
object-fit: cover;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Video({ url = "" }) {
|
||||
if (!url) return null;
|
||||
return (
|
||||
@@ -1,4 +1,5 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const Styled = styled.div`
|
||||
background: #f3f4f6;
|
||||
border: 1px solid #d4d4d4;
|
||||
@@ -67,4 +68,5 @@ const Styled = styled.div`
|
||||
/* todo */
|
||||
}
|
||||
`;
|
||||
|
||||
export default Styled;
|
||||
+3
-1
@@ -2,6 +2,8 @@ import { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { CircularProgressbar, buildStyles } from "react-circular-progressbar";
|
||||
import "react-circular-progressbar/dist/styles.css";
|
||||
import { getDefaultSize } from "../../utils";
|
||||
|
||||
const Styled = styled.div`
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
@@ -30,7 +32,7 @@ const Styled = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
import { getDefaultSize } from "../../utils";
|
||||
|
||||
export default function ImageMessage({
|
||||
uploading,
|
||||
progress,
|
||||
+12
-3
@@ -1,5 +1,6 @@
|
||||
// import React from 'react'
|
||||
import { FC } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Styled = styled.div`
|
||||
background: #ecfdff;
|
||||
border-radius: 4px;
|
||||
@@ -12,10 +13,18 @@ const Styled = styled.div`
|
||||
border-radius: 4px;
|
||||
}
|
||||
`;
|
||||
export default function Progress({ value, width = "100%" }) {
|
||||
|
||||
interface Props {
|
||||
value: number;
|
||||
width?: string;
|
||||
}
|
||||
|
||||
const Progress: FC<Props> = ({ value, width = "100%" }) => {
|
||||
return (
|
||||
<Styled style={{ width }}>
|
||||
<div className="progress" style={{ width: `${value}%` }}></div>
|
||||
</Styled>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Progress;
|
||||
+6
-2
@@ -9,12 +9,16 @@ import useUploadFile from "../../hook/useUploadFile";
|
||||
import useSendMessage from "../../hook/useSendMessage";
|
||||
import Progress from "./Progress";
|
||||
import { getFileIcon, formatBytes, isImage, getImageSize } from "../../utils";
|
||||
// import { ReactComponent as IconDownload } from "../../../assets/icons/download.svg";
|
||||
// import { ReactComponent as IconClose } from "../../../assets/icons/close.circle.svg";
|
||||
import IconDownload from "../../../assets/icons/download.svg";
|
||||
import IconClose from "../../../assets/icons/close.circle.svg";
|
||||
|
||||
// todo: move to root file
|
||||
dayjs.extend(relativeTime);
|
||||
const isLocalFile = (content) => {
|
||||
return content && typeof content == "string" && content.startsWith("blob:");
|
||||
|
||||
const isLocalFile = (content: string) => {
|
||||
return content.startsWith("blob:");
|
||||
};
|
||||
|
||||
export default function FileMessage({
|
||||
+2
@@ -1,4 +1,5 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const Styled = styled.div`
|
||||
background: #f3f4f6;
|
||||
border: 1px solid #d4d4d4;
|
||||
@@ -58,4 +59,5 @@ const Styled = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default Styled;
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { useState, MouseEvent } from "react";
|
||||
// import toast from "react-hot-toast";
|
||||
import Modal from "../Modal";
|
||||
import Button from "../styled/Button";
|
||||
@@ -13,7 +13,7 @@ import useSendMessage from "../../hook/useSendMessage";
|
||||
import useFilteredChannels from "../../hook/useFilteredChannels";
|
||||
import useFilteredUsers from "../../hook/useFilteredUsers";
|
||||
import CloseIcon from "../../../assets/icons/close.circle.svg";
|
||||
import StyledCheckbox from "../../component/styled/Checkbox";
|
||||
import StyledCheckbox from "../styled/Checkbox";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export default function ForwardModal({ mids, closeModal }) {
|
||||
@@ -31,7 +31,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
||||
// const { conactsData, loginUid } = useSelector((store) => {
|
||||
// return { conactsData: store.contacts.byId, loginUid: store.authData.uid };
|
||||
// });
|
||||
const toggleCheck = ({ currentTarget }) => {
|
||||
const toggleCheck = ({ currentTarget }: MouseEvent<HTMLLIElement>) => {
|
||||
const { id, type = "user" } = currentTarget.dataset;
|
||||
const ids = type == "user" ? selectedMembers : selectedChannels;
|
||||
const updateState = type == "user" ? setSelectedMembers : setSelectedChannels;
|
||||
@@ -3,6 +3,7 @@
|
||||
import IconGithub from "../../assets/icons/github.svg";
|
||||
import styled from "styled-components";
|
||||
import Button from "./styled/Button";
|
||||
|
||||
const StyledSocialButton = styled(Button)`
|
||||
width: 100%;
|
||||
margin-bottom: 16px;
|
||||
@@ -18,6 +19,7 @@ const StyledSocialButton = styled(Button)`
|
||||
height: 24px;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function GithubLoginButton({ config = {} }) {
|
||||
const { client_id } = config;
|
||||
const handleGithubLogin = () => {
|
||||
+16
-6
@@ -1,11 +1,11 @@
|
||||
import { useEffect } from "react";
|
||||
import { FC, useEffect } from "react";
|
||||
import { useGoogleLogin } from "react-google-login";
|
||||
|
||||
import googleSvg from "../../assets/icons/google.svg?url";
|
||||
import toast from "react-hot-toast";
|
||||
import styled from "styled-components";
|
||||
import googleSvg from "../../assets/icons/google.svg?url";
|
||||
import Button from "./styled/Button";
|
||||
import { useLoginMutation } from "../../app/services/auth";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
const StyledSocialButton = styled(Button)`
|
||||
width: 100%;
|
||||
margin-bottom: 16px;
|
||||
@@ -21,8 +21,14 @@ const StyledSocialButton = styled(Button)`
|
||||
height: 24px;
|
||||
}
|
||||
`;
|
||||
export default function GoogleLoginButton({ clientId }) {
|
||||
|
||||
interface Props {
|
||||
clientId: string;
|
||||
}
|
||||
|
||||
const GoogleLoginButton: FC<Props> = ({ clientId }) => {
|
||||
const [login, { isSuccess, isLoading }] = useLoginMutation();
|
||||
|
||||
const { signIn, loaded } = useGoogleLogin({
|
||||
onScriptLoadFailure: (wtf) => {
|
||||
console.error("google login script load failure", wtf);
|
||||
@@ -39,6 +45,7 @@ export default function GoogleLoginButton({ clientId }) {
|
||||
console.error("google login failure", wtf);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
toast.success("Login Successfully");
|
||||
@@ -48,6 +55,7 @@ export default function GoogleLoginButton({ clientId }) {
|
||||
const handleGoogleLogin = () => {
|
||||
signIn();
|
||||
};
|
||||
|
||||
// console.log("google login ", loaded);
|
||||
return (
|
||||
<StyledSocialButton disabled={!loaded || isLoading} onClick={handleGoogleLogin}>
|
||||
@@ -55,4 +63,6 @@ export default function GoogleLoginButton({ clientId }) {
|
||||
{loaded ? `Sign in with Google` : `Initailizing`}
|
||||
</StyledSocialButton>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default GoogleLoginButton;
|
||||
@@ -3,6 +3,7 @@ import styled, { keyframes } from "styled-components";
|
||||
import { useOutsideClick, useKey } from "rooks";
|
||||
import { Ring } from "@uiball/loaders";
|
||||
import Modal from "./Modal";
|
||||
|
||||
const AniFadeIn = keyframes`
|
||||
from{
|
||||
background: transparent;
|
||||
@@ -11,6 +12,7 @@ to{
|
||||
background: rgba(1, 1, 1, 0.9);
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
/* todo */
|
||||
transition: all 0.5s ease;
|
||||
@@ -58,6 +60,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function ImagePreviewModal({ download = true, data, closeModal }) {
|
||||
const [url, setUrl] = useState(data?.thumbnail);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -1,8 +1,8 @@
|
||||
// import { useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import useInviteLink from "../hook/useInviteLink";
|
||||
import Input from "./styled/Input";
|
||||
import Button from "./styled/Button";
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -37,11 +37,13 @@ const StyledWrapper = styled.div`
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function InviteLink() {
|
||||
const { generating, link, linkCopied, copyLink, generateNewLink } = useInviteLink();
|
||||
const handleNewLink = () => {
|
||||
generateNewLink();
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<span className="tip">Share this link to invite people to this server.</span>
|
||||
+2
-2
@@ -1,14 +1,14 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useSelector } from "react-redux";
|
||||
import toast from "react-hot-toast";
|
||||
import Button from "../styled/Button";
|
||||
import Input from "../styled/Input";
|
||||
import Contact from "../Contact";
|
||||
import StyledCheckbox from "../styled/Checkbox";
|
||||
import { useAddMembersMutation } from "../../../app/services/channel";
|
||||
import CloseIcon from "../../../assets/icons/close.svg";
|
||||
import toast from "react-hot-toast";
|
||||
import useFilteredUsers from "../../../common/hook/useFilteredUsers";
|
||||
import useFilteredUsers from "../../hook/useFilteredUsers";
|
||||
|
||||
const Styled = styled.div`
|
||||
padding-top: 16px;
|
||||
+8
-4
@@ -1,7 +1,10 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState, ChangeEvent } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import styled from "styled-components";
|
||||
import useInviteLink from "../../hook/useInviteLink";
|
||||
import Button from "../styled/Button";
|
||||
import Input from "../styled/Input";
|
||||
|
||||
const Styled = styled.div`
|
||||
padding: 16px 0;
|
||||
.input {
|
||||
@@ -64,8 +67,7 @@ const Styled = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
import Button from "../styled/Button";
|
||||
import Input from "../styled/Input";
|
||||
|
||||
export default function InviteByEmail({ cid = null }) {
|
||||
const [email, setEmail] = useState("");
|
||||
const { enableSMTP, linkCopied, link, copyLink, generateNewLink, generating } =
|
||||
@@ -75,9 +77,11 @@ export default function InviteByEmail({ cid = null }) {
|
||||
toast.success("Invite Link Copied!");
|
||||
}
|
||||
}, [linkCopied]);
|
||||
const handleEmail = (evt) => {
|
||||
|
||||
const handleEmail = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
setEmail(evt.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<Styled>
|
||||
<div className="invite">
|
||||
+5
-4
@@ -1,9 +1,10 @@
|
||||
// import React from 'react'
|
||||
import styled from "styled-components";
|
||||
import { useSelector } from "react-redux";
|
||||
import InviteByEmail from "./InviteByEmail";
|
||||
import AddMembers from "./AddMembers";
|
||||
import CloseIcon from "../../../assets/icons/close.svg";
|
||||
import Modal from "../Modal";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
|
||||
const Styled = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -26,10 +27,10 @@ const Styled = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
import Modal from "../Modal";
|
||||
|
||||
// type: server,channel
|
||||
export default function InviteModal({ type = "server", cid = null, title = "", closeModal }) {
|
||||
const { channel, server } = useSelector((store) => {
|
||||
const { channel, server } = useAppSelector((store) => {
|
||||
return {
|
||||
channel: store.channels.byId[cid],
|
||||
server: store.server
|
||||
+11
-3
@@ -1,4 +1,4 @@
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, FC } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import useLeaveChannel from "../../hook/useLeaveChannel";
|
||||
@@ -6,7 +6,13 @@ import Modal from "../Modal";
|
||||
import StyledModal from "../styled/Modal";
|
||||
import Button from "../styled/Button";
|
||||
|
||||
export default function LeaveConfirmModal({ id, closeModal, handleNextStep }) {
|
||||
interface Props {
|
||||
id: number;
|
||||
closeModal: () => void;
|
||||
handleNextStep: () => void;
|
||||
}
|
||||
|
||||
const LeaveConfirmModal: FC<Props> = ({ id, closeModal, handleNextStep }) => {
|
||||
const navigateTo = useNavigate();
|
||||
const { isOwner, leaving, leaveChannel, leaveSuccess } = useLeaveChannel(id);
|
||||
|
||||
@@ -47,4 +53,6 @@ export default function LeaveConfirmModal({ id, closeModal, handleNextStep }) {
|
||||
></StyledModal>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default LeaveConfirmModal;
|
||||
+13
-3
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState, FC } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import styled from "styled-components";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
@@ -7,6 +7,7 @@ import useLeaveChannel from "../../hook/useLeaveChannel";
|
||||
import StyledModal from "../styled/Modal";
|
||||
import Button from "../styled/Button";
|
||||
import Contact from "../Contact";
|
||||
|
||||
const UserList = styled.ul`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -28,7 +29,14 @@ const UserList = styled.ul`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export default function TransferOwnerModal({ id, closeModal, withLeave = true }) {
|
||||
|
||||
interface Props {
|
||||
id: number;
|
||||
closeModal: () => void;
|
||||
withLeave?: boolean;
|
||||
}
|
||||
|
||||
const TransferOwnerModal: FC<Props> = ({ id, closeModal, withLeave = true }) => {
|
||||
const {
|
||||
transferOwner,
|
||||
otherMembers,
|
||||
@@ -97,4 +105,6 @@ export default function TransferOwnerModal({ id, closeModal, withLeave = true })
|
||||
</StyledModal>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default TransferOwnerModal;
|
||||
+12
-4
@@ -1,12 +1,20 @@
|
||||
import { useState } from "react";
|
||||
// import styled from "styled-components";
|
||||
import { useState, FC } from "react";
|
||||
import TransferOwnerModal from "./TransferOwnerModal";
|
||||
import LeaveConfirmModal from "./LeaveConfirmModal";
|
||||
export default function LeaveChannel({ id = null, isOwner = false, closeModal }) {
|
||||
|
||||
interface Props {
|
||||
id: number;
|
||||
isOwner?: boolean;
|
||||
closeModal: () => void;
|
||||
}
|
||||
|
||||
const LeaveChannel: FC<Props> = ({ id, isOwner = false, closeModal }) => {
|
||||
const [transferOwner, setTransferOwner] = useState(isOwner);
|
||||
const handleNextStep = () => {
|
||||
setTransferOwner(true);
|
||||
};
|
||||
if (transferOwner) return <TransferOwnerModal id={id} closeModal={closeModal} />;
|
||||
return <LeaveConfirmModal id={id} closeModal={closeModal} handleNextStep={handleNextStep} />;
|
||||
}
|
||||
};
|
||||
|
||||
export default LeaveChannel;
|
||||
@@ -1,9 +1,9 @@
|
||||
// import { useState, useEffect } from "react";
|
||||
import styled, { keyframes } from "styled-components";
|
||||
import { Ring } from "@uiball/loaders";
|
||||
|
||||
import Button from "./styled/Button";
|
||||
import useLogout from "../hook/useLogout";
|
||||
|
||||
const DelayVisible = keyframes`
|
||||
from{
|
||||
opacity: 0;
|
||||
@@ -12,6 +12,7 @@ to{
|
||||
opacity: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -37,12 +38,15 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Loading({ reload = false, fullscreen = false }) {
|
||||
const { clearLocalData } = useLogout();
|
||||
const handleReload = () => {
|
||||
clearLocalData();
|
||||
// todo: only firefox has argument
|
||||
location.reload(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className={fullscreen ? "fullscreen" : ""}>
|
||||
<Ring className="loading" size={40} lineWeight={5} speed={2} color="black" />
|
||||
@@ -1,9 +1,7 @@
|
||||
import { useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import Tippy from "@tippyjs/react";
|
||||
import { hideAll } from "tippy.js";
|
||||
import { useSelector } from "react-redux";
|
||||
import toast from "react-hot-toast";
|
||||
import { useUpdateContactMutation } from "../../app/services/contact";
|
||||
import Contact from "./Contact";
|
||||
@@ -14,6 +12,8 @@ import IconOwner from "../../assets/icons/owner.svg";
|
||||
import IconArrowDown from "../../assets/icons/arrow.down.mini.svg";
|
||||
import IconCheck from "../../assets/icons/check.sign.svg";
|
||||
import useContactOperation from "../hook/useContactOperation";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
|
||||
const StyledWrapper = styled.section`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -114,8 +114,9 @@ const StyledWrapper = styled.section`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function ManageMembers({ cid = null }) {
|
||||
const { contacts, channels, loginUser } = useSelector((store) => {
|
||||
const { contacts, channels, loginUser } = useAppSelector((store) => {
|
||||
return {
|
||||
contacts: store.contacts,
|
||||
channels: store.channels,
|
||||
@@ -1,8 +1,10 @@
|
||||
// import React from "react";
|
||||
import { FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import Modal from "../Modal";
|
||||
import IconClose from "../../../assets/icons/close.svg";
|
||||
import Button from "../../component/styled/Button";
|
||||
// import { ReactComponent as IconClose } from "../../../assets/icons/close.svg";
|
||||
import Button from "../styled/Button";
|
||||
|
||||
const Styled = styled.div`
|
||||
position: relative;
|
||||
margin-top: 15px;
|
||||
@@ -11,7 +13,7 @@ const Styled = styled.div`
|
||||
padding: 16px;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
box-shadow: 0px 25px 50px rgba(31, 41, 55, 0.25);
|
||||
box-shadow: 0 25px 50px rgba(31, 41, 55, 0.25);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
@@ -44,7 +46,13 @@ const Styled = styled.div`
|
||||
right: 16px;
|
||||
}
|
||||
`;
|
||||
export default function Prompt({ handleInstall, closePrompt }) {
|
||||
|
||||
interface Props {
|
||||
handleInstall?: () => void;
|
||||
closePrompt?: () => void;
|
||||
}
|
||||
|
||||
const Prompt: FC<Props> = ({ handleInstall, closePrompt }) => {
|
||||
return (
|
||||
<Modal mask={false}>
|
||||
<Styled>
|
||||
@@ -64,4 +72,6 @@ export default function Prompt({ handleInstall, closePrompt }) {
|
||||
</Styled>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Prompt;
|
||||
@@ -1,6 +1,4 @@
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
// import { useGetServerQuery } from "../../../app/services/server";
|
||||
// import manifest from "./manifest.json";
|
||||
import Prompt from "./Prompt";
|
||||
import usePrompt from "./usePrompt";
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// import React from "react";
|
||||
import { KEY_PWA_INSTALLED } from "../../../app/config";
|
||||
|
||||
export default function usePrompt() {
|
||||
const resetPrompt = () => {
|
||||
localStorage.removeItem(KEY_PWA_INSTALLED);
|
||||
+1
-1
@@ -8,7 +8,7 @@ import codeSyntaxHighlight from "@toast-ui/editor-plugin-code-syntax-highlight/d
|
||||
import StyledWrapper from "./styled";
|
||||
import useUploadFile from "../../hook/useUploadFile";
|
||||
|
||||
import Button from "../../component/styled/Button";
|
||||
import Button from "../styled/Button";
|
||||
|
||||
function MarkdownEditor({
|
||||
updateDraft = null,
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
position: relative;
|
||||
width: 100%;
|
||||
+4
-4
@@ -3,11 +3,10 @@ import { useDispatch } from "react-redux";
|
||||
import styled from "styled-components";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import { hideAll } from "tippy.js";
|
||||
import toast from "react-hot-toast";
|
||||
import { updateSelectMessages } from "../../../app/slices/ui";
|
||||
// import StyledMenu from "../styled/Menu";
|
||||
import ContextMenu from "../ContextMenu";
|
||||
import Tooltip from "../../component/Tooltip";
|
||||
|
||||
import Tooltip from "../Tooltip";
|
||||
import useFavMessage from "../../hook/useFavMessage";
|
||||
import useSendMessage from "../../hook/useSendMessage";
|
||||
import ReactionPicker from "./ReactionPicker";
|
||||
@@ -20,8 +19,8 @@ import IconForward from "../../../assets/icons/forward.svg";
|
||||
import IconSelect from "../../../assets/icons/select.svg";
|
||||
import IconDelete from "../../../assets/icons/delete.svg";
|
||||
import moreIcon from "../../../assets/icons/more.svg?url";
|
||||
import toast from "react-hot-toast";
|
||||
import useMessageOperation from "./useMessageOperation";
|
||||
|
||||
const StyledCmds = styled.ul`
|
||||
z-index: 999;
|
||||
position: absolute;
|
||||
@@ -62,6 +61,7 @@ const StyledCmds = styled.ul`
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Commands({ context = "user", contextId = 0, mid = 0, toggleEditMessage }) {
|
||||
const {
|
||||
canDelete,
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// import { useState } from "react";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import ContextMenu from "../ContextMenu";
|
||||
+5
-2
@@ -2,9 +2,10 @@ import { useState, useRef, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import TextareaAutosize from "react-textarea-autosize";
|
||||
import { useKey } from "rooks";
|
||||
import { useEditMessageMutation } from "../../../app/services/message";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useEditMessageMutation } from "../../../app/services/message";
|
||||
import { ContentTypes } from "../../../app/config";
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
width: 100%;
|
||||
.input {
|
||||
@@ -45,8 +46,9 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function EditMessage({ mid, cancelEdit }) {
|
||||
const inputRef = useRef();
|
||||
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||
const msg = useSelector((store) => store.message[mid] || {});
|
||||
const [shift, setShift] = useState(false);
|
||||
const [enter, setEnter] = useState(false);
|
||||
@@ -95,6 +97,7 @@ export default function EditMessage({ mid, cancelEdit }) {
|
||||
});
|
||||
};
|
||||
if (!msg) return null;
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="input">
|
||||
+2
-2
@@ -1,17 +1,17 @@
|
||||
import { useEffect, useState } from "react";
|
||||
// import dayjs from "dayjs";
|
||||
|
||||
import styled from "styled-components";
|
||||
import StyledMsg from "./styled";
|
||||
import renderContent from "./renderContent";
|
||||
import Avatar from "../Avatar";
|
||||
import useFavMessage from "../../hook/useFavMessage";
|
||||
|
||||
const StyledFav = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: var(--br);
|
||||
background-color: #f4f4f5;
|
||||
`;
|
||||
|
||||
const FavoritedMessage = ({ id }) => {
|
||||
const { favorites } = useFavMessage({});
|
||||
const [msgs, setMsgs] = useState(null);
|
||||
+1
@@ -5,6 +5,7 @@ import renderContent from "./renderContent";
|
||||
import Avatar from "../Avatar";
|
||||
import IconForward from "../../../assets/icons/forward.svg";
|
||||
import useNormalizeMessage from "../../hook/useNormalizeMessage";
|
||||
|
||||
const StyledForward = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -1,8 +1,9 @@
|
||||
// import React from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { FC } from "react";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import Profile from "../Profile";
|
||||
import styled from "styled-components";
|
||||
import Profile from "../Profile";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
|
||||
const Styled = styled.span`
|
||||
padding: 0 2px;
|
||||
color: #1fe1f9;
|
||||
@@ -10,8 +11,16 @@ const Styled = styled.span`
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
export default function Mention({ uid, popover = true, cid, textOnly = false }) {
|
||||
const contactsData = useSelector((store) => store.contacts.byId);
|
||||
|
||||
interface Props {
|
||||
uid: number;
|
||||
popover?: boolean;
|
||||
cid: number;
|
||||
textOnly?: boolean;
|
||||
}
|
||||
|
||||
const Mention: FC<Props> = ({ uid, popover = true, cid, textOnly = false }) => {
|
||||
const contactsData = useAppSelector((store) => store.contacts.byId);
|
||||
const user = contactsData[uid];
|
||||
if (!user) return null;
|
||||
if (textOnly) return `@${user.name}`;
|
||||
@@ -26,4 +35,6 @@ export default function Mention({ uid, popover = true, cid, textOnly = false })
|
||||
<Styled className={popover ? "clickable" : ""}>{`@${user.name}`}</Styled>
|
||||
</Tippy>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Mention;
|
||||
+20
-10
@@ -1,6 +1,13 @@
|
||||
// import React from "react";
|
||||
import { FC } from "react";
|
||||
import { useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import toast from "react-hot-toast";
|
||||
import usePinMessage from "../../hook/usePinMessage";
|
||||
import StyledModal from "../styled/Modal";
|
||||
import Button from "../styled/Button";
|
||||
import Modal from "../Modal";
|
||||
import PreviewMessage from "./PreviewMessage";
|
||||
|
||||
const StyledPinModal = styled(StyledModal)`
|
||||
min-width: 406px;
|
||||
.title,
|
||||
@@ -15,24 +22,25 @@ const StyledPinModal = styled(StyledModal)`
|
||||
overflow-x: hidden;
|
||||
}
|
||||
`;
|
||||
import usePinMessage from "../../hook/usePinMessage";
|
||||
import StyledModal from "../styled/Modal";
|
||||
import Button from "../styled/Button";
|
||||
import Modal from "../Modal";
|
||||
import PreviewMessage from "./PreviewMessage";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export default function PinMessageModal({ closeModal, mid = 0, gid = 0 }) {
|
||||
// const dispatch = useDispatch();
|
||||
interface Props {
|
||||
closeModal: () => void;
|
||||
mid: number;
|
||||
gid: number;
|
||||
}
|
||||
|
||||
const PinMessageModal: FC<Props> = ({ closeModal, mid = 0, gid = 0 }) => {
|
||||
const { channel, pinMessage, isPining, isSuccess } = usePinMessage(gid);
|
||||
const handlePin = () => {
|
||||
pinMessage(mid);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
closeModal();
|
||||
toast.success("Pin Message Successfully");
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isSuccess]);
|
||||
|
||||
if (!mid) return null;
|
||||
@@ -57,4 +65,6 @@ export default function PinMessageModal({ closeModal, mid = 0, gid = 0 }) {
|
||||
</StyledPinModal>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default PinMessageModal;
|
||||
+3
-3
@@ -1,11 +1,11 @@
|
||||
// import { useEffect, useRef, useState } from "react";
|
||||
import dayjs from "dayjs";
|
||||
import renderContent from "./renderContent";
|
||||
import Avatar from "../Avatar";
|
||||
import StyledWrapper from "./styled";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
|
||||
export default function PreviewMessage({ mid = 0 }) {
|
||||
const { msg, contactsData } = useSelector((store) => {
|
||||
const { msg, contactsData } = useAppSelector((store) => {
|
||||
return { msg: store.message[mid], contactsData: store.contacts.byId };
|
||||
});
|
||||
if (!msg) return null;
|
||||
+1
-1
@@ -1,4 +1,3 @@
|
||||
// import { useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import styled from "styled-components";
|
||||
import { getEmojiDataFromNative } from "emoji-mart";
|
||||
@@ -10,6 +9,7 @@ import ReactionPicker from "./ReactionPicker";
|
||||
import Tooltip from "../Tooltip";
|
||||
import { useReactMessageMutation } from "../../../app/services/message";
|
||||
import addEmojiIcon from "../../../assets/icons/add.emoji.svg?url";
|
||||
|
||||
const StyledWrapper = styled.span`
|
||||
position: relative;
|
||||
margin-top: 8px;
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
import styled from "styled-components";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
import { useReactMessageMutation } from "../../../app/services/message";
|
||||
import { Emojis } from "../../../app/config";
|
||||
import Emoji from "../ReactionItem";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
|
||||
const StyledPicker = styled.div`
|
||||
background: none;
|
||||
z-index: 999;
|
||||
@@ -38,7 +38,7 @@ const StyledPicker = styled.div`
|
||||
export default function ReactionPicker({ mid, hidePicker }) {
|
||||
// const wrapperRef = useRef(null);
|
||||
const [reactMessage, { isLoading }] = useReactMessageMutation();
|
||||
const { reactionData, currUid } = useSelector((store) => {
|
||||
const { reactionData, currUid } = useAppSelector((store) => {
|
||||
return {
|
||||
reactionData: store.reactionMessage[mid] || {},
|
||||
currUid: store.authData.uid
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from "react";
|
||||
import React, { MouseEvent, FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useSelector } from "react-redux";
|
||||
import reactStringReplace from "react-string-replace";
|
||||
import MrakdownRender from "../MrakdownRender";
|
||||
import Mention from "./Mention";
|
||||
@@ -8,6 +7,7 @@ import { ContentTypes } from "../../../app/config";
|
||||
import { getFileIcon, isImage } from "../../utils";
|
||||
|
||||
import Avatar from "../Avatar";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
const Styled = styled.div`
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
@@ -16,19 +16,23 @@ const Styled = styled.div`
|
||||
border-radius: var(--br);
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
white-space: nowrap;
|
||||
|
||||
.avatar {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
@@ -37,6 +41,7 @@ const Styled = styled.div`
|
||||
color: #06b6d4;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
overflow: hidden;
|
||||
font-weight: 500;
|
||||
@@ -45,20 +50,22 @@ const Styled = styled.div`
|
||||
color: #616161;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.txt {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
-webkit-box-orient: vertical;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.md {
|
||||
position: relative;
|
||||
max-height: 152px;
|
||||
overflow: hidden;
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
@@ -69,24 +76,29 @@ const Styled = styled.div`
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0) 63.54%, #e5e7eb 93.09%);
|
||||
}
|
||||
}
|
||||
|
||||
.pic {
|
||||
display: inherit;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 15px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.file_name {
|
||||
margin-left: 5px;
|
||||
font-size: 10px;
|
||||
color: #555;
|
||||
}
|
||||
}
|
||||
|
||||
/* padding-left: 10px; */
|
||||
`;
|
||||
|
||||
const renderContent = (data) => {
|
||||
const { content_type, content, thumbnail, properties } = data;
|
||||
let res = null;
|
||||
@@ -136,13 +148,19 @@ const renderContent = (data) => {
|
||||
}
|
||||
return res;
|
||||
};
|
||||
const Reply = ({ mid, interactive = true }) => {
|
||||
const { data, users } = useSelector((store) => {
|
||||
|
||||
interface ReplyProps {
|
||||
mid: number;
|
||||
interactive?: boolean;
|
||||
}
|
||||
|
||||
const Reply: FC<ReplyProps> = ({ mid, interactive = true }) => {
|
||||
const { data, users } = useAppSelector((store) => {
|
||||
return { data: store.message[mid], users: store.contacts.byId };
|
||||
});
|
||||
const handleClick = (evt) => {
|
||||
const handleClick = (evt: MouseEvent<HTMLDivElement>) => {
|
||||
const { mid } = evt.currentTarget.dataset;
|
||||
const msgEle = document.querySelector(`[data-msg-mid='${mid}']`);
|
||||
const msgEle = document.querySelector<HTMLDivElement>(`[data-msg-mid='${mid}']`);
|
||||
if (msgEle) {
|
||||
msgEle.dataset.highlight = true;
|
||||
msgEle.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
+3
-2
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useLazyGetOGInfoQuery } from "../../../app/services/message";
|
||||
// import favUrl from "../../../assets/icons/favicon.default.svg?url";
|
||||
|
||||
const StyledCompact = styled.a`
|
||||
background: #f3f4f6;
|
||||
border: 1px solid #d4d4d4;
|
||||
@@ -92,12 +92,13 @@ const StyledDetails = styled.a`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function URLPreview({ url = "" }) {
|
||||
const [favicon, setFavicon] = useState("");
|
||||
const [getInfo] = useLazyGetOGInfoQuery();
|
||||
const [data, setData] = useState(null);
|
||||
useEffect(() => {
|
||||
const getMetaData = async (url) => {
|
||||
const getMetaData = async (url: string) => {
|
||||
// todo
|
||||
const { data } = await getInfo(url);
|
||||
const title = data.title || data.site_name;
|
||||
@@ -15,11 +15,13 @@ import EditMessage from "./EditMessage";
|
||||
import renderContent from "./renderContent";
|
||||
import Tooltip from "../Tooltip";
|
||||
import ContextMenu from "./ContextMenu";
|
||||
|
||||
import useContextMenu from "../../hook/useContextMenu";
|
||||
import usePinMessage from "../../hook/usePinMessage";
|
||||
|
||||
// todo: move to root file
|
||||
dayjs.extend(isToday);
|
||||
dayjs.extend(isYesterday);
|
||||
|
||||
function Message({
|
||||
readOnly = false,
|
||||
contextId = 0,
|
||||
+1
-2
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import Linkit from "react-linkify";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import reactStringReplace from "react-string-replace";
|
||||
import { ContentTypes } from "../../../app/config";
|
||||
import Mention from "./Mention";
|
||||
import ForwardedMessage from "./ForwardedMessage";
|
||||
@@ -9,7 +9,6 @@ import MrakdownRender from "../MrakdownRender";
|
||||
import FileMessage from "../FileMessage";
|
||||
import URLPreview from "./URLPreview";
|
||||
|
||||
import reactStringReplace from "react-string-replace";
|
||||
const renderContent = ({
|
||||
context = null,
|
||||
to = null,
|
||||
@@ -1,4 +1,5 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledMsg = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
import { useRef, useEffect } from "react";
|
||||
|
||||
export default function useInView() {
|
||||
const ref = useRef(undefined);
|
||||
export default function useInView<T extends HTMLElement>() {
|
||||
const ref = useRef<T>(null);
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
@@ -17,7 +17,7 @@ export default function useInView() {
|
||||
{ threshold: 0 }
|
||||
);
|
||||
useEffect(() => {
|
||||
const currEle = ref?.current;
|
||||
const currEle = ref.current;
|
||||
if (currEle) {
|
||||
observer.observe(ref.current);
|
||||
}
|
||||
+9
-3
@@ -4,13 +4,19 @@ import DeleteMessageConfirm from "../DeleteMessageConfirm";
|
||||
import ForwardModal from "../ForwardModal";
|
||||
import PinMessageModal from "./PinMessageModal";
|
||||
import { ContentTypes } from "../../../app/config";
|
||||
import { useSelector } from "react-redux";
|
||||
import useCopy from "../../hook/useCopy";
|
||||
import usePinMessage from "../../hook/usePinMessage";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
|
||||
export default function useMessageOperation({ mid, context, contextId }) {
|
||||
interface Params {
|
||||
mid: number;
|
||||
context: string;
|
||||
contextId: number;
|
||||
}
|
||||
|
||||
export default function useMessageOperation({ mid, context, contextId }: Params) {
|
||||
const { copy } = useCopy();
|
||||
const { content_type, properties, currUid, from_uid, content } = useSelector((store) => {
|
||||
const { content_type, properties, currUid, from_uid, content } = useAppSelector((store) => {
|
||||
return {
|
||||
content: store.message[mid]?.content,
|
||||
from_uid: store.message[mid]?.from_uid,
|
||||
@@ -1,6 +1,5 @@
|
||||
// import React from 'react'
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
import BASE_URL from "../../app/config";
|
||||
import { useGetServerQuery } from "../../app/services/server";
|
||||
|
||||
+2
@@ -4,6 +4,7 @@ import { Transforms } from "slate";
|
||||
import { ReactEditor } from "slate-react";
|
||||
import styled from "styled-components";
|
||||
import iconClose from "../../../assets/icons/close.circle.svg?url";
|
||||
|
||||
const StylePicture = styled.picture`
|
||||
position: relative;
|
||||
display: flex;
|
||||
@@ -21,6 +22,7 @@ const StylePicture = styled.picture`
|
||||
right: -20px;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function ImageElement({ attributes, children, element }) {
|
||||
const editor = useEditorRef();
|
||||
const path = ReactEditor.findPath(editor, element);
|
||||
+2
-1
@@ -1,8 +1,9 @@
|
||||
// import React from 'react'
|
||||
import styled from "styled-components";
|
||||
|
||||
const Styled = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export default function StyledCombobox({ store }) {
|
||||
console.log("combox wtf", store.get.state());
|
||||
return <Styled>StyledCombobox</Styled>;
|
||||
@@ -1,4 +1,5 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const Styled = styled.div`
|
||||
max-height: 50vh;
|
||||
overflow: auto;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import { useEffect, useState, useRef, FC } from "react";
|
||||
import "prismjs/themes/prism.css";
|
||||
import "@toast-ui/editor-plugin-code-syntax-highlight/dist/toastui-editor-plugin-code-syntax-highlight.css";
|
||||
import codeSyntaxHighlight from "@toast-ui/editor-plugin-code-syntax-highlight/dist/toastui-editor-plugin-code-syntax-highlight-all.js";
|
||||
@@ -11,7 +11,7 @@ import codeSyntaxHighlight from "@toast-ui/editor-plugin-code-syntax-highlight/d
|
||||
|
||||
import { Viewer } from "@toast-ui/react-editor";
|
||||
import styled from "styled-components";
|
||||
import ImagePreviewModal from "../../common/component/ImagePreviewModal";
|
||||
import ImagePreviewModal from "./ImagePreviewModal";
|
||||
|
||||
const Styled = styled.div`
|
||||
* {
|
||||
@@ -23,9 +23,15 @@ const Styled = styled.div`
|
||||
align-items: flex-start;
|
||||
}
|
||||
`;
|
||||
export default function MrakdownRender({ content }) {
|
||||
const mdContainer = useRef(undefined);
|
||||
|
||||
interface Props {
|
||||
content: string;
|
||||
}
|
||||
|
||||
const MrakdownRender: FC<Props> = ({ content }) => {
|
||||
const mdContainer = useRef<HTMLDivElement>(null);
|
||||
const [previewImage, setPreviewImage] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const container = mdContainer?.current;
|
||||
if (container) {
|
||||
@@ -50,9 +56,11 @@ export default function MrakdownRender({ content }) {
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const closePreviewModal = () => {
|
||||
setPreviewImage(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{previewImage && (
|
||||
@@ -67,4 +75,6 @@ export default function MrakdownRender({ content }) {
|
||||
</Styled>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default MrakdownRender;
|
||||
+1
-2
@@ -1,6 +1,5 @@
|
||||
import { useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
// import toast, { Toaster } from 'react-hot-toast';
|
||||
import useDeviceToken from "./useDeviceToken";
|
||||
import { vapidKey } from "../../../app/config";
|
||||
import { useUpdateDeviceTokenMutation } from "../../../app/services/auth";
|
||||
@@ -16,7 +15,7 @@ const Notification = () => {
|
||||
}, [token]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleServiceworkerMessage = (event) => {
|
||||
const handleServiceworkerMessage = (event: MessageEvent) => {
|
||||
const { newPath } = event.data;
|
||||
navigateTo(newPath);
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user