chore: add usage into heatmap (#1443)

This commit is contained in:
boojack
2023-04-02 11:56:09 +08:00
committed by GitHub
parent 1ea65c0b60
commit d71bfce1a0
9 changed files with 86 additions and 102 deletions

View File

@ -474,7 +474,6 @@ const MemoEditor = () => {
disabled={!(allowSave || editorState.resourceList.length > 0) || state.isUploadingResource || state.isRequesting} disabled={!(allowSave || editorState.resourceList.length > 0) || state.isUploadingResource || state.isRequesting}
onClick={handleSaveBtnClick} onClick={handleSaveBtnClick}
> >
<img className="w-5 -ml-0.5 mr-0.5 h-auto" src="/logo.webp" />
{t("editor.save")} {t("editor.save")}
</button> </button>
</div> </div>

View File

@ -14,14 +14,12 @@ interface Props extends DialogProps {
} }
interface State { interface State {
angle: number;
scale: number; scale: number;
originX: number; originX: number;
originY: number; originY: number;
} }
const defaultState: State = { const defaultState: State = {
angle: 0,
scale: 1, scale: 1,
originX: -1, originX: -1,
originY: -1, originY: -1,
@ -104,36 +102,22 @@ const PreviewImageDialog: React.FC<Props> = ({ destroy, imgUrls, initialIndex }:
} }
}; };
const handleImgRotate = (event: React.MouseEvent, angle: number) => {
const curImgAngle = (state.angle + angle + 360) % 360;
setState({
...state,
originX: -1,
originY: -1,
angle: curImgAngle,
});
};
const handleImgContainerScroll = (event: React.WheelEvent) => { const handleImgContainerScroll = (event: React.WheelEvent) => {
const offsetX = event.nativeEvent.offsetX; const offsetX = event.nativeEvent.offsetX;
const offsetY = event.nativeEvent.offsetY; const offsetY = event.nativeEvent.offsetY;
const sign = event.deltaY < 0 ? 1 : -1; const sign = event.deltaY < 0 ? 1 : -1;
const curAngle = Math.max(MIN_SCALE, Math.min(MAX_SCALE, state.scale + sign * SCALE_UNIT)); const scale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, state.scale + sign * SCALE_UNIT));
setState({ setState({
...state, ...state,
originX: offsetX, originX: offsetX,
originY: offsetY, originY: offsetY,
scale: curAngle, scale: scale,
}); });
}; };
const getImageComputedStyle = () => { const imageComputedStyle = {
return { transform: `scale(${state.scale})`,
transform: `scale(${state.scale}) rotate(${state.angle}deg)`, transformOrigin: `${state.originX === -1 ? "center" : `${state.originX}px`} ${state.originY === -1 ? "center" : `${state.originY}px`}`,
transformOrigin: `${state.originX === -1 ? "center" : `${state.originX}px`} ${
state.originY === -1 ? "center" : `${state.originY}px`
}`,
};
}; };
return ( return (
@ -145,22 +129,16 @@ const PreviewImageDialog: React.FC<Props> = ({ destroy, imgUrls, initialIndex }:
<button className="btn" onClick={handleDownloadBtnClick}> <button className="btn" onClick={handleDownloadBtnClick}>
<Icon.Download className="icon-img" /> <Icon.Download className="icon-img" />
</button> </button>
<button className="btn" onClick={(e) => handleImgRotate(e, -90)}>
<Icon.RotateCcw className="icon-img" />
</button>
<button className="btn" onClick={(e) => handleImgRotate(e, 90)}>
<Icon.RotateCw className="icon-img" />
</button>
</div> </div>
<div className="img-container" onClick={handleImgContainerClick}> <div className="img-container" onClick={handleImgContainerClick}>
<img <img
style={imageComputedStyle}
src={imgUrls[currentIndex]}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
onTouchStart={handleTouchStart} onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove} onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd} onTouchEnd={handleTouchEnd}
src={imgUrls[currentIndex]}
onWheel={handleImgContainerScroll} onWheel={handleImgContainerScroll}
style={getImageComputedStyle()}
/> />
</div> </div>
</> </>

View File

@ -131,7 +131,7 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
</div> </div>
<div className="watermark-container"> <div className="watermark-container">
<div className="logo-container"> <div className="logo-container">
<img className="logo-img" src={`${systemStatus.customizedProfile.logoUrl || "/logo.webp"}`} alt="" /> <img className="h-10 w-auto rounded-lg" src={`${systemStatus.customizedProfile.logoUrl || "/logo.webp"}`} alt="" />
</div> </div>
<div className="userinfo-container"> <div className="userinfo-container">
<span className="name-text">{user.nickname || user.username}</span> <span className="name-text">{user.nickname || user.username}</span>
@ -141,10 +141,9 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
</div> </div>
<QRCodeSVG <QRCodeSVG
value={`${window.location.origin}/m/${memo.id}`} value={`${window.location.origin}/m/${memo.id}`}
size={64} size={40}
bgColor={"#F3F4F6"} bgColor={"#F3F4F6"}
fgColor={"#4B5563"} fgColor={"#4B5563"}
level={"L"}
includeMargin={false} includeMargin={false}
/> />
</div> </div>
@ -166,17 +165,17 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
))} ))}
</Select> </Select>
<div className="flex flex-row justify-end items-center"> <div className="flex flex-row justify-end items-center">
<button disabled={createLoadingState.isLoading} className="btn-normal mr-2" onClick={handleDownloadBtnClick}> <button disabled={createLoadingState.isLoading} className="btn-normal h-8 mr-2" onClick={handleDownloadBtnClick}>
{createLoadingState.isLoading ? ( {createLoadingState.isLoading ? (
<Icon.Loader className="w-4 h-auto mr-1 animate-spin" /> <Icon.Loader className="w-4 h-auto sm:mr-1 animate-spin" />
) : ( ) : (
<Icon.Download className="w-4 h-auto mr-1" /> <Icon.Download className="w-4 h-auto sm:mr-1" />
)} )}
<span>{t("common.image")}</span> <span className="hidden sm:block">{t("common.image")}</span>
</button> </button>
<button className="btn-normal" onClick={handleCopyLinkBtnClick}> <button className="btn-normal h-8" onClick={handleCopyLinkBtnClick}>
<Icon.Link className="w-4 h-auto mr-1" /> <Icon.Link className="w-4 h-auto sm:mr-1" />
<span>{t("common.link")}</span> <span className="hidden sm:block">{t("common.link")}</span>
</button> </button>
</div> </div>
</div> </div>

