Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(flat-pages): drop/paste images to temporary storage #1947

Merged
merged 2 commits into from
Jun 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,13 @@ const areAllSupportedFiles = (files: FileList): boolean => {
return Array.from(files).every(file => isSupportedFile(file));
};

const onDragOver = (event: React.DragEvent<HTMLDivElement>): void => {
event.preventDefault();
if (areAllSupportedFiles(event.dataTransfer.files)) {
event.dataTransfer.dropEffect = "copy";
}
};

/** CloudStorage page with MobX Store */
export const CloudStorageContainer = /* @__PURE__ */ observer<CloudStorageContainerProps>(
function CloudStorageContainer({ store, path, pushHistory }) {
const t = useTranslate();
const cloudStorageContainerRef = useRef<HTMLDivElement>(null);
const [skeletonsVisible, setSkeletonsVisible] = useState(false);
const [tipsVisible, setTipsVisible] = useState(false);
const [isAtTheBottom, setIsAtTheBottom] = useState(false);

// Wait 200ms before showing skeletons to reduce flashing.
Expand All @@ -75,12 +69,25 @@ export const CloudStorageContainer = /* @__PURE__ */ observer<CloudStorageContai
}
}, [isAtTheBottom, store]);

const onDragOver = useCallback((event: React.DragEvent<HTMLDivElement>): void => {
event.preventDefault();
if (areAllSupportedFiles(event.dataTransfer.files)) {
event.dataTransfer.dropEffect = "copy";
setTipsVisible(true);
}
}, []);

const onDragLeave = useCallback((): void => {
setTipsVisible(false);
}, []);

const onDrop = useCallback(
(event: React.DragEvent<HTMLDivElement>): void => {
event.preventDefault();
if (areAllSupportedFiles(event.dataTransfer.files)) {
store.onDropFile(event.dataTransfer.files);
}
setTipsVisible(false);
},
[store],
);
Expand Down Expand Up @@ -159,7 +166,12 @@ export const CloudStorageContainer = /* @__PURE__ */ observer<CloudStorageContai
};

