Merge remote-tracking branch 'upstream/main' into refactor/typescript

# Conflicts:
#	src/app/services/server.ts
#	src/common/component/GoogleLoginButton.tsx
#	src/common/utils.tsx
This commit is contained in:
HD
2022-06-28 10:10:56 +08:00
47 changed files with 513 additions and 355 deletions
+48 -12
View File
@@ -1,7 +1,11 @@
import { FC } from "react";
import { useEffect, FC } from "react";
import IconGithub from "../../assets/icons/github.svg";
import styled from "styled-components";
import { KEY_LOCAL_MAGIC_TOKEN } from "../../app/config";
import Button from "./styled/Button";
import { useLoginMutation } from "../../app/services/auth";
import toast from "react-hot-toast";
const StyledSocialButton = styled(Button)`
width: 100%;
@@ -20,26 +24,58 @@ const StyledSocialButton = styled(Button)`
}
`;
interface Props {
config: {
client_id: string;
};
}
type Props = {
client_id: string;
type?: "login" | "register";
};
const GithubLoginButton: FC<Props> = ({ config }) => {
const { client_id } = config;
const GithubLoginButton: FC<Props> = ({ type = "login", client_id }) => {
//拿本地存的magic token
const magic_token = localStorage.getItem(KEY_LOCAL_MAGIC_TOKEN);
const [login, { isLoading, isSuccess, error }] = useLoginMutation();
useEffect(() => {
const query = new URLSearchParams(location.search);
const isGithub = query.get("oauth") === "github";
const code = query.get("code");
if (isGithub && code) {
login({
magic_token,
code: code,
type: "github"
});
}
}, []);
useEffect(() => {
if (isSuccess) {
toast.success("Login Successfully");
// navigateTo("/");
}
}, [isSuccess]);
useEffect(() => {
if (error) {
switch (error.status) {
case 410:
toast.error(
"No associated account found, please contact admin for an invitation link to join."
);
break;
default:
toast.error("Something Error");
break;
}
return;
}
}, [error]);
const handleGithubLogin = () => {
location.href = `https://github.com/login/oauth/authorize?client_id=${client_id}`;
// console.log("github login");
};
// console.log("google login ", loaded);
return (
<StyledSocialButton onClick={handleGithubLogin}>
<StyledSocialButton onClick={handleGithubLogin} disabled={isLoading}>
<IconGithub className="icon" />
Sign in with Github
{/* {loaded ? `Sign in with Github` : `Initializing`} */}
{` ${type === "login" ? "Sign in" : "Sign up"} with Github`}
</StyledSocialButton>
);
};
export default GithubLoginButton;
+25 -6
View File
@@ -2,7 +2,8 @@ import { FC, useEffect } from "react";
import { useGoogleLogin } from "react-google-login";
import toast from "react-hot-toast";
import styled from "styled-components";
import googleSvg from "../../assets/icons/google.svg?url";
import { KEY_LOCAL_MAGIC_TOKEN } from "../../app/config";
import IconGoogle from "../../assets/icons/google.svg";
import Button from "./styled/Button";
import { useLoginMutation } from "../../app/services/auth";
@@ -24,11 +25,13 @@ const StyledSocialButton = styled(Button)`
interface Props {
clientId: string;
type?: "login" | "register";
}
const GoogleLoginButton: FC<Props> = ({ clientId }) => {
const [login, { isSuccess, isLoading }] = useLoginMutation();
const GoogleLoginButton: FC<Props> = ({ type = "login", clientId }) => {
const [login, { isSuccess, isLoading, error }] = useLoginMutation();
//拿本地存的magic token
const magic_token = localStorage.getItem(KEY_LOCAL_MAGIC_TOKEN);
const { signIn, loaded } = useGoogleLogin({
onScriptLoadFailure: (wtf) => {
console.error("google login script load failure", wtf);
@@ -37,6 +40,7 @@ const GoogleLoginButton: FC<Props> = ({ clientId }) => {
onSuccess: ({ tokenId, ...rest }) => {
console.info("google oauth success", tokenId, rest);
login({
magic_token,
id_token: tokenId,
type: "google"
});
@@ -52,6 +56,21 @@ const GoogleLoginButton: FC<Props> = ({ clientId }) => {
// navigateTo("/");
}
}, [isSuccess]);
useEffect(() => {
if (error) {
switch (error.status) {
case 410:
toast.error(
"No associated account found, please contact admin for an invitation link to join."
);
break;
default:
toast.error("Something Error");
break;
}
return;
}
}, [error]);
const handleGoogleLogin = () => {
signIn();
};
@@ -59,8 +78,8 @@ const GoogleLoginButton: FC<Props> = ({ clientId }) => {
// console.log("google login ", loaded);
return (
<StyledSocialButton disabled={!loaded || isLoading} onClick={handleGoogleLogin}>
<img className="icon" src={googleSvg} alt="google icon" />
{loaded ? "Sign in with Google" : "Initializing"}
<IconGoogle className="icon" alt="google icon" />
{loaded ? `${type === "login" ? "Sign in" : "Sign up"} with Google` : `Initializing`}
</StyledSocialButton>
);
};
+2 -2
View File
@@ -3,7 +3,7 @@ import Prompt from "./Prompt";
import usePrompt from "./usePrompt";
export default function Manifest() {
const { setCanneled, prompted } = usePrompt();
const { setCanceled: setCanceled, prompted } = usePrompt();
const deferredPromptRef = useRef(null);
const [popup, setPopup] = useState(false);
// const { data, isSuccess } = useGetServerQuery();
@@ -57,7 +57,7 @@ export default function Manifest() {
deferredPromptRef.current = null;
};
const handleClose = async () => {
setCanneled();
setCanceled();
setPopup(false);
};
if (!popup || prompted) return null;
+1 -1
View File
@@ -1,5 +1,5 @@
{
"name": "RustChat",
"name": "VoceChat",
"short_name": "Your private chat APP",
"icons": [
{
+1 -1
View File
@@ -10,7 +10,7 @@ export default function usePrompt() {
};
return {
setCanneled: setPrompt,
setCanceled: setPrompt,
prompted: !!localStorage.getItem(KEY_PWA_INSTALLED),
resetPrompt
};
+1 -1
View File
@@ -31,7 +31,7 @@ import useUploadFile from "../../hook/useUploadFile";
import Styled from "./styled";
import { CONFIG } from "./config";
import Contact from "../Contact";
export const TEXT_EDITOR_PREFIX = "rustchat_text_editor";
export const TEXT_EDITOR_PREFIX = "_text_editor";
let components = createPlateUI({
// [ELEMENT_IMAGE]: ImageElement,
+2 -2
View File
@@ -1,6 +1,6 @@
import { useEffect, useState } from "react";
import toast from "react-hot-toast";
import { isObjectEqual } from "../utils";
import { isEqual } from "lodash";
import {
useUpdateLoginConfigMutation,
useUpdateSMTPConfigMutation,
@@ -101,7 +101,7 @@ export default function useConfig(config = "smtp") {
useEffect(() => {
// 空对象
if (Object.keys(values).length == 0) return;
if (!isObjectEqual(originalValue, values)) {
if (!isEqual(originalValue, values)) {
setChanged(true);
} else {
setChanged(false);
+1 -1
View File
@@ -65,7 +65,7 @@ export default function useMessageFeed({ context = "channel", id = null }) {
}, [context, id]);
useEffect(() => {
if (items.length) {
containerRef.current = document.querySelector(`#RUSTCHAT_FEED_${context}_${id}`);
containerRef.current = document.querySelector(`#VOCECHAT_FEED_${context}_${id}`);
if (containerRef.current) {
const newScroll = containerRef.current.scrollHeight - containerRef.current.clientHeight;
containerRef.current.scrollTop = curScrollPos + (newScroll - oldScroll);
+4 -4
View File
@@ -219,7 +219,7 @@ export default function useStreaming() {
const { gid, ...rest } = data;
dispatch(
updateChannel({
id: gid,
gid,
...rest
})
);
@@ -233,7 +233,7 @@ export default function useStreaming() {
dispatch(
updateChannel({
operation: "add_member",
id: gid,
gid,
members: uids
})
);
@@ -242,13 +242,13 @@ export default function useStreaming() {
case "user_leaved_group":
{
const { gid, uid: uids } = data;
if (uids.findIndex((id) => id == loginUid) > -1) {
if (uids.findIndex((uid) => uid == loginUid) > -1) {
dispatch(removeChannel(gid));
} else {
dispatch(
updateChannel({
operation: "remove_member",
id: gid,
gid,
members: uids
})
);
+1 -1
View File
@@ -42,7 +42,7 @@ export default function useUploadFile(props: { context: string; id: string } | o
if (!file) return;
setData(null);
const {
name = `rustchat-${+new Date()}.${file.type.split("/")[1]}`,
name = `-${+new Date()}.${file.type.split("/")[1]}`,
type: file_type,
size: file_size
} = file;
+20 -61
View File
@@ -11,27 +11,7 @@ export const isImage = (file_type = "", size = 0) => {
return file_type.startsWith("image") && size <= FILE_IMAGE_SIZE;
};
const deepSortObject = (obj: object): any => {
return Object.fromEntries(
Object.entries(obj)
.map((entry) => [
entry[0],
typeof entry[1] === "object" ? deepSortObject(entry[1]) : entry[1]
])
.sort()
);
};
export const isObjectEqual = (obj1: any, obj2: any) => {
// Check for reference equal
if (obj1 === obj2) return true;
// Check for deep equal
let o1 = JSON.stringify(deepSortObject(obj1 ?? {}));
let o2 = JSON.stringify(deepSortObject(obj2 ?? {}));
return o1 === o2;
};
export const isTreatAsImage = (file: File | null) => {
export const isTreatAsImage = (file) => {
let isImage = false;
if (!file) return isImage;
const { type, size } = file;
@@ -42,8 +22,8 @@ export const isTreatAsImage = (file: File | null) => {
return isImage;
};
export const getNonNullValues = (obj: any, whiteList = ["log_id"]): any => {
const tmp: any = {};
export const getNonNullValues = (obj, whiteList = ["log_id"]) => {
const tmp = {};
Object.keys(obj).forEach((k) => {
if (!whiteList.includes(k) && obj[k] !== null) {
tmp[k] = obj[k];
@@ -51,20 +31,14 @@ export const getNonNullValues = (obj: any, whiteList = ["log_id"]): any => {
});
return tmp;
};
interface Size {
width: number;
height: number;
}
export function getDefaultSize(size: Size | null = null, min: number = 480) {
export function getDefaultSize(size = null, min = 480) {
if (!size) return { width: 0, height: 0 };
const { width: oWidth, height: oHeight } = size;
if (oWidth == oHeight) {
const tmp = min > oWidth ? oWidth : min;
return { width: tmp, height: tmp };
}
const isVertical = oWidth <= oHeight;
const isVertical = oWidth > oHeight ? false : true;
let dWidth = 0;
let dHeight = 0;
if (isVertical) {
@@ -76,8 +50,7 @@ export function getDefaultSize(size: Size | null = null, min: number = 480) {
}
return { width: dWidth, height: dHeight };
}
export function formatBytes(bytes: number, decimals = 2) {
export function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return "0 Bytes";
const k = 1000;
@@ -88,16 +61,9 @@ export function formatBytes(bytes: number, decimals = 2) {
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
}
export interface ImageSize {
width: number;
height: number;
}
export const getImageSize = (url: string): Promise<ImageSize> => {
const size: ImageSize = { width: 0, height: 0 };
// todo: check inactive code
// if (!url) return size;
export const getImageSize = (url) => {
const size = { width: 0, height: 0 };
if (!url) return size;
return new Promise((resolve) => {
const img = new Image();
img.src = url;
@@ -111,15 +77,13 @@ export const getImageSize = (url: string): Promise<ImageSize> => {
};
});
};
export const getInitials = (name: string) => {
export const getInitials = (name) => {
const arr = name.split(" ").filter((n) => !!n);
return arr
.map((t) => t[0])
.join("")
.toUpperCase();
};
export const getInitialsAvatar = ({
initials = "UK",
initial_size = 0,
@@ -138,7 +102,7 @@ export const getInitialsAvatar = ({
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
const context = canvas.getContext("2d")!;
const context = canvas.getContext("2d");
context.scale(devicePixelRatio, devicePixelRatio);
context.rect(0, 0, canvas.width, canvas.height);
context.fillStyle = background;
@@ -156,8 +120,12 @@ export const getInitialsAvatar = ({
/* istanbul ignore next */
return canvas.toDataURL("image/png");
};
export function sliceFile(file: File | null, chunksAmount: number) {
/**
* @param {File|Blob} - file to slice
* @param {Number} - chunksAmount
* @return {Array} - an array of Blobs
**/
export function sliceFile(file, chunksAmount) {
if (!file) return null;
let byteIndex = 0;
let chunks = [];
@@ -170,8 +138,7 @@ export function sliceFile(file: File | null, chunksAmount: number) {
return chunks;
}
export const getFileIcon = (type: string, name: string = "") => {
export const getFileIcon = (type, name = "") => {
let icon = null;
const checks = {
@@ -213,18 +180,10 @@ export const getFileIcon = (type: string, name: string = "") => {
}
return icon;
};
export const normalizeArchiveData = (
data = null,
filePath: string | null = null,
uid: number | null = null
) => {
export const normalizeArchiveData = (data = null, filePath = null, uid = null) => {
if (!data || !filePath) return [];
const { messages, users } = data;
const getUrls = (
uid: number,
{ content, content_type, file_id, thumbnail_id, filePath, avatar }
) => {
const getUrls = (uid, { content, content_type, file_id, thumbnail_id, filePath, avatar }) => {
// uid存在,则favorite,否则archive
const prefix = uid
? `${BASE_URL}/favorite/attachment/${uid}/${filePath}/`