Fixes and refactor for compose UI with media uploads
Somehow prettier (for CSS) start running properly
This commit is contained in:
parent
f7571f6df1
commit
b988b10c3d
|
@ -24,7 +24,7 @@
|
|||
color: var(--text-insignificant-color);
|
||||
}
|
||||
|
||||
#compose-container textarea{
|
||||
#compose-container textarea {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
height: 3em;
|
||||
|
@ -114,14 +114,16 @@
|
|||
#compose-container .toolbar-button:has([disabled]) > * {
|
||||
filter: opacity(0.3);
|
||||
}
|
||||
#compose-container .toolbar-button:not(.show-field) :is(input[type="checkbox"], select, input[type="file"]) {
|
||||
#compose-container
|
||||
.toolbar-button:not(.show-field)
|
||||
:is(input[type='checkbox'], select, input[type='file']) {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
#compose-container .toolbar-button input[type="file"] {
|
||||
#compose-container .toolbar-button input[type='file'] {
|
||||
/* Move this out of the way, to fix cursor: pointer bug */
|
||||
left: -100vw !important;
|
||||
}
|
||||
|
@ -201,7 +203,7 @@
|
|||
#compose-container .media-preview {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
#compose-container .media-preview > *{
|
||||
#compose-container .media-preview > * {
|
||||
min-width: 80px;
|
||||
width: 80px !important;
|
||||
height: 80px;
|
||||
|
@ -215,6 +217,22 @@
|
|||
flex-grow: 1;
|
||||
resize: none;
|
||||
}
|
||||
#compose-container .media-attachments .media-desc {
|
||||
flex-grow: 1;
|
||||
}
|
||||
#compose-container .media-attachments .media-desc p {
|
||||
font-size: 90%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
/* clamp 2 lines */
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
#compose-container .media-attachments .media-desc p i {
|
||||
color: var(--text-insignificant-color);
|
||||
}
|
||||
#compose-container .media-aside {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -226,6 +244,9 @@
|
|||
align-self: flex-start;
|
||||
color: var(--text-insignificant-color);
|
||||
}
|
||||
#compose-container .media-aside .close-button:hover {
|
||||
color: var(--text-color);
|
||||
}
|
||||
#compose-container .media-aside .uploaded {
|
||||
color: var(--green-color);
|
||||
margin-bottom: 4px;
|
||||
|
|
|
@ -263,26 +263,33 @@ export default ({ onClose, replyToStatus }) => {
|
|||
if (mediaAttachments.length > 0) {
|
||||
// Upload media attachments first
|
||||
const mediaPromises = mediaAttachments.map((attachment) => {
|
||||
const params = {
|
||||
file: attachment.file,
|
||||
description: attachment.description || undefined,
|
||||
};
|
||||
return masto.mediaAttachments.create(params).then((res) => {
|
||||
// Update media attachment with ID
|
||||
if (res.id) {
|
||||
attachment.id = res.id;
|
||||
}
|
||||
return res;
|
||||
});
|
||||
const { file, description, sourceDescription, id } =
|
||||
attachment;
|
||||
console.log('UPLOADING', attachment);
|
||||
if (id) {
|
||||
// If already uploaded
|
||||
return attachment;
|
||||
} else {
|
||||
const params = {
|
||||
file,
|
||||
description,
|
||||
};
|
||||
return masto.mediaAttachments.create(params).then((res) => {
|
||||
// Update media attachment with ID
|
||||
if (res.id) {
|
||||
attachment.id = res.id;
|
||||
}
|
||||
return res;
|
||||
});
|
||||
}
|
||||
});
|
||||
const results = await Promise.allSettled(mediaPromises);
|
||||
|
||||
// If any failed, return
|
||||
if (
|
||||
results.some(
|
||||
(result) =>
|
||||
result.status === 'rejected' || !result.value.id,
|
||||
)
|
||||
results.some((result) => {
|
||||
return result.status === 'rejected' || !result.value?.id;
|
||||
})
|
||||
) {
|
||||
setUIState('error');
|
||||
// Alert all the reasons
|
||||
|
@ -314,7 +321,8 @@ export default ({ onClose, replyToStatus }) => {
|
|||
newStatus,
|
||||
});
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
console.error(e);
|
||||
alert(e?.reason || e);
|
||||
setUIState('error');
|
||||
}
|
||||
})();
|
||||
|
@ -410,63 +418,25 @@ export default ({ onClose, replyToStatus }) => {
|
|||
{mediaAttachments.length > 0 && (
|
||||
<div class="media-attachments">
|
||||
{mediaAttachments.map((attachment, i) => {
|
||||
const { url, type, id } = attachment;
|
||||
const suffixType = type.split('/')[0];
|
||||
const { id } = attachment;
|
||||
return (
|
||||
<div class="media-attachment" key={i + id}>
|
||||
<div class="media-preview">
|
||||
{suffixType === 'image' ? (
|
||||
<img src={url} alt="" />
|
||||
) : suffixType === 'video' ? (
|
||||
<video src={url} playsinline muted />
|
||||
) : suffixType === 'audio' ? (
|
||||
<audio src={url} controls />
|
||||
) : null}
|
||||
</div>
|
||||
<textarea
|
||||
placeholder={
|
||||
{
|
||||
image: 'Image description',
|
||||
video: 'Video description',
|
||||
audio: 'Audio description',
|
||||
}[suffixType]
|
||||
}
|
||||
autoCapitalize="sentences"
|
||||
autoComplete="on"
|
||||
autoCorrect="on"
|
||||
spellCheck="true"
|
||||
dir="auto"
|
||||
disabled={uiState === 'loading'}
|
||||
maxlength="1500"
|
||||
// TODO: Un-hard-code this maxlength, ref: https://github.com/mastodon/mastodon/blob/b59fb28e90bc21d6fd1a6bafd13cfbd81ab5be54/app/models/media_attachment.rb#L39
|
||||
onInput={(e) => {
|
||||
const { value } = e.target;
|
||||
// Modify `description` in media attachment
|
||||
setMediaAttachments((attachments) => {
|
||||
const newAttachments = [...attachments];
|
||||
newAttachments[i].description = value;
|
||||
return newAttachments;
|
||||
});
|
||||
}}
|
||||
></textarea>
|
||||
<div class="media-aside">
|
||||
<button
|
||||
type="button"
|
||||
class="plain close-button"
|
||||
disabled={uiState === 'loading'}
|
||||
onClick={() => {
|
||||
setMediaAttachments((attachments) => {
|
||||
return attachments.filter((_, j) => j !== i);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Icon icon="x" />
|
||||
</button>
|
||||
{!!id && (
|
||||
<Icon icon="upload" title="Uploaded" class="uploaded" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<MediaAttachment
|
||||
key={i + id}
|
||||
attachment={attachment}
|
||||
disabled={uiState === 'loading'}
|
||||
onDescriptionChange={(value) => {
|
||||
setMediaAttachments((attachments) => {
|
||||
const newAttachments = [...attachments];
|
||||
newAttachments[i].description = value;
|
||||
return newAttachments;
|
||||
});
|
||||
}}
|
||||
onRemove={() => {
|
||||
setMediaAttachments((attachments) => {
|
||||
return attachments.filter((_, j) => j !== i);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
@ -529,3 +499,65 @@ export default ({ onClose, replyToStatus }) => {
|
|||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function MediaAttachment({
|
||||
attachment,
|
||||
disabled,
|
||||
onDescriptionChange = () => {},
|
||||
onRemove = () => {},
|
||||
}) {
|
||||
const { url, type, id } = attachment;
|
||||
const suffixType = type.split('/')[0];
|
||||
return (
|
||||
<div class="media-attachment">
|
||||
<div class="media-preview">
|
||||
{suffixType === 'image' ? (
|
||||
<img src={url} alt="" />
|
||||
) : suffixType === 'video' ? (
|
||||
<video src={url} playsinline muted />
|
||||
) : suffixType === 'audio' ? (
|
||||
<audio src={url} controls />
|
||||
) : null}
|
||||
</div>
|
||||
{!!id ? (
|
||||
<div class="media-desc">
|
||||
<span class="tag">Uploaded</span>
|
||||
<p>{attachment.description || <i>No description</i>}</p>
|
||||
</div>
|
||||
) : (
|
||||
<textarea
|
||||
value={attachment.description || ''}
|
||||
placeholder={
|
||||
{
|
||||
image: 'Image description',
|
||||
video: 'Video description',
|
||||
audio: 'Audio description',
|
||||
}[suffixType]
|
||||
}
|
||||
autoCapitalize="sentences"
|
||||
autoComplete="on"
|
||||
autoCorrect="on"
|
||||
spellCheck="true"
|
||||
dir="auto"
|
||||
disabled={disabled}
|
||||
maxlength="1500" // Not unicode-aware :(
|
||||
// TODO: Un-hard-code this maxlength, ref: https://github.com/mastodon/mastodon/blob/b59fb28e90bc21d6fd1a6bafd13cfbd81ab5be54/app/models/media_attachment.rb#L39
|
||||
onInput={(e) => {
|
||||
const { value } = e.target;
|
||||
onDescriptionChange(value);
|
||||
}}
|
||||
></textarea>
|
||||
)}
|
||||
<div class="media-aside">
|
||||
<button
|
||||
type="button"
|
||||
class="plain close-button"
|
||||
disabled={disabled}
|
||||
onClick={onRemove}
|
||||
>
|
||||
<Icon icon="x" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,23 +1,28 @@
|
|||
/* REBLOG + REPLY-TO */
|
||||
|
||||
.status-reblog {
|
||||
background: linear-gradient(to bottom right, var(
|
||||
--reblog-faded-color
|
||||
), transparent 160px);
|
||||
background: linear-gradient(
|
||||
to bottom right,
|
||||
var(--reblog-faded-color),
|
||||
transparent 160px
|
||||
);
|
||||
}
|
||||
.status-reply-to {
|
||||
background: linear-gradient(to bottom right, var(
|
||||
--reply-to-faded-color
|
||||
), transparent 160px);
|
||||
background: linear-gradient(
|
||||
to bottom right,
|
||||
var(--reply-to-faded-color),
|
||||
transparent 160px
|
||||
);
|
||||
}
|
||||
.status-reblog .status-reply-to {
|
||||
background: linear-gradient(to top left, var(
|
||||
--reply-to-faded-color
|
||||
), transparent 160px);
|
||||
background: linear-gradient(
|
||||
to top left,
|
||||
var(--reply-to-faded-color),
|
||||
transparent 160px
|
||||
);
|
||||
}
|
||||
.visibility-direct {
|
||||
/* diagonal stripes of yellow */
|
||||
background-image: repeating-linear-gradient(
|
||||
--yellow-stripes: repeating-linear-gradient(
|
||||
-45deg,
|
||||
var(--reply-to-faded-color),
|
||||
var(--reply-to-faded-color) 10px,
|
||||
|
@ -25,6 +30,8 @@
|
|||
transparent 10px,
|
||||
transparent 20px
|
||||
);
|
||||
/* diagonal stripes of yellow */
|
||||
background-image: var(--yellow-stripes);
|
||||
}
|
||||
|
||||
/* STATUS PRE META */
|
||||
|
@ -51,7 +58,18 @@
|
|||
align-items: flex-start;
|
||||
}
|
||||
.status.large {
|
||||
--fade-in-out-bg: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
var(--bg-color) 70px,
|
||||
var(--bg-color) calc(100% - 50px),
|
||||
transparent
|
||||
);
|
||||
padding-bottom: 8px;
|
||||
background-image: var(--fade-in-out-bg);
|
||||
}
|
||||
.status.large.visibility-direct {
|
||||
background-image: var(--fade-in-out-bg), var(--yellow-stripes);
|
||||
}
|
||||
.status-pre-meta + .status {
|
||||
padding-top: 8px;
|
||||
|
@ -87,11 +105,11 @@
|
|||
min-height: 50px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.status > .container > .meta .arrow {
|
||||
.status > .container > .meta .arrow {
|
||||
color: var(--reply-to-color);
|
||||
vertical-align: middle;
|
||||
}
|
||||
.status > .container > .meta :is(.time, .edited) {
|
||||
.status > .container > .meta :is(.time, .edited) {
|
||||
color: inherit;
|
||||
text-align: end;
|
||||
opacity: 0.5;
|
||||
|
@ -100,17 +118,16 @@
|
|||
margin-left: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.status > .container > .meta a.time:hover {
|
||||
.status > .container > .meta a.time:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.status > .container > .meta .reply-to {
|
||||
.status > .container > .meta .reply-to {
|
||||
opacity: 0.5;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
.status.large .content-container {
|
||||
margin-left: calc(-50px - 16px);
|
||||
background-image: linear-gradient(to bottom, transparent, var(--bg-color) 10px, var(--bg-color));
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
@ -124,13 +141,13 @@
|
|||
align-items: center;
|
||||
}
|
||||
.status .content-container.has-spoiler .spoiler ~ * {
|
||||
filter: blur(6px) invert(.5);
|
||||
filter: blur(6px) invert(0.5);
|
||||
pointer-events: none;
|
||||
transition: filter .5s;
|
||||
transition: filter 0.5s;
|
||||
user-select: none;
|
||||
}
|
||||
.status .content-container.has-spoiler .spoiler ~ .content ~ * {
|
||||
opacity: .5;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.status .content-container.show-spoiler .spoiler {
|
||||
border-style: dotted;
|
||||
|
@ -148,7 +165,7 @@
|
|||
margin-top: 8px;
|
||||
}
|
||||
.status .content p {
|
||||
margin-block: .75em;
|
||||
margin-block: 0.75em;
|
||||
}
|
||||
.status .content p:first-child {
|
||||
margin-block-start: 0;
|
||||
|
@ -236,7 +253,7 @@
|
|||
height: 70px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--bg-blur-color);
|
||||
backdrop-filter: blur(6px) saturate(3) invert(.2);
|
||||
backdrop-filter: blur(6px) saturate(3) invert(0.2);
|
||||
z-index: 1;
|
||||
}
|
||||
.status .media-video:after {
|
||||
|
@ -249,8 +266,9 @@
|
|||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 15px 0 15px 26.0px;
|
||||
border-color: transparent transparent transparent var(--text-insignificant-color);
|
||||
border-width: 15px 0 15px 26px;
|
||||
border-color: transparent transparent transparent
|
||||
var(--text-insignificant-color);
|
||||
pointer-events: none;
|
||||
opacity: 0.75;
|
||||
z-index: 2;
|
||||
|
@ -351,9 +369,15 @@ a.card:hover {
|
|||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: space-between;
|
||||
background-image: linear-gradient(to right, var(--link-faded-color), var(--link-faded-color) var(--percentage), transparent var(--percentage), transparent);
|
||||
background-image: linear-gradient(
|
||||
to right,
|
||||
var(--link-faded-color),
|
||||
var(--link-faded-color) var(--percentage),
|
||||
transparent var(--percentage),
|
||||
transparent
|
||||
);
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(128, 128, 128, .1);
|
||||
border: 1px solid rgba(128, 128, 128, 0.1);
|
||||
align-items: center;
|
||||
}
|
||||
.poll-label {
|
||||
|
@ -391,8 +415,7 @@ a.card:hover {
|
|||
}
|
||||
.status.large .extra-meta {
|
||||
padding-top: 0;
|
||||
margin-left: calc(-50px - 16px);
|
||||
background-color: var(--bg-color);
|
||||
margin-left: calc(-50px - 4px);
|
||||
}
|
||||
|
||||
/* ACTIONS */
|
||||
|
@ -405,14 +428,12 @@ a.card:hover {
|
|||
justify-content: space-between;
|
||||
}
|
||||
.status.large .actions {
|
||||
/* margin-left: -12px; */
|
||||
padding-top: 8px;
|
||||
padding-bottom: 16px;
|
||||
margin-left: calc(-50px - 16px);
|
||||
background-image: linear-gradient(to bottom, var(--bg-color), var(--bg-color) calc(100% - 10px), transparent);
|
||||
}
|
||||
.status .actions > * {
|
||||
opacity: .5;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
}
|
||||
.status:hover .actions > * {
|
||||
|
@ -459,9 +480,11 @@ a.card:hover {
|
|||
width: 100%;
|
||||
font-size: 90%;
|
||||
border: 1px solid var(--outline-color);
|
||||
background: linear-gradient(to bottom right, var(
|
||||
--bg-faded-color
|
||||
), transparent 160px);
|
||||
background: linear-gradient(
|
||||
to bottom right,
|
||||
var(--bg-faded-color),
|
||||
transparent 160px
|
||||
);
|
||||
}
|
||||
|
||||
/* MISC */
|
||||
|
@ -485,7 +508,7 @@ a.card:hover {
|
|||
min-height: 50dvh;
|
||||
}
|
||||
|
||||
#edit-history :is(ol, ol li){
|
||||
#edit-history :is(ol, ol li) {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
|
Loading…
Reference in a new issue