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
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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}
onClick={handleSaveBtnClick}
>
<img className="w-5 -ml-0.5 mr-0.5 h-auto" src="/logo.webp" />
{t("editor.save")}
</button>
</div>

View file

@ -14,14 +14,12 @@ interface Props extends DialogProps {
}
interface State {
angle: number;
scale: number;
originX: number;
originY: number;
}
const defaultState: State = {
angle: 0,
scale: 1,
originX: -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 offsetX = event.nativeEvent.offsetX;
const offsetY = event.nativeEvent.offsetY;
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({
...state,
originX: offsetX,
originY: offsetY,
scale: curAngle,
scale: scale,
});
};
const getImageComputedStyle = () => {
return {
transform: `scale(${state.scale}) rotate(${state.angle}deg)`,
transformOrigin: `${state.originX === -1 ? "center" : `${state.originX}px`} ${
state.originY === -1 ? "center" : `${state.originY}px`
}`,
};
const imageComputedStyle = {
transform: `scale(${state.scale})`,
transformOrigin: `${state.originX === -1 ? "center" : `${state.originX}px`} ${state.originY === -1 ? "center" : `${state.originY}px`}`,
};
return (
@ -145,22 +129,16 @@ const PreviewImageDialog: React.FC<Props> = ({ destroy, imgUrls, initialIndex }:
<button className="btn" onClick={handleDownloadBtnClick}>
<Icon.Download className="icon-img" />
</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 className="img-container" onClick={handleImgContainerClick}>
<img
style={imageComputedStyle}
src={imgUrls[currentIndex]}
onClick={(e) => e.stopPropagation()}
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}
src={imgUrls[currentIndex]}
onWheel={handleImgContainerScroll}
style={getImageComputedStyle()}
/>
</div>
</>

View file

@ -131,7 +131,7 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
</div>
<div className="watermark-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 className="userinfo-container">
<span className="name-text">{user.nickname || user.username}</span>
@ -141,10 +141,9 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
</div>
<QRCodeSVG
value={`${window.location.origin}/m/${memo.id}`}
size={64}
size={40}
bgColor={"#F3F4F6"}
fgColor={"#4B5563"}
level={"L"}
includeMargin={false}
/>
</div>
@ -166,17 +165,17 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
))}
</Select>
<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 ? (
<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 className="btn-normal" onClick={handleCopyLinkBtnClick}>
<Icon.Link className="w-4 h-auto mr-1" />
<span>{t("common.link")}</span>
<button className="btn-normal h-8" onClick={handleCopyLinkBtnClick}>
<Icon.Link className="w-4 h-auto sm:mr-1" />
<span className="hidden sm:block">{t("common.link")}</span>
</button>
</div>
</div>

View file

@ -36,7 +36,7 @@ const ShortcutList = () => {
}, []);
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">
<span className="text-sm leading-6 font-mono text-gray-400">{t("common.shortcuts")}</span>
<button

View file

@ -68,7 +68,7 @@ const TagList = () => {
}, [tagsText]);
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">
<span className="text-sm leading-6 font-mono text-gray-400">{t("common.tags")}</span>
<button

View file

