1
0
Fork 0

Experiment: allow minimize composer

This commit is contained in:
Lim Chee Aun 2024-05-24 12:30:20 +08:00
parent 8aab997900
commit cd17ca0b42
13 changed files with 216 additions and 30 deletions

View file

@ -1609,6 +1609,47 @@ body:has(.media-modal-container + .status-deck) .media-post-link {
bottom: calc(16px + env(safe-area-inset-bottom) + 52px);
}
}
#compose-button {
&.min {
outline: 2px solid var(--button-text-color);
&:after {
content: '';
display: block;
position: absolute;
top: 0;
right: 0;
width: 14px;
height: 14px;
border-radius: 50%;
background-color: var(--button-bg-color);
border: 2px solid var(--button-text-color);
box-shadow: 0 2px 8px var(--drop-shadow-color);
opacity: 0;
transition: opacity 0.2s ease-out 0.5s;
opacity: 1;
}
}
&.loading {
outline-color: var(--button-bg-blur-color);
&:before {
position: absolute;
inset: 0;
content: '';
border-radius: 50%;
animation: spin 5s linear infinite;
border: 2px dashed var(--button-text-color);
}
}
&.error {
&:after {
background-color: var(--red-color);
}
}
}
/* SHEET */

View file

@ -108,4 +108,5 @@ export const ICONS = {
settings: () => import('@iconify-icons/mingcute/settings-6-line'),
'heart-break': () => import('@iconify-icons/mingcute/heart-crack-line'),
'user-x': () => import('@iconify-icons/mingcute/user-x-line'),
minimize: () => import('@iconify-icons/mingcute/arrows-down-line'),
};

View file

