mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-02-02 06:06:41 +01:00
Experiment: allow minimize composer
This commit is contained in:
parent
8aab997900
commit
cd17ca0b42
13 changed files with 216 additions and 30 deletions
41
src/app.css
41
src/app.css
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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'),
|
||||
};
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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={
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
27
src/utils/show-compose.js
Normal 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;
|
||||
}
|
|
@ -40,6 +40,7 @@ const states = proxy({
|
|||
statusReply: {},
|
||||
accounts: {},
|
||||
routeNotification: null,
|
||||
composerState: {},
|
||||
// Modals
|
||||
showCompose: false,
|
||||
showSettings: false,
|
||||
|
|
Loading…
Reference in a new issue