return (
<div className="cloud-storage-container" onDragOver={onDragOver} onDrop={onDrop}>
<div
className="cloud-storage-container"
onDragLeave={onDragLeave}
onDragOver={onDragOver}
onDrop={onDrop}
>
{!store.compact && (
<div className="cloud-storage-container-head">
<div>
Expand Down Expand Up @@ -233,6 +245,13 @@ export const CloudStorageContainer = /* @__PURE__ */ observer<CloudStorageContai
{store.compact && (
<div className="cloud-storage-container-footer">{containerBtns}</div>
)}
{tipsVisible && (
<div className="cloud-storage-container-tips">
<div className="cloud-storage-container-tips-content">
{t("drop-to-storage")}
</div>
</div>
)}
</div>
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,23 @@
align-items: center;
margin-left: auto;

& > * {
&>* {
margin-left: 1em !important;
}

.ant-btn-group > .ant-btn:first-child:not(:last-child),
.ant-btn-group > span:first-child:not(:last-child) > .ant-btn {
.ant-btn-group>.ant-btn:first-child:not(:last-child),
.ant-btn-group>span:first-child:not(:last-child)>.ant-btn {
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}

.ant-btn-group > .ant-btn:last-child:not(:first-child),
.ant-btn-group>.ant-btn:last-child:not(:first-child),
.ant-btn-group .ant-btn-primary:not(:first-child):not(:last-child) {
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}

& > button {
&>button {
border-radius: 5px;
}

Expand All @@ -77,18 +77,20 @@
display: inline-block;
margin-left: 14px;

> span {
>span {
position: absolute;
top: 10px;
width: 9px;
height: 1px;
background-color: var(--grey-6);
display: inline-block;
transition: all .2s ease;

&:first-of-type {
left: 0;
transform: rotate(45deg);
}

&:last-of-type {
right: 0;
transform: rotate(-45deg);
Expand All @@ -104,11 +106,12 @@
.cloud-storage-container-btns .ant-dropdown:hover,
.cloud-storage-container-dropdown-btn:hover {
.cloud-storage-container-btn-arrow {
> span {
>span {
&:first-of-type {
background-color: var(--primary);
transform: rotate(-45deg);
}

&:last-of-type {
background-color: var(--primary);
transform: rotate(45deg);
Expand All @@ -117,11 +120,12 @@
}

.cloud-storage-container-btn-arrow-primary {
> span {
>span {
&:first-of-type {
background-color: #fff;
transform: rotate(-45deg);
}

&:last-of-type {
background-color: #fff;
transform: rotate(45deg);
Expand All @@ -130,8 +134,8 @@
}
}

.cloud-storage-container-btn-arrow-primary:extend(.cloud-storage-container-btn-arrow){
> span {
.cloud-storage-container-btn-arrow-primary:extend(.cloud-storage-container-btn-arrow) {
>span {
background-color: #fff;
}
}
Expand Down Expand Up @@ -184,18 +188,59 @@
.cloud-storage-container-mask-enter {
opacity: 0;
}

.cloud-storage-container-mask-enter-active {
opacity: 1;
transition: opacity 0.4s;
}

.cloud-storage-container-mask-exit {
opacity: 1;
}

.cloud-storage-container-mask-exit-active {
opacity: 0;
transition: opacity 0.4s;
}

.cloud-storage-container-tips {
position: absolute;
z-index: 100;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, .75);
pointer-events: none;
}

.cloud-storage-container-tips-content {
position: absolute;
bottom: 24px;
left: 50%;
transform: translateX(-50%);
padding: 6px 16px;
border-radius: 4px;
background: var(--primary);
color: #fff;
opacity: .95;
animation: bouncing 1s infinite cubic-bezier(0.5, 1, 0.89, 1);
}

@keyframes bouncing {
0% {
transform: translateX(-50%) translateY(0);
}

50% {
transform: translateX(-50%) translateY(-10px);
}

100% {
transform: translateX(-50%) translateY(0);
}
}

.flat-color-scheme-dark {
.cloud-storage-container-footer {
border-top-color: var(--grey-8);
Expand All @@ -215,8 +260,12 @@
}

.cloud-storage-container-btn-arrow {
> span {
>span {
background-color: var(--grey-3);
}
}
}

.cloud-storage-container-tips {
background: rgba(0, 0, 0, .75);
}
}
6 changes: 5 additions & 1 deletion packages/flat-i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@
"pencil-tail": "Pencil tail"
},
"upload-avatar-size-limit": "Avatar size should be less than 5MB",
"upload-image-size-limit": "Image size should be less than 5MB",
"applications": "Applications",
"developer": "Developer",
"oauth-apps": "OAuth Apps",
Expand Down Expand Up @@ -605,6 +606,7 @@
"teacher-has-turn-off-camera": "Teacher has turned your camera off",
"teacher-has-turn-off-mic": "Teacher has turned your microphone off",
"upload-concurrent-limit": "You have reached the upload concurrent limit",
"upload-image-concurrent-limit": "You have reached the upload limit of screenshot images",
"file-is-too-big": "File is too big",
"file-not-found": "File not found",
"file-already-exists": "File already exists",
Expand Down Expand Up @@ -679,5 +681,7 @@
"teal": "Teal",
"grey": "Grey",
"green": "Green"
}
},
"drop-to-storage": "Drop file to cloud storage",
"drop-to-board": "Drop file to whiteboard"
}
6 changes: 5 additions & 1 deletion packages/flat-i18n/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@
"pencil-tail": "开启铅笔笔锋"
},
"upload-avatar-size-limit": "头像大小不能超过 5MB",
"upload-image-size-limit": "图片大小不能超过 5MB",
"applications": "应用管理",
"developer": "开发者设置",
"oauth-apps": "OAuth 应用",
Expand Down Expand Up @@ -605,6 +606,7 @@
"teacher-has-turn-off-camera": "老师已关闭你的摄像头",
"teacher-has-turn-off-mic": "老师已关闭你的麦克风",
"upload-concurrent-limit": "上传并发数达到上限",
"upload-image-concurrent-limit": "今日上传截图数达到上限",
"file-is-too-big": "文件过大",
"file-not-found": "文件不存在",
"file-already-exists": "文件已存在",
Expand Down Expand Up @@ -679,5 +681,7 @@
"teal": "藏青",
"grey": "浅灰",
"green": "墨绿"
}
},
"drop-to-storage": "拖拽文件到云盘",
"drop-to-board": "拖拽文件到白板"
}
37 changes: 37 additions & 0 deletions packages/flat-pages/src/components/Whiteboard.less
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,43 @@
opacity: 0.9;
}

.whiteboard-container-tips {
position: absolute;
z-index: 100;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}

.whiteboard-container-tips-content {
position: absolute;
bottom: 24px;
left: 50%;
transform: translateX(-50%);
padding: 6px 16px;
border-radius: 4px;
background: var(--primary);
color: #fff;
opacity: .95;
animation: bouncing 1s infinite cubic-bezier(0.5, 1, 0.89, 1);
}

@keyframes bouncing {
0% {
transform: translateX(-50%) translateY(0);
}

50% {
transform: translateX(-50%) translateY(-10px);
}

100% {
transform: translateX(-50%) translateY(0);
}
}

.is-readonly .whiteboard-scroll-page {
opacity: 0;
}
Expand Down
15 changes: 15 additions & 0 deletions packages/flat-pages/src/components/Whiteboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const Whiteboard = observer<WhiteboardProps>(function Whiteboard({
const [page, setPage] = useState(0);
const [maxPage, setMaxPage] = useState(Infinity);
const [showPage, setShowPage] = useState(false);
const [tipsVisible, setTipsVisible] = useState(false);

const isReconnecting = phase === RoomPhase.Reconnecting;
const handRaisingCount = classRoomStore.users.handRaisingJoiners.length;
Expand Down Expand Up @@ -123,6 +124,7 @@ export const Whiteboard = observer<WhiteboardProps>(function Whiteboard({
const onDragOver = useCallback(
(event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
setTipsVisible(true);
const file = event.dataTransfer.files[0];
if (room && file && isSupportedFileExt(file)) {
event.dataTransfer.dropEffect = "copy";
Expand All @@ -131,9 +133,14 @@ export const Whiteboard = observer<WhiteboardProps>(function Whiteboard({
[room],
);

const onDragLeave = useCallback(() => {
setTipsVisible(false);
}, []);

const onDrop = useCallback(
async (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
setTipsVisible(false);
const file = event.dataTransfer.files[0];
if (room && file) {
if (isSupportedImageType(file)) {
Expand Down Expand Up @@ -196,6 +203,7 @@ export const Whiteboard = observer<WhiteboardProps>(function Whiteboard({
className={classNames("whiteboard-container", {
"is-readonly": !whiteboardStore.allowDrawing,
})}
onDragLeave={onDragLeave}
onDragOver={onDragOver}
onDrop={onDrop}
>
Expand Down Expand Up @@ -271,6 +279,13 @@ export const Whiteboard = observer<WhiteboardProps>(function Whiteboard({
<div className="hand-raising-empty">{t("no-one-raising-hand")}</div>
)}
</div>
{tipsVisible && (
<div className="whiteboard-container-tips">
<div className="whiteboard-container-tips-content">
{t("drop-to-board")}
</div>
</div>
)}
</div>
)}
<SaveAnnotationModal
Expand Down
Loading