rewrite component & support additions uploads

This commit is contained in:
srgooglo 2022-06-06 16:09:54 +02:00
parent a0f641957f
commit 49999e8acb
2 changed files with 379 additions and 95 deletions

View File

@ -3,45 +3,159 @@ import * as antd from "antd"
import { Icons } from "components/Icons" import { Icons } from "components/Icons"
import { User } from "models" import { User } from "models"
import classnames from "classnames" import classnames from "classnames"
import { PostAdditions } from "components/PostCard"
import "./index.less" import "./index.less"
// TODO: Fetch `maxMessageLength` value from server API
const maxMessageLength = 512 const maxMessageLength = 512
const PostCreatorInput = (props) => { export default (props) => {
const [value, setValue] = React.useState("") const api = window.app.request
const [loading, setLoading] = React.useState(false)
const [uploaderVisible, setUploaderVisible] = React.useState(false)
const [focused, setFocused] = React.useState(false)
const [userData, setUserData] = React.useState(null)
const [postData, setPostData] = React.useState({
message: "",
additions: []
})
const updatePostData = (update) => {
setPostData({
...postData,
...update
})
}
const cleanPostData = () => {
setPostData({
message: "",
additions: []
})
}
const submit = () => {
setLoading(true)
const response = api.put.post({ ...postData }).catch(error => {
console.error(error)
antd.message.error(error)
return false
})
setLoading(false)
if (response) {
cleanPostData()
}
}
const onUploadFile = async (req) => {
// get file data
const file = req.file
// append to form data
const formData = new FormData()
formData.append("files", file)
setLoading(true)
// send request
const request = await api.post.upload(formData, undefined).catch((error) => {
console.error(error)
antd.message.error(error)
req.onError(error)
return false
})
setLoading(false)
if (request) {
return req.onSuccess(request)
}
}
const canPublish = () => { const canPublish = () => {
return value.length !== 0 && value.length < maxMessageLength const messageLengthValid = postData.message.length !== 0 && postData.message.length < maxMessageLength
return Boolean(messageLengthValid)
} }
const onChange = (e) => { const onDraggerChange = (change) => {
setValue(e.target.value) console.log(change)
switch (change.file.status) {
case "done": {
let additions = postData.additions ?? []
additions.push(...change.file.response)
return updatePostData({ additions })
} }
const handleSubmit = () => { default: {
if (canPublish()) { break
if (typeof props.onSubmit === "function") {
props.onSubmit(value)
} }
setValue("")
} }
} }
return <div className="textInput"> const onChangeMessageInput = (event) => {
console.log(event.target.value)
updatePostData({
message: event.target.value
})
}
const toggleUploader = (to) => {
setUploaderVisible(to ?? !uploaderVisible)
}
const toggleFocus = (to) => {
setFocused(to ?? !focused)
}
React.useEffect(() => {
User.data().then(user => {
setUserData(user)
})
}, [])
return <div
className="postCreator"
onDragOver={(e) => {
e.preventDefault()
toggleUploader(true)
}}
onDragLeave={(e) => {
e.preventDefault()
toggleUploader(false)
}}
onMouseEnter={() => {
toggleFocus(true)
}}
onMouseLeave={() => {
toggleFocus(false)
}}
>
<div className="textInput">
<div className="avatar"> <div className="avatar">
<img src={props.user?.avatar} /> <img src={userData?.avatar} />
</div> </div>
<antd.Input.TextArea <antd.Input.TextArea
//className={classnames("textArea", { ["active"]: canPublish() })} disabled={loading}
disabled={props.loading} value={postData.message}
value={value} onPressEnter={submit}
onPressEnter={handleSubmit}
autoSize={{ minRows: 3, maxRows: 6 }} autoSize={{ minRows: 3, maxRows: 6 }}
dragable="false" dragable="false"
placeholder="What are you thinking?" placeholder="What are you thinking?"
onChange={onChange} onChange={onChangeMessageInput}
allowClear allowClear
rows={8} rows={8}
maxLength={maxMessageLength} maxLength={maxMessageLength}
@ -49,50 +163,36 @@ const PostCreatorInput = (props) => {
<div> <div>
<antd.Button <antd.Button
type="primary" type="primary"
disabled={props.loading || !canPublish()} disabled={loading || !canPublish()}
onClick={handleSubmit} onClick={submit}
icon={props.loading ? <Icons.LoadingOutlined spin /> : <Icons.Send />} icon={loading ? <Icons.LoadingOutlined spin /> : <Icons.Send />}
/> />
</div> </div>
</div> </div>
}
export default class PostCreator extends React.Component { {postData.additions.length > 0 && <PostAdditions additions={postData.additions} />}
state = {
loading: false,
}
api = window.app.request
componentDidMount = async () => { <div className={classnames("actions", { ["hided"]: !focused && !uploaderVisible })}>
const userData = await User.data() <div>
<antd.Button
this.setState({ type={uploaderVisible ? "default" : "primary"}
userData disabled={loading}
}) onClick={() => {
} toggleUploader()
}}
onSubmit = async (value) => { icon={<Icons.Upload />}
await this.setState({ loading: true })
const result = this.api.put.post({
message: value,
}).catch(error => {
console.error(error)
antd.message.error(error)
return false
})
this.setState({ loading: false })
}
render() {
return <div className="postCreator">
<PostCreatorInput
user={this.state.userData}
loading={this.state.loading}
onSubmit={this.onSubmit}
/> />
</div> </div>
} </div>
<div className={classnames("uploader", { ["hided"]: !uploaderVisible })}>
<antd.Upload.Dragger
multiple={true}
onChange={onDraggerChange}
customRequest={onUploadFile}
>
<p >Click or drag file to this area to upload</p>
</antd.Upload.Dragger>
</div>
</div>
} }

View File

@ -1,15 +1,199 @@
.postCreator { .postCreator {
width : 100%; display: flex;
padding : 15px; flex-direction: column;
width: 100%;
padding: 15px;
background-color: var(--background-color-accent); background-color: var(--background-color-accent);
max-width : 600px; max-width: 600px;
border-radius: 7px; border-radius: 7px;
.additions {
margin: 10px 0;
width: 100%;
height: 28vh;
.slick-slider {
.slick-prev {
display: inline !important;
color: #ffffff;
z-index: 100;
top: 0;
left: 0;
height: 100%;
width: 5vw;
opacity: 0;
transition: all 150ms ease-in-out;
&:hover {
opacity: 0.6;
width: 7vw;
}
&:active {
transform: scale(0.5);
}
}
.slick-next {
display: inline !important;
color: #ffffff;
z-index: 100;
top: 0;
right: 0;
height: 100%;
width: 5vw;
opacity: 0;
transition: all 150ms ease-in-out;
&:hover {
opacity: 0.6;
width: 7vw;
}
&:active {
transform: scale(0.5);
}
}
}
.slick-track {
display: flex;
align-items: center;
}
.slick-slide {
height: 100%;
div {
height: 100%;
}
}
.slick-list {
border-radius: 8px;
}
.ant-carousel,
.slick-slider,
.slick-list,
.slick-track {
height: 100%;
}
.addition {
width: 100%;
height: 100%;
// fixtures for media content
img {
width: 100%;
height: 100%;
user-select: none;
-webkit-user-drag: none;
object-fit: cover;
}
video {
width: 100%;
height: 100%;
user-select: none;
-webkit-user-drag: none;
object-fit: cover;
}
audio {
width: 100%;
height: 100%;
user-select: none;
-webkit-user-drag: none;
}
>div {
height: 100%;
}
}
}
.actions {
display: inline-flex;
flex-direction: row;
justify-content: flex-start;
height: 40px;
overflow: hidden;
transition: all 150ms ease-in-out;
>div {
margin-left: 10px;
font-size: 1rem;
svg {
margin: 0 !important;
}
}
&.hided {
height: 0;
}
}
.uploader {
display: flex;
flex-direction: row;
justify-content: flex-start;
height: 5vh;
width: 100%;
overflow: hidden;
transition: all 150ms ease-in-out;
>div {
margin-left: 10px;
font-size: 1rem;
svg {
margin: 0 !important;
}
}
&.hided {
height: 0;
}
span {
width: 100%;
}
.ant-upload {
width: 100%;
}
}
.textInput { .textInput {
display : flex; display: flex;
width : 100%; width: 100%;
transition : height 150ms ease-in-out; transition: height 150ms ease-in-out;
background-color: var(--background-color-accent); background-color: var(--background-color-accent);
svg { svg {
@ -17,14 +201,14 @@
} }
.avatar { .avatar {
width : fit-content; width: fit-content;
height: 45px; height: 45px;
display: flex; display: flex;
img { img {
width : 45px; width: 45px;
height : 45px; height: 45px;
border-radius: 12px; border-radius: 12px;
} }
} }
@ -35,7 +219,7 @@
.textArea { .textArea {
border-radius: 8px !important; border-radius: 8px !important;
transition : all 150ms ease-in-out !important; transition: all 150ms ease-in-out !important;
&.active { &.active {
background-color: var(--background-color-primary); background-color: var(--background-color-primary);
@ -43,27 +227,27 @@
} }
.ant-btn-primary { .ant-btn-primary {
z-index : 10; z-index: 10;
position : relative; position: relative;
border-radius : 0 10px 10px 0; border-radius: 0 10px 10px 0;
height : 100%; height: 100%;
vertical-align: bottom; vertical-align: bottom;
border : none; border: none;
box-shadow : none; box-shadow: none;
} }
.ant-input { .ant-input {
background-color: var(--background-color-accent); background-color: var(--background-color-accent);
z-index : 10; z-index: 10;
position : relative; position: relative;
border-color : transparent !important; border-color: transparent !important;
box-shadow : none; box-shadow: none;
border-radius: 3px 0 0; border-radius: 3px 0 0;
height : 100%; height: 100%;
padding : 5px 10px; padding: 5px 10px;
transition : height 150ms linear; transition: height 150ms linear;
width : 100%; width: 100%;
} }
.ant-btn-primary[disabled] { .ant-btn-primary[disabled] {