mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-02-24 00:38:49 +01:00
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
3 changed files with 189 additions and 113 deletions
|
@ -24,7 +24,7 @@
|
||||||
color: var(--text-insignificant-color);
|
color: var(--text-insignificant-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#compose-container textarea{
|
#compose-container textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: 3em;
|
height: 3em;
|
||||||
|
@ -114,14 +114,16 @@
|
||||||
#compose-container .toolbar-button:has([disabled]) > * {
|
#compose-container .toolbar-button:has([disabled]) > * {
|
||||||
filter: opacity(0.3);
|
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;
|
opacity: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0;
|
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 */
|
/* Move this out of the way, to fix cursor: pointer bug */
|
||||||
left: -100vw !important;
|
left: -100vw !important;
|
||||||
}
|
}
|
||||||
|
@ -201,7 +203,7 @@
|
||||||
#compose-container .media-preview {
|
#compose-container .media-preview {
|
||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
}
|
}
|
||||||
#compose-container .media-preview > *{
|
#compose-container .media-preview > * {
|
||||||
min-width: 80px;
|
min-width: 80px;
|
||||||
width: 80px !important;
|
width: 80px !important;
|
||||||
height: 80px;
|
height: 80px;
|
||||||
|
@ -215,6 +217,22 @@
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
resize: none;
|
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 {
|
#compose-container .media-aside {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -226,6 +244,9 @@
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
color: var(--text-insignificant-color);
|
color: var(--text-insignificant-color);
|
||||||
}
|
}
|
||||||
|
#compose-container .media-aside .close-button:hover {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
#compose-container .media-aside .uploaded {
|
#compose-container .media-aside .uploaded {
|
||||||
color: var(--green-color);
|
color: var(--green-color);
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
|
|
|
@ -263,9 +263,16 @@ export default ({ onClose, replyToStatus }) => {
|
||||||
if (mediaAttachments.length > 0) {
|
if (mediaAttachments.length > 0) {
|
||||||
// Upload media attachments first
|
// Upload media attachments first
|
||||||
const mediaPromises = mediaAttachments.map((attachment) => {
|
const mediaPromises = mediaAttachments.map((attachment) => {
|
||||||
|
const { file, description, sourceDescription, id } =
|
||||||
|
attachment;
|
||||||
|
console.log('UPLOADING', attachment);
|
||||||
|
if (id) {
|
||||||
|
// If already uploaded
|
||||||
|
return attachment;
|
||||||
|
} else {
|
||||||
const params = {
|
const params = {
|
||||||
file: attachment.file,
|
file,
|
||||||
description: attachment.description || undefined,
|
description,
|
||||||
};
|
};
|
||||||
return masto.mediaAttachments.create(params).then((res) => {
|
return masto.mediaAttachments.create(params).then((res) => {
|
||||||
// Update media attachment with ID
|
// Update media attachment with ID
|
||||||
|
@ -274,15 +281,15 @@ export default ({ onClose, replyToStatus }) => {
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
const results = await Promise.allSettled(mediaPromises);
|
const results = await Promise.allSettled(mediaPromises);
|
||||||
|
|
||||||
// If any failed, return
|
// If any failed, return
|
||||||
if (
|
if (
|
||||||
results.some(
|
results.some((result) => {
|
||||||
(result) =>
|
return result.status === 'rejected' || !result.value?.id;
|
||||||
result.status === 'rejected' || !result.value.id,
|
})
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
setUIState('error');
|
setUIState('error');
|
||||||
// Alert all the reasons
|
// Alert all the reasons
|
||||||
|
@ -314,7 +321,8 @@ export default ({ onClose, replyToStatus }) => {
|
||||||
newStatus,
|
newStatus,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e);
|
console.error(e);
|
||||||
|
alert(e?.reason || e);
|
||||||
setUIState('error');
|
setUIState('error');
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -410,63 +418,25 @@ export default ({ onClose, replyToStatus }) => {
|
||||||
{mediaAttachments.length > 0 && (
|
{mediaAttachments.length > 0 && (
|
||||||
<div class="media-attachments">
|
<div class="media-attachments">
|
||||||
{mediaAttachments.map((attachment, i) => {
|
{mediaAttachments.map((attachment, i) => {
|
||||||
const { url, type, id } = attachment;
|
const { id } = attachment;
|
||||||
const suffixType = type.split('/')[0];
|
|
||||||
return (
|
return (
|
||||||
<div class="media-attachment" key={i + id}>
|
<MediaAttachment
|
||||||
<div class="media-preview">
|
key={i + id}
|
||||||
{suffixType === 'image' ? (
|
attachment={attachment}
|
||||||
<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'}
|
disabled={uiState === 'loading'}
|
||||||
maxlength="1500"
|
onDescriptionChange={(value) => {
|
||||||
// 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) => {
|
setMediaAttachments((attachments) => {
|
||||||
const newAttachments = [...attachments];
|
const newAttachments = [...attachments];
|
||||||
newAttachments[i].description = value;
|
newAttachments[i].description = value;
|
||||||
return newAttachments;
|
return newAttachments;
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
></textarea>
|
onRemove={() => {
|
||||||
<div class="media-aside">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="plain close-button"
|
|
||||||
disabled={uiState === 'loading'}
|
|
||||||
onClick={() => {
|
|
||||||
setMediaAttachments((attachments) => {
|
setMediaAttachments((attachments) => {
|
||||||
return attachments.filter((_, j) => j !== i);
|
return attachments.filter((_, j) => j !== i);
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
<Icon icon="x" />
|
|
||||||
</button>
|
|
||||||
{!!id && (
|
|
||||||
<Icon icon="upload" title="Uploaded" class="uploaded" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
@ -529,3 +499,65 @@ export default ({ onClose, replyToStatus }) => {
|
||||||
</div>
|
</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 */
|
/* REBLOG + REPLY-TO */
|
||||||
|
|
||||||
.status-reblog {
|
.status-reblog {
|
||||||
background: linear-gradient(to bottom right, var(
|
background: linear-gradient(
|
||||||
--reblog-faded-color
|
to bottom right,
|
||||||
), transparent 160px);
|
var(--reblog-faded-color),
|
||||||
|
transparent 160px
|
||||||
|
);
|
||||||
}
|
}
|
||||||
.status-reply-to {
|
.status-reply-to {
|
||||||
background: linear-gradient(to bottom right, var(
|
background: linear-gradient(
|
||||||
--reply-to-faded-color
|
to bottom right,
|
||||||
), transparent 160px);
|
var(--reply-to-faded-color),
|
||||||
|
transparent 160px
|
||||||
|
);
|
||||||
}
|
}
|
||||||
.status-reblog .status-reply-to {
|
.status-reblog .status-reply-to {
|
||||||
background: linear-gradient(to top left, var(
|
background: linear-gradient(
|
||||||
--reply-to-faded-color
|
to top left,
|
||||||
), transparent 160px);
|
var(--reply-to-faded-color),
|
||||||
|
transparent 160px
|
||||||
|
);
|
||||||
}
|
}
|
||||||
.visibility-direct {
|
.visibility-direct {
|
||||||
/* diagonal stripes of yellow */
|
--yellow-stripes: repeating-linear-gradient(
|
||||||
background-image: repeating-linear-gradient(
|
|
||||||
-45deg,
|
-45deg,
|
||||||
var(--reply-to-faded-color),
|
var(--reply-to-faded-color),
|
||||||
var(--reply-to-faded-color) 10px,
|
var(--reply-to-faded-color) 10px,
|
||||||
|
@ -25,6 +30,8 @@
|
||||||
transparent 10px,
|
transparent 10px,
|
||||||
transparent 20px
|
transparent 20px
|
||||||
);
|
);
|
||||||
|
/* diagonal stripes of yellow */
|
||||||
|
background-image: var(--yellow-stripes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* STATUS PRE META */
|
/* STATUS PRE META */
|
||||||
|
@ -51,7 +58,18 @@
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
.status.large {
|
.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;
|
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 {
|
.status-pre-meta + .status {
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
|
@ -110,7 +128,6 @@
|
||||||
|
|
||||||
.status.large .content-container {
|
.status.large .content-container {
|
||||||
margin-left: calc(-50px - 16px);
|
margin-left: calc(-50px - 16px);
|
||||||
background-image: linear-gradient(to bottom, transparent, var(--bg-color) 10px, var(--bg-color));
|
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
@ -124,13 +141,13 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.status .content-container.has-spoiler .spoiler ~ * {
|
.status .content-container.has-spoiler .spoiler ~ * {
|
||||||
filter: blur(6px) invert(.5);
|
filter: blur(6px) invert(0.5);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transition: filter .5s;
|
transition: filter 0.5s;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
.status .content-container.has-spoiler .spoiler ~ .content ~ * {
|
.status .content-container.has-spoiler .spoiler ~ .content ~ * {
|
||||||
opacity: .5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
.status .content-container.show-spoiler .spoiler {
|
.status .content-container.show-spoiler .spoiler {
|
||||||
border-style: dotted;
|
border-style: dotted;
|
||||||
|
@ -148,7 +165,7 @@
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
.status .content p {
|
.status .content p {
|
||||||
margin-block: .75em;
|
margin-block: 0.75em;
|
||||||
}
|
}
|
||||||
.status .content p:first-child {
|
.status .content p:first-child {
|
||||||
margin-block-start: 0;
|
margin-block-start: 0;
|
||||||
|
@ -236,7 +253,7 @@
|
||||||
height: 70px;
|
height: 70px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: var(--bg-blur-color);
|
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;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
.status .media-video:after {
|
.status .media-video:after {
|
||||||
|
@ -249,8 +266,9 @@
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 15px 0 15px 26.0px;
|
border-width: 15px 0 15px 26px;
|
||||||
border-color: transparent transparent transparent var(--text-insignificant-color);
|
border-color: transparent transparent transparent
|
||||||
|
var(--text-insignificant-color);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
@ -351,9 +369,15 @@ a.card:hover {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
justify-content: space-between;
|
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-radius: 8px;
|
||||||
border: 1px solid rgba(128, 128, 128, .1);
|
border: 1px solid rgba(128, 128, 128, 0.1);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.poll-label {
|
.poll-label {
|
||||||
|
@ -391,8 +415,7 @@ a.card:hover {
|
||||||
}
|
}
|
||||||
.status.large .extra-meta {
|
.status.large .extra-meta {
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
margin-left: calc(-50px - 16px);
|
margin-left: calc(-50px - 4px);
|
||||||
background-color: var(--bg-color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ACTIONS */
|
/* ACTIONS */
|
||||||
|
@ -405,14 +428,12 @@ a.card:hover {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.status.large .actions {
|
.status.large .actions {
|
||||||
/* margin-left: -12px; */
|
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
margin-left: calc(-50px - 16px);
|
margin-left: calc(-50px - 16px);
|
||||||
background-image: linear-gradient(to bottom, var(--bg-color), var(--bg-color) calc(100% - 10px), transparent);
|
|
||||||
}
|
}
|
||||||
.status .actions > * {
|
.status .actions > * {
|
||||||
opacity: .5;
|
opacity: 0.5;
|
||||||
transition: opacity 0.2s ease-in-out;
|
transition: opacity 0.2s ease-in-out;
|
||||||
}
|
}
|
||||||
.status:hover .actions > * {
|
.status:hover .actions > * {
|
||||||
|
@ -459,9 +480,11 @@ a.card:hover {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
border: 1px solid var(--outline-color);
|
border: 1px solid var(--outline-color);
|
||||||
background: linear-gradient(to bottom right, var(
|
background: linear-gradient(
|
||||||
--bg-faded-color
|
to bottom right,
|
||||||
), transparent 160px);
|
var(--bg-faded-color),
|
||||||
|
transparent 160px
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MISC */
|
/* MISC */
|
||||||
|
@ -485,7 +508,7 @@ a.card:hover {
|
||||||
min-height: 50dvh;
|
min-height: 50dvh;
|
||||||
}
|
}
|
||||||
|
|
||||||
#edit-history :is(ol, ol li){
|
#edit-history :is(ol, ol li) {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
Loading…
Reference in a new issue