View File

@ -36,7 +36,7 @@ const ShortcutList = () => {
}, []); }, []);
return ( return (
<div className="flex flex-col justify-start items-start w-full py-0 px-1 mt-2 h-auto shrink-0 flex-nowrap hide-scrollbar"> <div className="flex flex-col justify-start items-start w-full mt-2 h-auto shrink-0 flex-nowrap hide-scrollbar">
<div className="flex flex-row justify-start items-center w-full px-4"> <div className="flex flex-row justify-start items-center w-full px-4">
<span className="text-sm leading-6 font-mono text-gray-400">{t("common.shortcuts")}</span> <span className="text-sm leading-6 font-mono text-gray-400">{t("common.shortcuts")}</span>
<button <button

View File

@ -68,7 +68,7 @@ const TagList = () => {
}, [tagsText]); }, [tagsText]);
return ( return (
<div className="flex flex-col justify-start items-start w-full py-0 px-1 mt-2 h-auto shrink-0 flex-nowrap hide-scrollbar"> <div className="flex flex-col justify-start items-start w-full mt-2 h-auto shrink-0 flex-nowrap hide-scrollbar">
<div className="flex flex-row justify-start items-center w-full px-4"> <div className="flex flex-row justify-start items-center w-full px-4">
<span className="text-sm leading-6 font-mono text-gray-400">{t("common.tags")}</span> <span className="text-sm leading-6 font-mono text-gray-400">{t("common.tags")}</span>
<button <button

View File

@ -38,13 +38,23 @@ const UsageHeatMap = () => {
const usedDaysAmount = (tableConfig.width - 1) * tableConfig.height + todayDay; const usedDaysAmount = (tableConfig.width - 1) * tableConfig.height + todayDay;
const beginDayTimestamp = todayTimeStamp - usedDaysAmount * DAILY_TIMESTAMP; const beginDayTimestamp = todayTimeStamp - usedDaysAmount * DAILY_TIMESTAMP;
const memos = memoStore.state.memos; const memos = memoStore.state.memos;
const [memoAmount, setMemoAmount] = useState(0);
const [createdDays, setCreatedDays] = useState(0);
const [allStat, setAllStat] = useState<DailyUsageStat[]>(getInitialUsageStat(usedDaysAmount, beginDayTimestamp)); const [allStat, setAllStat] = useState<DailyUsageStat[]>(getInitialUsageStat(usedDaysAmount, beginDayTimestamp));
const [currentStat, setCurrentStat] = useState<DailyUsageStat | null>(null); const [currentStat, setCurrentStat] = useState<DailyUsageStat | null>(null);
const containerElRef = useRef<HTMLDivElement>(null); const containerElRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!userStore.state.user) {
return;
}
setCreatedDays(Math.ceil((Date.now() - utils.getTimeStampByDate(userStore.state.user.createdTs)) / 1000 / 3600 / 24));
}, [userStore.state.user]);
useEffect(() => { useEffect(() => {
getMemoStats(userStore.getCurrentUserId()) getMemoStats(userStore.getCurrentUserId())
.then(({ data: { data } }) => { .then(({ data: { data } }) => {
setMemoAmount(data.length);
const newStat: DailyUsageStat[] = getInitialUsageStat(usedDaysAmount, beginDayTimestamp); const newStat: DailyUsageStat[] = getInitialUsageStat(usedDaysAmount, beginDayTimestamp);
for (const record of data) { for (const record of data) {
const index = (utils.getDateStampByDate(record * 1000) - beginDayTimestamp) / (1000 * 3600 * 24) - 1; const index = (utils.getDateStampByDate(record * 1000) - beginDayTimestamp) / (1000 * 3600 * 24) - 1;
@ -97,6 +107,7 @@ const UsageHeatMap = () => {
}, []); }, []);
return ( return (
<>
<div className="usage-heat-map-wrapper" ref={containerElRef}> <div className="usage-heat-map-wrapper" ref={containerElRef}>
<div className="usage-heat-map"> <div className="usage-heat-map">
{allStat.map((v, i) => { {allStat.map((v, i) => {
@ -144,6 +155,11 @@ const UsageHeatMap = () => {
<span className="tip-text">{t("days.sat")}</span> <span className="tip-text">{t("days.sat")}</span>
</div> </div>
</div> </div>
<p className="w-full pl-4 text-xs -mt-2 mb-3 text-gray-400 dark:text-zinc-400">
<span className="font-medium text-gray-500 dark:text-zinc-300">{memoAmount}</span> memos in{" "}
<span className="font-medium text-gray-500 dark:text-zinc-300">{createdDays}</span> days
</p>
</>
); );
}; };

View File

@ -56,7 +56,7 @@
} }
> .watermark-container { > .watermark-container {
@apply flex flex-row justify-between items-center w-full bg-gray-100 dark:bg-zinc-700 py-2 px-6; @apply flex flex-row justify-between items-center w-full bg-gray-100 dark:bg-zinc-700 py-3 px-6;
> .userinfo-container { > .userinfo-container {
@apply w-auto grow truncate flex mr-2 flex-col justify-center items-start; @apply w-auto grow truncate flex mr-2 flex-col justify-center items-start;
@ -70,7 +70,7 @@
} }
} }
> .logo-container{ > .logo-container {
@apply mr-2; @apply mr-2;
> .logo-img { > .logo-img {

View File

@ -53,7 +53,7 @@ const MemoDetail = () => {
<div className="page-container"> <div className="page-container">
<div className="page-header"> <div className="page-header">
<div className="title-container"> <div className="title-container">
<img className="logo-img" src={customizedProfile.logoUrl} alt="" /> <img className="h-10 w-auto rounded-lg mr-2" src={customizedProfile.logoUrl} alt="" />
<p className="logo-text">{customizedProfile.name}</p> <p className="logo-text">{customizedProfile.name}</p>
</div> </div>
<div className="action-button-container"> <div className="action-button-container">

View File

@ -346,20 +346,12 @@ const ResourcesDashboard = () => {
</div> </div>
)} )}
</div> </div>
<div className="flex flex-col justify-start items-center w-full my-6"> <div className="flex flex-col justify-start items-center w-full">
<p className="text-sm text-gray-400 italic"> <p className="text-sm text-gray-400 italic">
{isComplete ? ( {!isComplete && (
resources.length === 0 ? ( <span className="cursor-pointer my-6 hover:text-green-600" onClick={handleFetchMoreResourceBtnClick}>
t("message.no-resource")
) : (
t("message.resource-ready")
)
) : (
<>
<span className="cursor-pointer hover:text-green-600" onClick={handleFetchMoreResourceBtnClick}>
{t("memo-list.fetch-more")} {t("memo-list.fetch-more")}
</span> </span>
</>
)} )}
</p> </p>
</div> </div>