feat: github login
This commit is contained in:
@@ -7,15 +7,15 @@ type Props = {
|
||||
};
|
||||
|
||||
const Index: FC<Props> = ({ handleClose }) => {
|
||||
const { name, logo } = useAppSelector(store => store.server);
|
||||
const { name, description, logo } = useAppSelector(store => store.server);
|
||||
return (
|
||||
<div className="flex gap-4 justify-between items-center p-2">
|
||||
<div className="relative w-10 h-10">
|
||||
<div className="flex gap-2 justify-between items-center p-2">
|
||||
<div className="relative w-12 h-12">
|
||||
<img src={logo} alt="logo" className="w-full h-full" />
|
||||
</div>
|
||||
<div className="flex flex-col flex-1">
|
||||
<span className="text-gray-900 text-lg">{name}</span>
|
||||
{/* <span className="text-gray-400 text-sm">{online ? 'Active now' : 'Offline now'}</span> */}
|
||||
<span className="text-gray-900 text-lg font-bold">{name}</span>
|
||||
{description && <span className="text-gray-400 text-sm">{description}</span>}
|
||||
</div>
|
||||
<IconClose className="w-5 h-5 mr-2 cursor-pointer" onClick={handleClose} />
|
||||
</div>
|
||||
|
||||
@@ -20,9 +20,9 @@ const Login = () => {
|
||||
} = loginConfig;
|
||||
const googleLogin = enableGoogleLogin && clientId;
|
||||
return (
|
||||
<div className="w-60 flex flex-col">
|
||||
<div className="w-60 flex flex-col py-2">
|
||||
{googleLogin && <GoogleLoginButton clientId={clientId} />}
|
||||
{enableGithubLogin && <GithubLoginButton client_id={githubAuthConfig?.client_id} />}
|
||||
{enableGithubLogin && <GithubLoginButton client_id={githubAuthConfig?.client_id} source="widget" />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -28,18 +28,18 @@ export type MessageProps = {
|
||||
|
||||
const Index: FC<MessageProps> = (props) => {
|
||||
const { server: { name, logo }, loginUser } = useAppSelector(store => { return { server: store.server, loginUser: store.authData.user }; });
|
||||
const { hostId, from_uid, content, created_at, compact, isFirst = false } = props;
|
||||
const { hostId, from_uid, content, created_at = 0, compact, isFirst = false } = props;
|
||||
const isHost = from_uid == hostId;
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'flex flex-col relative pl-14 py-1 hover:bg-gray-100 rounded-sm',
|
||||
compact ? 'mt-0' : 'mt-4',
|
||||
'flex flex-col relative pl-14 py-2 hover:bg-gray-100 rounded-sm',
|
||||
compact ? 'py-1' : 'mt-4',
|
||||
isFirst ? 'mt-0' : ''
|
||||
)}
|
||||
>
|
||||
{!compact && (
|
||||
<div className="absolute left-0 top-2 w-12 h-12 rounded-full overflow-hidden">
|
||||
<div className="absolute left-1 top-2 w-12 h-12 rounded-full overflow-hidden">
|
||||
{isHost ? (
|
||||
<img src={logo} alt="logo" className="w-full h-full" />
|
||||
) : (
|
||||
@@ -48,19 +48,19 @@ const Index: FC<MessageProps> = (props) => {
|
||||
</div>
|
||||
)}
|
||||
{!compact && (
|
||||
<span className="flex items-center gap-2 leading-6">
|
||||
<em className="text-black not-italic font-bold text-md">{isHost ? name : loginUser?.name}</em>
|
||||
<Time time={created_at ?? 0} />
|
||||
<span className="flex items-center gap-2 text-md">
|
||||
<em className="text-black not-italic font-bold">{isHost ? name : loginUser?.name}</em>
|
||||
<Time time={created_at} />
|
||||
</span>
|
||||
)}
|
||||
<p
|
||||
className={clsx(
|
||||
'text-gray-600 text-md whitespace-normal break-words mt-2 w-[80%]',
|
||||
compact ? 'group relative mt-0' : ''
|
||||
'text-gray-600 text-md whitespace-normal break-words w-[80%]',
|
||||
compact ? 'group relative ' : 'pt-1'
|
||||
)}
|
||||
>
|
||||
{compact && (
|
||||
<time className="absolute -left-2 top-0 -translate-x-full text-gray-300 text-xs invisible group-hover:visible">{`${new Date(created_at)
|
||||
<time className="absolute -left-2 top-1/2 -translate-x-full -translate-y-1/2 text-gray-300 text-xs invisible group-hover:visible">{`${new Date(created_at)
|
||||
.toLocaleTimeString('en-US', {
|
||||
second: 'numeric',
|
||||
minute: 'numeric',
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useAppSelector } from '../../app/store';
|
||||
import Login from './Login';
|
||||
|
||||
const TextMessage = ({ text, animate = '' }: { text: string; animate?: string }) => (
|
||||
<p className={`text-gray-600 text-md mb-3 ${animate}`}>{text}</p>
|
||||
<p className={`text-gray-600 text-md mb-1 ${animate}`}>{text}</p>
|
||||
);
|
||||
type Props = {
|
||||
needLogin?: boolean
|
||||
@@ -12,20 +12,20 @@ type Props = {
|
||||
const Index = ({ needLogin = false }: Props) => {
|
||||
const { name, logo } = useAppSelector(store => store.server);
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<div className="flex gap-1 pl-1">
|
||||
<div className="w-12 h-12">
|
||||
<img src={logo} alt="logo" className="w-full h-full" />
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="flex items-center gap-2 leading-6">
|
||||
<em className="text-black not-italic font-bold text-lg">{name}</em>
|
||||
<em className="text-black not-italic text-lg font-bold">{name}</em>
|
||||
<time className="text-gray-300 text-sm">{`${new Date().toLocaleTimeString('en-US', {
|
||||
minute: 'numeric',
|
||||
hour: 'numeric',
|
||||
hour12: true,
|
||||
})}`}</time>
|
||||
</span>
|
||||
<TextMessage text="👋 Hi there, Nice to meet you!" animate="animate-[fadeInUp_.5s_ease-in-out_both]" />
|
||||
<TextMessage text="Hi there, Nice to meet you! 👋👋👋" animate={needLogin ? "animate-[fadeInUp_.5s_ease-in-out_both]" : ''} />
|
||||
{needLogin && <>
|
||||
<TextMessage text="You need login before we have a nice talk 👇" animate="animate-[fadeInUp_.5s_.8s_ease-in-out_both]" />
|
||||
<div className="animate-[fadeInUp_.5s_1.2s_ease-in-out_both]">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useRef, ChangeEvent } from 'react';
|
||||
import { useState, ChangeEvent } from 'react';
|
||||
import Header from './Header';
|
||||
import Welcome from './Welcome';
|
||||
import MessageFeed from './MessageFeed';
|
||||
@@ -33,7 +33,7 @@ const Index = ({ handleClose, hostId }: Props) => {
|
||||
// no token or guest login
|
||||
const notLogin = !token || isGuest;
|
||||
return (
|
||||
<aside className="flex flex-col justify-between bg-white w-full h-full rounded-md overflow-hidden">
|
||||
<aside className="flex flex-col justify-between bg-white w-[600px] h-[800px] rounded-md overflow-hidden">
|
||||
<Header handleClose={handleClose} />
|
||||
<Line />
|
||||
{/* message list */}
|
||||
@@ -45,10 +45,11 @@ const Index = ({ handleClose, hostId }: Props) => {
|
||||
{/* message input */}
|
||||
<div className="w-full px-2 py-3">
|
||||
<textarea
|
||||
disabled={notLogin}
|
||||
value={input}
|
||||
onChange={handleInput}
|
||||
className="w-full h-full text-sm p-2 rounded-lg bg-gray-200 resize-none text-black outline-none"
|
||||
placeholder="Write a message..."
|
||||
placeholder={notLogin ? "Login first..." : "Write a message..."}
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useState } from "react";
|
||||
import { useGetServerQuery } from "../app/services/server";
|
||||
|
||||
import Icon from "./Icon";
|
||||
@@ -10,18 +10,16 @@ type Props = {
|
||||
|
||||
function Widget({ hostId }: Props) {
|
||||
const { rehydrated } = usePreload();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [visible, setVisible] = useState(!!new URLSearchParams(location.search).get("open"));
|
||||
const { isLoading, isError } = useGetServerQuery();
|
||||
const toggleVisible = () => {
|
||||
setVisible((prev) => !prev);
|
||||
};
|
||||
useEffect(() => {
|
||||
// 有无iframe内嵌
|
||||
const parentWindow = window.parent;
|
||||
if (parentWindow) {
|
||||
parentWindow.postMessage(visible ? 'OPEN' : 'CLOSE', '*');
|
||||
parentWindow.postMessage(visible ? 'CLOSE' : 'OPEN', '*');
|
||||
}
|
||||
}, [visible]);
|
||||
setVisible((prev) => !prev);
|
||||
};
|
||||
if (isLoading || isError || !rehydrated) return null;
|
||||
return visible ? <Popup handleClose={toggleVisible} hostId={hostId} /> : <Icon handleClick={toggleVisible} />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user