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