@ -19,6 +19,7 @@ import { getLists } from '../utils/lists';
import niceDateTime from '../utils/nice-date-time';
import pmem from '../utils/pmem';
import shortenNumber from '../utils/shorten-number';
import showCompose from '../utils/show-compose';
import showToast from '../utils/show-toast';
import states, { hideAllModals } from '../utils/states';
import store from '../utils/store';
@ -1081,11 +1082,11 @@ function RelatedActions({
<>
<MenuItem
onClick={() => {
states.showCompose = {
showCompose({
draftStatus: {
status: `@${currentInfo?.acct || acct} `,
},
};
});
}}
>
<Icon icon="at" />

View file

@ -1,4 +1,5 @@
import { useHotkeys } from 'react-hotkeys-hook';
import { useSnapshot } from 'valtio';
import openCompose from '../utils/open-compose';
import openOSK from '../utils/open-osk';
@ -7,7 +8,15 @@ import states from '../utils/states';
import Icon from './icon';
export default function ComposeButton() {
const snapStates = useSnapshot(states);
function handleButton(e) {
if (snapStates.composerState.minimized) {
states.composerState.minimized = false;
openOSK();
return;
}
if (e.shiftKey) {
const newWin = openCompose();
@ -28,7 +37,14 @@ export default function ComposeButton() {
});
return (
<button type="button" id="compose-button" onClick={handleButton}>
<button
type="button"
id="compose-button"
onClick={handleButton}
class={`${snapStates.composerState.minimized ? 'min' : ''} ${
snapStates.composerState.publishing ? 'loading' : ''
} ${snapStates.composerState.publishingError ? 'error' : ''}`}
>
<Icon icon="quill" size="xl" alt="Compose" />
</button>
);

View file

@ -514,6 +514,7 @@ function Compose({
// I don't think this warrant a draft mode for a status that's already posted
// Maybe it could be a big edit change but it should be rare
if (editStatus) return;
if (states.composerState.minimized) return;
const key = draftKey();
const backgroundDraft = {
key,
@ -670,6 +671,11 @@ function Compose({
[replyToStatus],
);
const onMinimize = () => {
saveUnsavedDraft();
states.composerState.minimized = true;
};
return (
<div id="compose-container-outer">
<div id="compose-container" class={standalone ? 'standalone' : ''}>
@ -689,7 +695,7 @@ function Compose({
/>
)}
{!standalone ? (
<span>
<span class="button-group">
<button
type="button"
class="light pop-button"
@ -736,6 +742,13 @@ function Compose({
>
<Icon icon="popout" alt="Pop out" />
</button>{' '}
<button
type="button"
class="light min-button"
onClick={onMinimize}
>
<Icon icon="minimize" alt="Minimize" />
</button>{' '}
<button
type="button"
class="light close-button"
@ -810,6 +823,10 @@ function Compose({
} else {
window.opener.__STATES__.showCompose = true;
}
if (window.opener.__STATES__.composerState.minimized) {
// Maximize it
window.opener.__STATES__.composerState.minimized = false;
}
},
});
}}
@ -915,6 +932,8 @@ function Compose({
spoilerText = (sensitive && spoilerText) || undefined;
status = status === '' ? undefined : status;
// states.composerState.minimized = true;
states.composerState.publishing = true;
setUIState('loading');
(async () => {
try {
@ -948,6 +967,8 @@ function Compose({
return result.status === 'rejected' || !result.value?.id;
})
) {
states.composerState.publishing = false;
states.composerState.publishingError = true;
setUIState('error');
// Alert all the reasons
results.forEach((result) => {
@ -1021,6 +1042,8 @@ function Compose({
newStatus = await masto.v1.statuses.create(params);
}
}
states.composerState.minimized = false;
states.composerState.publishing = false;
setUIState('default');
// Close
@ -1031,6 +1054,8 @@ function Compose({
instance,
});
} catch (e) {
states.composerState.publishing = false;
states.composerState.publishingError = true;
console.error(e);
alert(e?.reason || e);
setUIState('error');

View file

@ -10,17 +10,56 @@
align-items: center;
background-color: var(--backdrop-color);
animation: appear 0.5s var(--timing-function) both;
transition: all 0.5s var(--timing-function);
&.solid {
background-color: var(--backdrop-solid-color);
}
--compose-button-dimension: 56px;
--compose-button-dimension-half: calc(var(--compose-button-dimension) / 2);
--compose-button-dimension-margin: 16px;
&.min {
/* Minimized */
pointer-events: none;
user-select: none;
overflow: hidden;
transform: scale(0);
--right: max(
var(--compose-button-dimension-margin),
env(safe-area-inset-right)
);
--bottom: max(
var(--compose-button-dimension-margin),
env(safe-area-inset-bottom)
);
--origin-right: calc(
100% - var(--compose-button-dimension-half) - var(--right)
);
--origin-bottom: calc(
100% - var(--compose-button-dimension-half) - var(--bottom)
);
transform-origin: var(--origin-right) var(--origin-bottom);
}
.sheet {
transition: transform 0.3s var(--timing-function);
transform-origin: center bottom;
transform-origin: 80% 80%;
}
&:has(~ div) .sheet {
transform: scale(0.975);
}
}
@media (max-width: calc(40em - 1px)) {
#app[data-shortcuts-view-mode='tab-menu-bar'] ~ #modal-container > div.min {
border: 2px solid red;
--bottom: calc(
var(--compose-button-dimension-margin) + env(safe-area-inset-bottom) +
52px
);
}
}

View file

@ -8,7 +8,7 @@ import useCloseWatcher from '../utils/useCloseWatcher';
const $modalContainer = document.getElementById('modal-container');
function Modal({ children, onClose, onClick, class: className }) {
function Modal({ children, onClose, onClick, class: className, minimized }) {
if (!children) return null;
const modalRef = useRef();
@ -43,21 +43,30 @@ function Modal({ children, onClose, onClick, class: className }) {
useEffect(() => {
const $deckContainers = document.querySelectorAll('.deck-container');
if (children) {
$deckContainers.forEach(($deckContainer) => {
$deckContainer.setAttribute('inert', '');
});
if (minimized) {
// Similar to focusDeck in focus-deck.jsx
// Focus last deck
const page = $deckContainers[$deckContainers.length - 1]; // last one
if (page && page.tabIndex === -1) {
page.focus();
}
} else {
$deckContainers.forEach(($deckContainer) => {
$deckContainer.removeAttribute('inert');
});
if (children) {
$deckContainers.forEach(($deckContainer) => {
$deckContainer.setAttribute('inert', '');
});
} else {
$deckContainers.forEach(($deckContainer) => {
$deckContainer.removeAttribute('inert');
});
}
}
return () => {
$deckContainers.forEach(($deckContainer) => {
$deckContainer.removeAttribute('inert');
});
};
}, [children]);
}, [children, minimized]);
const Modal = (
<div
@ -72,7 +81,8 @@ function Modal({ children, onClose, onClick, class: className }) {
onClose?.(e);
}
}}
tabIndex="-1"
tabIndex={minimized ? 0 : '-1'}
inert={minimized}
onFocus={(e) => {
try {
if (e.target === e.currentTarget) {

View file

@ -39,7 +39,10 @@ export default function Modals() {
return (
<>
{!!snapStates.showCompose && (
<Modal class="solid">
<Modal
class={`solid ${snapStates.composerState.minimized ? 'min' : ''}`}
minimized={!!snapStates.composerState.minimized}
>
<IntlSegmenterSuspense>
<Compose
replyToStatus={

View file

@ -51,6 +51,7 @@ import openCompose from '../utils/open-compose';
import pmem from '../utils/pmem';
import safeBoundingBoxPadding from '../utils/safe-bounding-box-padding';
import shortenNumber from '../utils/shorten-number';
import showCompose from '../utils/show-compose';
import showToast from '../utils/show-toast';
import { speak, supportsTTS } from '../utils/speech';
import states, { getStatus, saveStatus, statusKey } from '../utils/states';
@ -524,9 +525,9 @@ function Status({
});
if (newWin) return;
}
states.showCompose = {
showCompose({
replyToStatus: status,
};
});
};
// Check if media has no descriptions
@ -771,11 +772,11 @@ function Status({
menuExtras={
<MenuItem
onClick={() => {
states.showCompose = {
showCompose({
draftStatus: {
status: `\n${url}`,
},
};
});
}}
>
<Icon icon="quote" />
@ -1092,9 +1093,9 @@ function Status({
{supports('@mastodon/post-edit') && (
<MenuItem
onClick={() => {
states.showCompose = {
showCompose({
editStatus: status,
};
});
}}
>
<Icon icon="pencil" />
@ -2125,11 +2126,11 @@ function Status({
menuExtras={
<MenuItem
onClick={() => {
states.showCompose = {
showCompose({
draftStatus: {
status: `\n${url}`,
},
};
});
}}
>
<Icon icon="quote" />

View file

@ -388,6 +388,27 @@ select.plain {
background-color: transparent;
}
.button-group {
display: flex;
button,
.button {
margin-inline: calc(-1 * var(--hairline-width));
&:first-child:not(:only-child) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&:not(:first-child, :last-child, :only-child) {
border-radius: 0;
}
&:last-child:not(:only-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
}
}
pre {
tab-size: 2;
}
@ -547,3 +568,9 @@ kbd {
.shazam-container-horizontal[hidden] {
grid-template-columns: 0fr;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}

View file

@ -23,12 +23,6 @@
}
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.hero-heading {
font-size: var(--text-size);
display: inline-block;

27
src/utils/show-compose.js Normal file
View file

@ -0,0 +1,27 @@
import openOSK from './open-osk';
import showToast from './show-toast';
import states from './states';
const TOAST_DURATION = 5_000; // 5 seconds
export default function showCompose(opts) {
if (!opts) opts = true;
if (states.showCompose) {
if (states.composerState.minimized) {
showToast({
duration: TOAST_DURATION,
text: `A draft post is currently minimized. Post or discard it before creating a new one.`,
});
} else {
showToast({
duration: TOAST_DURATION,
text: `A post is currently open. Post or discard it before creating a new one.`,
});
}
return;
}
openOSK();
states.showCompose = opts;
}

View file

@ -40,6 +40,7 @@ const states = proxy({
statusReply: {},
accounts: {},
routeNotification: null,
composerState: {},
// Modals
showCompose: false,
showSettings: false,