mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 02:54:15 +00:00
rewrite component & support additions uploads
This commit is contained in:
parent
a0f641957f
commit
49999e8acb
@ -3,96 +3,196 @@ 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 canPublish = () => {
|
const [loading, setLoading] = React.useState(false)
|
||||||
return value.length !== 0 && value.length < maxMessageLength
|
const [uploaderVisible, setUploaderVisible] = React.useState(false)
|
||||||
}
|
const [focused, setFocused] = React.useState(false)
|
||||||
|
|
||||||
const onChange = (e) => {
|
const [userData, setUserData] = React.useState(null)
|
||||||
setValue(e.target.value)
|
const [postData, setPostData] = React.useState({
|
||||||
}
|
message: "",
|
||||||
|
additions: []
|
||||||
|
})
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const updatePostData = (update) => {
|
||||||
if (canPublish()) {
|
setPostData({
|
||||||
if (typeof props.onSubmit === "function") {
|
...postData,
|
||||||
props.onSubmit(value)
|
...update
|
||||||
}
|
|
||||||
|
|
||||||
setValue("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div className="textInput">
|
|
||||||
<div className="avatar">
|
|
||||||
<img src={props.user?.avatar} />
|
|
||||||
</div>
|
|
||||||
<antd.Input.TextArea
|
|
||||||
//className={classnames("textArea", { ["active"]: canPublish() })}
|
|
||||||
disabled={props.loading}
|
|
||||||
value={value}
|
|
||||||
onPressEnter={handleSubmit}
|
|
||||||
autoSize={{ minRows: 3, maxRows: 6 }}
|
|
||||||
dragable="false"
|
|
||||||
placeholder="What are you thinking?"
|
|
||||||
onChange={onChange}
|
|
||||||
allowClear
|
|
||||||
rows={8}
|
|
||||||
maxLength={maxMessageLength}
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<antd.Button
|
|
||||||
type="primary"
|
|
||||||
disabled={props.loading || !canPublish()}
|
|
||||||
onClick={handleSubmit}
|
|
||||||
icon={props.loading ? <Icons.LoadingOutlined spin /> : <Icons.Send />}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class PostCreator extends React.Component {
|
|
||||||
state = {
|
|
||||||
loading: false,
|
|
||||||
}
|
|
||||||
api = window.app.request
|
|
||||||
|
|
||||||
componentDidMount = async () => {
|
|
||||||
const userData = await User.data()
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
userData
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmit = async (value) => {
|
const cleanPostData = () => {
|
||||||
await this.setState({ loading: true })
|
setPostData({
|
||||||
|
message: "",
|
||||||
|
additions: []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const result = this.api.put.post({
|
const submit = () => {
|
||||||
message: value,
|
setLoading(true)
|
||||||
}).catch(error => {
|
|
||||||
|
const response = api.put.post({ ...postData }).catch(error => {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
antd.message.error(error)
|
antd.message.error(error)
|
||||||
|
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
this.setState({ loading: false })
|
setLoading(false)
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
cleanPostData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
const onUploadFile = async (req) => {
|
||||||
return <div className="postCreator">
|
// get file data
|
||||||
<PostCreatorInput
|
const file = req.file
|
||||||
user={this.state.userData}
|
|
||||||
loading={this.state.loading}
|
// append to form data
|
||||||
onSubmit={this.onSubmit}
|
const formData = new FormData()
|
||||||
/>
|
formData.append("files", file)
|
||||||
</div>
|
|
||||||
|
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 messageLengthValid = postData.message.length !== 0 && postData.message.length < maxMessageLength
|
||||||
|
|
||||||
|
return Boolean(messageLengthValid)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDraggerChange = (change) => {
|
||||||
|
console.log(change)
|
||||||
|
|
||||||
|
switch (change.file.status) {
|
||||||
|
case "done": {
|
||||||
|
let additions = postData.additions ?? []
|
||||||
|
|
||||||
|
additions.push(...change.file.response)
|
||||||
|
|
||||||
|
return updatePostData({ additions })
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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">
|
||||||
|
<img src={userData?.avatar} />
|
||||||
|
</div>
|
||||||
|
<antd.Input.TextArea
|
||||||
|
disabled={loading}
|
||||||
|
value={postData.message}
|
||||||
|
onPressEnter={submit}
|
||||||
|
autoSize={{ minRows: 3, maxRows: 6 }}
|
||||||
|
dragable="false"
|
||||||
|
placeholder="What are you thinking?"
|
||||||
|
onChange={onChangeMessageInput}
|
||||||
|
allowClear
|
||||||
|
rows={8}
|
||||||
|
maxLength={maxMessageLength}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<antd.Button
|
||||||
|
type="primary"
|
||||||
|
disabled={loading || !canPublish()}
|
||||||
|
onClick={submit}
|
||||||
|
icon={loading ? <Icons.LoadingOutlined spin /> : <Icons.Send />}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{postData.additions.length > 0 && <PostAdditions additions={postData.additions} />}
|
||||||
|
|
||||||
|
<div className={classnames("actions", { ["hided"]: !focused && !uploaderVisible })}>
|
||||||
|
<div>
|
||||||
|
<antd.Button
|
||||||
|
type={uploaderVisible ? "default" : "primary"}
|
||||||
|
disabled={loading}
|
||||||
|
onClick={() => {
|
||||||
|
toggleUploader()
|
||||||
|
}}
|
||||||
|
icon={<Icons.Upload />}
|
||||||
|
/>
|
||||||
|
</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>
|
||||||
}
|
}
|
@ -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] {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user