mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-02-24 16:58:47 +01:00
Adjustments to composer footer buttons
- Make it one-liner - Make the add-action buttons scrollable - Introduce 'Add' button that shows a menu of the actions to allow more actions in the future
This commit is contained in:
parent
b6d1522480
commit
28bdd9a0fa
2 changed files with 335 additions and 115 deletions
|
@ -20,11 +20,15 @@
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 16px;
|
padding: 8px;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
|
@media (min-width: 480px) {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#compose-container .compose-top .account-block {
|
#compose-container .compose-top .account-block {
|
||||||
text-align: start;
|
text-align: start;
|
||||||
|
@ -110,10 +114,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#compose-container form {
|
#compose-container form {
|
||||||
--form-padding-inline: 8px;
|
--form-spacing-inline: 4px;
|
||||||
--form-padding-block: 0;
|
--form-spacing-block: 0;
|
||||||
/* border-radius: 16px; */
|
/* border-radius: 16px; */
|
||||||
padding: var(--form-padding-block) var(--form-padding-inline);
|
padding: var(--form-spacing-block) var(--form-spacing-inline);
|
||||||
background-color: var(--bg-blur-color);
|
background-color: var(--bg-blur-color);
|
||||||
/* background-image: linear-gradient(var(--bg-color) 85%, transparent); */
|
/* background-image: linear-gradient(var(--bg-color) 85%, transparent); */
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -121,6 +125,10 @@
|
||||||
--drop-shadow: 0 3px 6px -3px var(--drop-shadow-color);
|
--drop-shadow: 0 3px 6px -3px var(--drop-shadow-color);
|
||||||
box-shadow: var(--drop-shadow);
|
box-shadow: var(--drop-shadow);
|
||||||
|
|
||||||
|
@media (min-width: 480px) {
|
||||||
|
--form-spacing-inline: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 40em) {
|
@media (min-width: 40em) {
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
}
|
}
|
||||||
|
@ -153,8 +161,8 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 8px 0;
|
padding: var(--form-spacing-inline) 0;
|
||||||
gap: 8px;
|
gap: var(--form-spacing-inline);
|
||||||
}
|
}
|
||||||
#compose-container .toolbar.wrap {
|
#compose-container .toolbar.wrap {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
@ -181,6 +189,11 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
filter: brightness(0.8);
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#compose-container .toolbar-button > * {
|
#compose-container .toolbar-button > * {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
@ -248,6 +261,38 @@
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#compose-container .compose-footer {
|
||||||
|
.add-toolbar-button-group {
|
||||||
|
display: flex;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.add-sub-toolbar-button-group {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
overflow: auto;
|
||||||
|
transition: 0.5s ease-in-out;
|
||||||
|
transition-property: opacity, width;
|
||||||
|
scrollbar-width: none;
|
||||||
|
padding-inline-end: 16px;
|
||||||
|
mask-image: linear-gradient(
|
||||||
|
var(--to-backward),
|
||||||
|
transparent 0,
|
||||||
|
black 16px,
|
||||||
|
black 100%
|
||||||
|
);
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[hidden] {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#compose-container text-expander {
|
#compose-container text-expander {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -516,6 +561,37 @@
|
||||||
color: var(--red-color);
|
color: var(--red-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.compose-menu-add-media {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.compose-menu-add-media-field {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
opacity: 0;
|
||||||
|
cursor: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-gif {
|
||||||
|
display: inline-block !important;
|
||||||
|
min-width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
font-size: 10px !important;
|
||||||
|
letter-spacing: -0.5px;
|
||||||
|
font-size-adjust: none;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-rendering: optimizeSpeed;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
display: block;
|
||||||
|
content: 'GIF';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (display-mode: standalone) {
|
@media (display-mode: standalone) {
|
||||||
/* No popping in standalone mode */
|
/* No popping in standalone mode */
|
||||||
#compose-container .pop-button {
|
#compose-container .pop-button {
|
||||||
|
@ -525,8 +601,10 @@
|
||||||
|
|
||||||
#compose-container button[type='submit'] {
|
#compose-container button[type='submit'] {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
@media (min-width: 480px) {
|
@media (min-width: 480px) {
|
||||||
padding-inline: 24px;
|
padding-inline: 24px;
|
||||||
|
font-size: 125%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -820,8 +898,8 @@
|
||||||
.compose-field-container {
|
.compose-field-container {
|
||||||
display: grid !important;
|
display: grid !important;
|
||||||
|
|
||||||
@media (width < 30em) {
|
@media (width < 480px) {
|
||||||
margin-inline: calc(-1 * var(--form-padding-inline));
|
margin-inline: calc(-1 * var(--form-spacing-inline));
|
||||||
width: 100vw !important;
|
width: 100vw !important;
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
|
|
||||||
|
@ -929,15 +1007,55 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes jump-scare {
|
||||||
|
from {
|
||||||
|
opacity: 0.5;
|
||||||
|
transform: scale(0.25) translateX(80px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1) translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes jump-scare-rtl {
|
||||||
|
from {
|
||||||
|
opacity: 0.5;
|
||||||
|
transform: scale(0.25) translateX(-80px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1) translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-button {
|
||||||
|
transform-origin: var(--forward) center;
|
||||||
|
background-color: var(--bg-blur-color) !important;
|
||||||
|
animation: jump-scare 0.2s ease-in-out both;
|
||||||
|
:dir(rtl) & {
|
||||||
|
animation-name: jump-scare-rtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
transition: transform 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
.icon {
|
||||||
|
transform: rotate(135deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.gif-picker-button {
|
.gif-picker-button {
|
||||||
span {
|
/* span {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 11.5px;
|
font-size: 11.5px;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
line-height: 1;
|
||||||
|
} */
|
||||||
|
|
||||||
&:is(:hover, :focus) {
|
&:is(:hover, :focus) {
|
||||||
span {
|
.icon {
|
||||||
animation: gif-shake 0.3s 3;
|
animation: gif-shake 0.3s 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import stringLength from 'string-length';
|
||||||
// import { detectAll } from 'tinyld/light';
|
// import { detectAll } from 'tinyld/light';
|
||||||
import { uid } from 'uid/single';
|
import { uid } from 'uid/single';
|
||||||
import { useDebouncedCallback, useThrottledCallback } from 'use-debounce';
|
import { useDebouncedCallback, useThrottledCallback } from 'use-debounce';
|
||||||
|
import useResizeObserver from 'use-resize-observer';
|
||||||
import { useSnapshot } from 'valtio';
|
import { useSnapshot } from 'valtio';
|
||||||
|
|
||||||
import poweredByGiphyURL from '../assets/powered-by-giphy.svg';
|
import poweredByGiphyURL from '../assets/powered-by-giphy.svg';
|
||||||
|
@ -201,6 +202,13 @@ const LF = mem((locale) => new Intl.ListFormat(locale || undefined));
|
||||||
|
|
||||||
const CUSTOM_EMOJIS_COUNT = 100;
|
const CUSTOM_EMOJIS_COUNT = 100;
|
||||||
|
|
||||||
|
const ADD_LABELS = {
|
||||||
|
media: msg`Add media`,
|
||||||
|
customEmoji: msg`Add custom emoji`,
|
||||||
|
gif: msg`Add GIF`,
|
||||||
|
poll: msg`Add poll`,
|
||||||
|
};
|
||||||
|
|
||||||
function Compose({
|
function Compose({
|
||||||
onClose,
|
onClose,
|
||||||
replyToStatus,
|
replyToStatus,
|
||||||
|
@ -209,7 +217,7 @@ function Compose({
|
||||||
standalone,
|
standalone,
|
||||||
hasOpener,
|
hasOpener,
|
||||||
}) {
|
}) {
|
||||||
const { i18n } = useLingui();
|
const { i18n, _ } = useLingui();
|
||||||
const rtf = RTF(i18n.locale);
|
const rtf = RTF(i18n.locale);
|
||||||
const lf = LF(i18n.locale);
|
const lf = LF(i18n.locale);
|
||||||
|
|
||||||
|
@ -732,6 +740,39 @@ function Compose({
|
||||||
states.composerState.minimized = true;
|
states.composerState.minimized = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const gifPickerDisabled =
|
||||||
|
uiState === 'loading' ||
|
||||||
|
(maxMediaAttachments !== undefined &&
|
||||||
|
mediaAttachments.length >= maxMediaAttachments) ||
|
||||||
|
!!poll;
|
||||||
|
|
||||||
|
// If maxOptions is not defined or defined and is greater than 1, show poll button
|
||||||
|
const showPollButton = maxOptions == null || maxOptions > 1;
|
||||||
|
const pollButtonDisabled =
|
||||||
|
uiState === 'loading' || !!poll || !!mediaAttachments.length;
|
||||||
|
const onPollButtonClick = () => {
|
||||||
|
setPoll({
|
||||||
|
options: ['', ''],
|
||||||
|
expiresIn: 24 * 60 * 60, // 1 day
|
||||||
|
multiple: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const addSubToolbarRef = useRef();
|
||||||
|
const [showAddButton, setShowAddButton] = useState(false);
|
||||||
|
useResizeObserver({
|
||||||
|
ref: addSubToolbarRef,
|
||||||
|
box: 'border-box',
|
||||||
|
onResize: ({ width }) => {
|
||||||
|
// If scrollable, it's truncated
|
||||||
|
const { scrollWidth } = addSubToolbarRef.current;
|
||||||
|
const truncated = scrollWidth > width;
|
||||||
|
const overTruncated = width < 84; // roughly two buttons width
|
||||||
|
setShowAddButton(overTruncated || truncated);
|
||||||
|
addSubToolbarRef.current.hidden = overTruncated;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="compose-container-outer">
|
<div id="compose-container-outer">
|
||||||
<div id="compose-container" class={standalone ? 'standalone' : ''}>
|
<div id="compose-container" class={standalone ? 'standalone' : ''}>
|
||||||
|
@ -1318,87 +1359,88 @@ function Compose({
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div
|
<div class="toolbar compose-footer">
|
||||||
class="toolbar wrap"
|
<span class="add-toolbar-button-group spacer">
|
||||||
style={{
|
{showAddButton && (
|
||||||
justifyContent: 'flex-end',
|
<Menu2
|
||||||
}}
|
portal={{
|
||||||
>
|
target: document.body,
|
||||||
<span>
|
|
||||||
<label class="toolbar-button">
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
accept={supportedMimeTypes?.join(',')}
|
|
||||||
multiple={
|
|
||||||
maxMediaAttachments === undefined ||
|
|
||||||
maxMediaAttachments - mediaAttachments >= 2
|
|
||||||
}
|
|
||||||
disabled={
|
|
||||||
uiState === 'loading' ||
|
|
||||||
mediaAttachments.length >= maxMediaAttachments ||
|
|
||||||
!!poll
|
|
||||||
}
|
|
||||||
onChange={(e) => {
|
|
||||||
const files = e.target.files;
|
|
||||||
if (!files) return;
|
|
||||||
|
|
||||||
const mediaFiles = Array.from(files).map((file) => ({
|
|
||||||
file,
|
|
||||||
type: file.type,
|
|
||||||
size: file.size,
|
|
||||||
url: URL.createObjectURL(file),
|
|
||||||
id: null, // indicate uploaded state
|
|
||||||
description: null,
|
|
||||||
}));
|
|
||||||
console.log('MEDIA ATTACHMENTS', files, mediaFiles);
|
|
||||||
|
|
||||||
// Validate max media attachments
|
|
||||||
if (
|
|
||||||
mediaAttachments.length + mediaFiles.length >
|
|
||||||
maxMediaAttachments
|
|
||||||
) {
|
|
||||||
alert(
|
|
||||||
plural(maxMediaAttachments, {
|
|
||||||
one: 'You can only attach up to 1 file.',
|
|
||||||
other: 'You can only attach up to # files.',
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
setMediaAttachments((attachments) => {
|
|
||||||
return attachments.concat(mediaFiles);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Reset
|
|
||||||
e.target.value = '';
|
|
||||||
}}
|
}}
|
||||||
/>
|
containerProps={{
|
||||||
<Icon icon="attachment" />
|
style: {
|
||||||
</label>
|
zIndex: 1001,
|
||||||
{/* If maxOptions is not defined or defined and is greater than 1, show poll button */}
|
},
|
||||||
{maxOptions == null ||
|
}}
|
||||||
(maxOptions > 1 && (
|
menuButton={({ open }) => (
|
||||||
<>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
class={`toolbar-button add-button ${
|
||||||
class="toolbar-button"
|
open ? 'active' : ''
|
||||||
disabled={
|
}`}
|
||||||
uiState === 'loading' ||
|
>
|
||||||
!!poll ||
|
<Icon icon="plus" title={t`Add`} />
|
||||||
!!mediaAttachments.length
|
</button>
|
||||||
}
|
)}
|
||||||
|
>
|
||||||
|
<MenuItem className="compose-menu-add-media">
|
||||||
|
<label class="compose-menu-add-media-field">
|
||||||
|
<FilePickerInput
|
||||||
|
hidden
|
||||||
|
supportedMimeTypes={supportedMimeTypes}
|
||||||
|
maxMediaAttachments={maxMediaAttachments}
|
||||||
|
mediaAttachments={mediaAttachments}
|
||||||
|
disabled={
|
||||||
|
uiState === 'loading' ||
|
||||||
|
mediaAttachments.length >= maxMediaAttachments ||
|
||||||
|
!!poll
|
||||||
|
}
|
||||||
|
setMediaAttachments={setMediaAttachments}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<Icon icon="media" /> <span>{_(ADD_LABELS.media)}</span>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => {
|
||||||
|
setShowEmoji2Picker(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon="emoji2" />{' '}
|
||||||
|
<span>{_(ADD_LABELS.customEmoji)}</span>
|
||||||
|
</MenuItem>
|
||||||
|
{!!states.settings.composerGIFPicker && (
|
||||||
|
<MenuItem
|
||||||
|
disabled={gifPickerDisabled}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPoll({
|
setShowGIFPicker(true);
|
||||||
options: ['', ''],
|
|
||||||
expiresIn: 24 * 60 * 60, // 1 day
|
|
||||||
multiple: false,
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon icon="poll" alt={t`Add poll`} />
|
<span class="icon icon-gif" role="img" />
|
||||||
</button>
|
<span>{_(ADD_LABELS.gif)}</span>
|
||||||
</>
|
</MenuItem>
|
||||||
))}
|
)}
|
||||||
{/* <button
|
<MenuItem
|
||||||
|
disabled={pollButtonDisabled}
|
||||||
|
onClick={onPollButtonClick}
|
||||||
|
>
|
||||||
|
<Icon icon="poll" /> <span>{_(ADD_LABELS.poll)}</span>
|
||||||
|
</MenuItem>
|
||||||
|
</Menu2>
|
||||||
|
)}
|
||||||
|
<span class="add-sub-toolbar-button-group" ref={addSubToolbarRef}>
|
||||||
|
<label class="toolbar-button">
|
||||||
|
<FilePickerInput
|
||||||
|
supportedMimeTypes={supportedMimeTypes}
|
||||||
|
maxMediaAttachments={maxMediaAttachments}
|
||||||
|
mediaAttachments={mediaAttachments}
|
||||||
|
disabled={
|
||||||
|
uiState === 'loading' ||
|
||||||
|
mediaAttachments.length >= maxMediaAttachments ||
|
||||||
|
!!poll
|
||||||
|
}
|
||||||
|
setMediaAttachments={setMediaAttachments}
|
||||||
|
/>
|
||||||
|
<Icon icon="media" alt={_(ADD_LABELS.media)} />
|
||||||
|
</label>
|
||||||
|
{/* <button
|
||||||
type="button"
|
type="button"
|
||||||
class="toolbar-button"
|
class="toolbar-button"
|
||||||
disabled={uiState === 'loading'}
|
disabled={uiState === 'loading'}
|
||||||
|
@ -1408,35 +1450,47 @@ function Compose({
|
||||||
>
|
>
|
||||||
<Icon icon="at" />
|
<Icon icon="at" />
|
||||||
</button> */}
|
</button> */}
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="toolbar-button"
|
|
||||||
disabled={uiState === 'loading'}
|
|
||||||
onClick={() => {
|
|
||||||
setShowEmoji2Picker(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon icon="emoji2" alt={t`Add custom emoji`} />
|
|
||||||
</button>
|
|
||||||
{!!states.settings.composerGIFPicker && (
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="toolbar-button gif-picker-button"
|
class="toolbar-button"
|
||||||
disabled={
|
disabled={uiState === 'loading'}
|
||||||
uiState === 'loading' ||
|
|
||||||
(maxMediaAttachments !== undefined &&
|
|
||||||
mediaAttachments.length >= maxMediaAttachments) ||
|
|
||||||
!!poll
|
|
||||||
}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowGIFPicker(true);
|
setShowEmoji2Picker(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span>GIF</span>
|
<Icon icon="emoji2" alt={_(ADD_LABELS.customEmoji)} />
|
||||||
</button>
|
</button>
|
||||||
)}
|
{!!states.settings.composerGIFPicker && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="toolbar-button gif-picker-button"
|
||||||
|
disabled={gifPickerDisabled}
|
||||||
|
onClick={() => {
|
||||||
|
setShowGIFPicker(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon icon-gif"
|
||||||
|
aria-label={_(ADD_LABELS.gif)}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{}
|
||||||
|
{showPollButton && (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="toolbar-button"
|
||||||
|
disabled={pollButtonDisabled}
|
||||||
|
onClick={onPollButtonClick}
|
||||||
|
>
|
||||||
|
<Icon icon="poll" alt={_(ADD_LABELS.poll)} />
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<div class="spacer" />
|
{/* <div class="spacer" /> */}
|
||||||
{uiState === 'loading' ? (
|
{uiState === 'loading' ? (
|
||||||
<Loader abrupt />
|
<Loader abrupt />
|
||||||
) : (
|
) : (
|
||||||
|
@ -1495,11 +1549,7 @@ function Compose({
|
||||||
})}
|
})}
|
||||||
</select>
|
</select>
|
||||||
</label>{' '}
|
</label>{' '}
|
||||||
<button
|
<button type="submit" disabled={uiState === 'loading'}>
|
||||||
type="submit"
|
|
||||||
class="large"
|
|
||||||
disabled={uiState === 'loading'}
|
|
||||||
>
|
|
||||||
{replyToStatus
|
{replyToStatus
|
||||||
? t`Reply`
|
? t`Reply`
|
||||||
: editStatus
|
: editStatus
|
||||||
|
@ -1671,6 +1721,58 @@ function Compose({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function FilePickerInput({
|
||||||
|
hidden,
|
||||||
|
supportedMimeTypes,
|
||||||
|
maxMediaAttachments,
|
||||||
|
mediaAttachments,
|
||||||
|
disabled = false,
|
||||||
|
setMediaAttachments,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
hidden={hidden}
|
||||||
|
accept={supportedMimeTypes?.join(',')}
|
||||||
|
multiple={
|
||||||
|
maxMediaAttachments === undefined ||
|
||||||
|
maxMediaAttachments - mediaAttachments >= 2
|
||||||
|
}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={(e) => {
|
||||||
|
const files = e.target.files;
|
||||||
|
if (!files) return;
|
||||||
|
|
||||||
|
const mediaFiles = Array.from(files).map((file) => ({
|
||||||
|
file,
|
||||||
|
type: file.type,
|
||||||
|
size: file.size,
|
||||||
|
url: URL.createObjectURL(file),
|
||||||
|
id: null, // indicate uploaded state
|
||||||
|
description: null,
|
||||||
|
}));
|
||||||
|
console.log('MEDIA ATTACHMENTS', files, mediaFiles);
|
||||||
|
|
||||||
|
// Validate max media attachments
|
||||||
|
if (mediaAttachments.length + mediaFiles.length > maxMediaAttachments) {
|
||||||
|
alert(
|
||||||
|
plural(maxMediaAttachments, {
|
||||||
|
one: 'You can only attach up to 1 file.',
|
||||||
|
other: 'You can only attach up to # files.',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setMediaAttachments((attachments) => {
|
||||||
|
return attachments.concat(mediaFiles);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Reset
|
||||||
|
e.target.value = '';
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function autoResizeTextarea(textarea) {
|
function autoResizeTextarea(textarea) {
|
||||||
if (!textarea) return;
|
if (!textarea) return;
|
||||||
const { value, offsetHeight, scrollHeight, clientHeight } = textarea;
|
const { value, offsetHeight, scrollHeight, clientHeight } = textarea;
|
||||||
|
|
Loading…
Reference in a new issue