@ -38,13 +38,23 @@ const UsageHeatMap = () => {
const usedDaysAmount = (tableConfig.width - 1) * tableConfig.height + todayDay;
const beginDayTimestamp = todayTimeStamp - usedDaysAmount * DAILY_TIMESTAMP;
const memos = memoStore.state.memos;
const [memoAmount, setMemoAmount] = useState(0);
const [createdDays, setCreatedDays] = useState(0);
const [allStat, setAllStat] = useState<DailyUsageStat[]>(getInitialUsageStat(usedDaysAmount, beginDayTimestamp));
const [currentStat, setCurrentStat] = useState<DailyUsageStat | null>(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(() => {
getMemoStats(userStore.getCurrentUserId())
.then(({ data: { data } }) => {
setMemoAmount(data.length);
const newStat: DailyUsageStat[] = getInitialUsageStat(usedDaysAmount, beginDayTimestamp);
for (const record of data) {
const index = (utils.getDateStampByDate(record * 1000) - beginDayTimestamp) / (1000 * 3600 * 24) - 1;
@ -97,53 +107,59 @@ const UsageHeatMap = () => {
}, []);
return (
<div className="usage-heat-map-wrapper" ref={containerElRef}>
<div className="usage-heat-map">
{allStat.map((v, i) => {
const count = v.count;
const colorLevel =
count <= 0
? ""
: count <= 1
? "stat-day-l1-bg"
: count <= 2
? "stat-day-l2-bg"
: count <= 4
? "stat-day-l3-bg"
: "stat-day-l4-bg";
<>
<div className="usage-heat-map-wrapper" ref={containerElRef}>
<div className="usage-heat-map">
{allStat.map((v, i) => {
const count = v.count;
const colorLevel =
count <= 0
? ""
: count <= 1
? "stat-day-l1-bg"
: count <= 2
? "stat-day-l2-bg"
: count <= 4
? "stat-day-l3-bg"
: "stat-day-l4-bg";
return (
<div
className="stat-wrapper"
key={i}
onMouseEnter={(e) => handleUsageStatItemMouseEnter(e, v)}
onMouseLeave={handleUsageStatItemMouseLeave}
onClick={() => handleUsageStatItemClick(v)}
>
<span
className={`stat-container ${colorLevel} ${currentStat === v ? "current" : ""} ${
todayTimeStamp === v.timestamp ? "today" : ""
}`}
></span>
return (
<div
className="stat-wrapper"
key={i}
onMouseEnter={(e) => handleUsageStatItemMouseEnter(e, v)}
onMouseLeave={handleUsageStatItemMouseLeave}
onClick={() => handleUsageStatItemClick(v)}
>
<span
className={`stat-container ${colorLevel} ${currentStat === v ? "current" : ""} ${
todayTimeStamp === v.timestamp ? "today" : ""
}`}
></span>
</div>
);
})}
{nullCell.map((_, i) => (
<div className="stat-wrapper" key={i}>
<span className="stat-container null"></span>
</div>
);
})}
{nullCell.map((_, i) => (
<div className="stat-wrapper" key={i}>
<span className="stat-container null"></span>
</div>
))}
))}
</div>
<div className="day-tip-text-container">
<span className="tip-text">{t("days.sun")}</span>
<span className="tip-text"></span>
<span className="tip-text">{t("days.tue")}</span>
<span className="tip-text"></span>
<span className="tip-text">{t("days.thu")}</span>
<span className="tip-text"></span>
<span className="tip-text">{t("days.sat")}</span>
</div>
</div>
<div className="day-tip-text-container">
<span className="tip-text">{t("days.sun")}</span>
<span className="tip-text"></span>
<span className="tip-text">{t("days.tue")}</span>
<span className="tip-text"></span>
<span className="tip-text">{t("days.thu")}</span>
<span className="tip-text"></span>
<span className="tip-text">{t("days.sat")}</span>
</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 {
@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 {
@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;
> .logo-img {

View file

@ -53,7 +53,7 @@ const MemoDetail = () => {
<div className="page-container">
<div className="page-header">
<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>
</div>
<div className="action-button-container">

View file

@ -346,20 +346,12 @@ const ResourcesDashboard = () => {
</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">
{isComplete ? (
resources.length === 0 ? (
t("message.no-resource")
) : (
t("message.resource-ready")
)
) : (
<>
<span className="cursor-pointer hover:text-green-600" onClick={handleFetchMoreResourceBtnClick}>
{t("memo-list.fetch-more")}
</span>
</>
{!isComplete && (
<span className="cursor-pointer my-6 hover:text-green-600" onClick={handleFetchMoreResourceBtnClick}>
{t("memo-list.fetch-more")}
</span>
)}
</p>
</div>