pl-fe: allow posting from drive on iceshrimp.net

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2025-11-27 15:16:09 +01:00
parent e9c3ff44cb
commit 86cdae3682
8 changed files with 83 additions and 27 deletions

View File

@ -519,7 +519,7 @@ const uploadCompose = (composeId: string, files: FileList, intl: IntlShape) =>
dispatch(uploadFile(
f,
intl,
(data) => dispatch(uploadComposeSuccess(composeId, data, f)),
(data) => dispatch(uploadComposeSuccess(composeId, data)),
(error) => dispatch(uploadComposeFail(composeId, error)),
({ loaded }) => {
progress[i] = loaded;
@ -543,11 +543,10 @@ const uploadComposeProgress = (composeId: string, loaded: number, total: number)
total,
});
const uploadComposeSuccess = (composeId: string, media: MediaAttachment, file: File) => ({
const uploadComposeSuccess = (composeId: string, media: MediaAttachment) => ({
type: COMPOSE_UPLOAD_SUCCESS,
composeId,
media,
file,
});
const uploadComposeFail = (composeId: string, error: unknown) => ({

View File

@ -42,6 +42,7 @@ import { countableText } from '../util/counter';
import ClearLinkSuggestion from './clear-link-suggestion';
import ContentTypeButton from './content-type-button';
import DriveButton from './drive-button';
import HashtagCasingSuggestion from './hashtag-casing-suggestion';
import InteractionPolicyButton from './interaction-policy-button';
import LanguageDropdown from './language-dropdown';
@ -281,6 +282,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
const renderButtons = useCallback(() => (
<div className='⁂-compose-form__buttons'>
<UploadButtonContainer composeId={id} />
{features.drive && <DriveButton composeId={id} />}
<EmojiPickerDropdown onPickEmoji={handleEmojiPick} condensed={shouldCondense} />
{features.polls && <PollButton composeId={id} />}
{features.scheduledStatuses && <ScheduleButton composeId={id} />}

View File

@ -0,0 +1,62 @@
import { mediaAttachmentSchema } from 'pl-api';
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import * as v from 'valibot';
import { uploadComposeSuccess } from 'pl-fe/actions/compose';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useInstance } from 'pl-fe/hooks/use-instance';
import { useModalsActions } from 'pl-fe/stores/modals';
import ComposeFormButton from './compose-form-button';
const messages = defineMessages({
button: { id: 'compose_form.drive_button', defaultMessage: 'Select from drive' },
});
interface IDriveButton {
composeId: string;
}
const DriveButton: React.FC<IDriveButton> = ({ composeId }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const { configuration } = useInstance();
const { openModal } = useModalsActions();
const attachmentTypes = configuration.media_attachments.supported_mime_types;
const onClick = () => openModal('SELECT_DRIVE_FILE', {
title: intl.formatMessage(messages.button),
type: 'file',
accepted: (attachmentTypes?.length === 0 && attachmentTypes[0] === 'application/octet-stream') ? undefined : attachmentTypes,
onSelect: (file) => {
let type = file.content_type.split('/')[0] as 'image' | 'video' | 'audio' | 'unknown';
if (!['image', 'video', 'audio', 'unknown'].includes(type)) {
type = 'unknown';
}
const mediaAttachment = v.parse(mediaAttachmentSchema, {
id: file.id,
url: file.url,
preview_url: file.thumbnail_url,
remote_url: file.url,
description: file.description || '',
type,
mime_type: file.content_type,
});
dispatch(uploadComposeSuccess(composeId, mediaAttachment));
},
});
return (
<ComposeFormButton
icon={require('@phosphor-icons/core/regular/cloud-arrow-up.svg')}
title={intl.formatMessage(messages.button)}
onClick={onClick}
/>
);
};
export { DriveButton as default };

View File

@ -542,6 +542,7 @@
"compose_form.approval_required": "The reply needs to be approved by the post author.",
"compose_form.content_type.change": "Change content type",
"compose_form.direct_message_warning": "This post will only be sent to the mentioned users.",
"compose_form.drive_button": "Select from drive",
"compose_form.event_placeholder": "Post to this event",
"compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
"compose_form.interaction_policy.label": "Manage interaction policy",

View File

@ -19,6 +19,7 @@ type SelectDriveFileModalProps = {
} & ({
type: 'file';
onSelect: (file: DriveFile) => void;
accepted?: Array<string>;
} | {
type: 'folder';
onSelect: (folder: DriveFolder) => void;
@ -89,7 +90,7 @@ const File: React.FC<IFile> = ({ file, active, disabled, onSelect }) => {
);
};
const SelectDriveFileModal: React.FC<SelectDriveFileModalProps & BaseModalProps> = ({ onClose, onSelect, type, disabled, title }) => {
const SelectDriveFileModal: React.FC<SelectDriveFileModalProps & BaseModalProps> = ({ onClose, onSelect, type, disabled, title, ...props }) => {
const onClickClose = () => {
onClose('SELECT_DRIVE_FILE');
};
@ -149,7 +150,7 @@ const SelectDriveFileModal: React.FC<SelectDriveFileModalProps & BaseModalProps>
key={file.id}
file={file}
active={selectedFile === file.id}
disabled={type === 'folder' || disabled?.includes(file.id)}
disabled={type === 'folder' || disabled?.includes(file.id) || ('accepted' in props && props.accepted && !props.accepted.includes(file.content_type))}
onSelect={({ id }) => {
if (type === 'file') {
setSelectedFile(id);

View File

@ -1,8 +1,10 @@
import defaultIcon from '@phosphor-icons/core/regular/paperclip.svg';
import { clsx } from 'clsx';
import { mediaAttachmentSchema, type DriveFile, type DriveFolder } from 'pl-api';
import React, { useMemo } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { Link, useHistory } from 'react-router-dom';
import * as v from 'valibot';
import DropdownMenu, { Menu } from 'pl-fe/components/dropdown-menu';
import { EmptyMessage } from 'pl-fe/components/empty-message';
@ -17,8 +19,6 @@ import { useModalsActions } from 'pl-fe/stores/modals';
import toast from 'pl-fe/toast';
import { download } from 'pl-fe/utils/download';
import type { DriveFile, DriveFolder, MediaAttachment } from 'pl-api';
const messages = defineMessages({
heading: { id: 'column.drive', defaultMessage: 'Drive' },
folderDropdown: { id: 'drive.folder.dropdown', defaultMessage: 'Folder menu' },
@ -165,16 +165,20 @@ const File: React.FC<IFile> = ({ file }) => {
return;
}
const mediaAttachment = {
let type = file.content_type.split('/')[0] as 'image' | 'video' | 'audio' | 'unknown';
if (!['image', 'video', 'audio', 'unknown'].includes(type)) {
type = 'unknown';
}
const mediaAttachment = v.parse(mediaAttachmentSchema, {
id: file.id,
url: file.url,
preview_url: file.thumbnail_url,
remote_url: file.url,
description: file.description || '',
type: file.content_type.split('/')[0] as 'image' | 'video' | 'audio' | 'unknown',
type,
mime_type: file.content_type,
blurhash: null,
} as MediaAttachment;
});
openModal('MEDIA', {
media: [mediaAttachment],

View File

@ -75,7 +75,7 @@ const CirclePage: React.FC = () => {
const file = new File([blob!], 'interactions_circle.png', { type: 'image/png' });
dispatch(uploadFile(file, intl, (data) => {
dispatch(uploadComposeSuccess('compose-modal', data, file));
dispatch(uploadComposeSuccess('compose-modal', data));
openModal('COMPOSE');
}));
}, 'image/png');

View File

@ -142,21 +142,8 @@
}
&__label {
// margin-top: auto;
// overflow: hidden;
// display: inline;
// display: -webkit-box;
// -webkit-box-orient: vertical;
// -webkit-line-clamp: 3;
// max-width: 100%;
// text-overflow: ellipsis;
// border-radius: 0.25rem;
// background-color: rgb(var(--color-gray-900));
// padding: 0.25rem 0.5rem;
// font-size: 0.75rem;
// line-height: 1rem;
// font-weight: 500;
// color: #fff;
overflow: hidden;
text-overflow: ellipsis;
}
&--active {