mirror of
https://github.com/cheeaun/phanpy.git
synced 2024-12-18 04:51:46 +03:00
New feature: pop-out compose window
- More consistent design for both reply-to status and source status preview - Fixed bugs too - Make sure index.css is always above
This commit is contained in:
parent
3e80ee03f3
commit
9d78e67381
10 changed files with 329 additions and 50 deletions
|
@ -3,7 +3,13 @@
|
||||||
"useTabs": false,
|
"useTabs": false,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "all",
|
"trailingComma": "all",
|
||||||
"importOrder": [".css$", "<THIRD_PARTY_MODULES>", "^../", "^[./]"],
|
"importOrder": [
|
||||||
|
"index.css$",
|
||||||
|
".css$",
|
||||||
|
"<THIRD_PARTY_MODULES>",
|
||||||
|
"^../",
|
||||||
|
"^[./]"
|
||||||
|
],
|
||||||
"importOrderSeparation": true,
|
"importOrderSeparation": true,
|
||||||
"importOrderSortSpecifiers": true,
|
"importOrderSortSpecifiers": true,
|
||||||
"importOrderGroupNamespaceSpecifiers": true,
|
"importOrderGroupNamespaceSpecifiers": true,
|
||||||
|
|
14
compose/index.html
Normal file
14
compose/index.html
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Compose / Phanpy</title>
|
||||||
|
<meta name="color-scheme" content="dark light" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/compose.jsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -23,7 +23,7 @@ import store from './utils/store';
|
||||||
|
|
||||||
const { VITE_CLIENT_NAME: CLIENT_NAME } = import.meta.env;
|
const { VITE_CLIENT_NAME: CLIENT_NAME } = import.meta.env;
|
||||||
|
|
||||||
window._STATES = states;
|
window.__STATES__ = states;
|
||||||
|
|
||||||
async function startStream() {
|
async function startStream() {
|
||||||
const stream = await masto.stream.streamUser();
|
const stream = await masto.stream.streamUser();
|
||||||
|
@ -267,6 +267,7 @@ export function App() {
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
editStatus={snapStates.showCompose?.editStatus || null}
|
editStatus={snapStates.showCompose?.editStatus || null}
|
||||||
|
draftStatus={snapStates.showCompose?.draftStatus || null}
|
||||||
onClose={(result) => {
|
onClose={(result) => {
|
||||||
states.showCompose = false;
|
states.showCompose = false;
|
||||||
if (result) {
|
if (result) {
|
||||||
|
|
|
@ -19,11 +19,6 @@
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
#compose-container .close-button {
|
|
||||||
padding: 6px;
|
|
||||||
color: var(--text-insignificant-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
#compose-container textarea {
|
#compose-container textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
@ -42,18 +37,21 @@
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#compose-container .reply-to {
|
#compose-container .status-preview {
|
||||||
border-radius: 8px 8px 0 0;
|
border-radius: 8px 8px 0 0;
|
||||||
max-height: 160px;
|
max-height: 160px;
|
||||||
pointer-events: none;
|
background-color: var(--bg-color);
|
||||||
filter: saturate(0.25) opacity(0.75);
|
|
||||||
background-color: var(--bg-blur-color);
|
|
||||||
margin: 0 12px;
|
margin: 0 12px;
|
||||||
border: 1px solid var(--outline-color);
|
border: 1px solid var(--outline-color);
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
/* box-shadow: 0 0 12px var(--divider-color); */
|
|
||||||
/* mask-image: linear-gradient(rgba(0, 0, 0, 1), rgba(0, 0, 0, 1) 90%, transparent); */
|
|
||||||
animation: appear-up 1s ease-in-out;
|
animation: appear-up 1s ease-in-out;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
#compose-container.standalone .status-preview * {
|
||||||
|
/*
|
||||||
|
For standalone mode (new window), prevent interacting with the status preview for now
|
||||||
|
*/
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
@keyframes appear-down {
|
@keyframes appear-down {
|
||||||
0% {
|
0% {
|
||||||
|
@ -63,6 +61,38 @@
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#compose-container .status-preview-legend {
|
||||||
|
pointer-events: none;
|
||||||
|
position: sticky;
|
||||||
|
bottom: 0;
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 80%;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--text-insignificant-color);
|
||||||
|
background-color: var(--bg-blur-color);
|
||||||
|
/* background-image: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
transparent,
|
||||||
|
var(--bg-faded-color)
|
||||||
|
); */
|
||||||
|
border-top: 1px solid var(--outline-color);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
text-shadow: 0 1px 10px var(--bg-color), 0 1px 10px var(--bg-color),
|
||||||
|
0 1px 10px var(--bg-color), 0 1px 10px var(--bg-color),
|
||||||
|
0 1px 10px var(--bg-color);
|
||||||
|
}
|
||||||
|
#_compose-container .status-preview-legend.reply-to {
|
||||||
|
color: var(--reply-to-color);
|
||||||
|
background-color: var(--reply-to-faded-color);
|
||||||
|
/* background-image: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
transparent,
|
||||||
|
var(--reply-to-faded-color)
|
||||||
|
); */
|
||||||
|
}
|
||||||
|
|
||||||
#compose-container form {
|
#compose-container form {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
|
@ -70,7 +100,7 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
#compose-container .reply-to ~ form {
|
#compose-container .status-preview ~ form {
|
||||||
animation: appear-down 1s ease-in-out;
|
animation: appear-down 1s ease-in-out;
|
||||||
box-shadow: 0 -12px 12px -12px var(--divider-color);
|
box-shadow: 0 -12px 12px -12px var(--divider-color);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,13 @@ import Status from './status';
|
||||||
- Max character limit includes BOTH status text and Content Warning text
|
- Max character limit includes BOTH status text and Content Warning text
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default ({ onClose, replyToStatus, editStatus }) => {
|
export default ({
|
||||||
|
onClose,
|
||||||
|
replyToStatus,
|
||||||
|
editStatus,
|
||||||
|
draftStatus,
|
||||||
|
standalone,
|
||||||
|
}) => {
|
||||||
const [uiState, setUIState] = useState('default');
|
const [uiState, setUIState] = useState('default');
|
||||||
|
|
||||||
const accounts = store.local.getJSON('accounts');
|
const accounts = store.local.getJSON('accounts');
|
||||||
|
@ -51,27 +57,34 @@ export default ({ onClose, replyToStatus, editStatus }) => {
|
||||||
|
|
||||||
const textareaRef = useRef();
|
const textareaRef = useRef();
|
||||||
|
|
||||||
const [visibility, setVisibility] = useState(
|
const [visibility, setVisibility] = useState('public');
|
||||||
replyToStatus?.visibility || 'public',
|
const [sensitive, setSensitive] = useState(false);
|
||||||
);
|
|
||||||
const [sensitive, setSensitive] = useState(replyToStatus?.sensitive || false);
|
|
||||||
const spoilerTextRef = useRef();
|
const spoilerTextRef = useRef();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let timer = setTimeout(() => {
|
if (replyToStatus) {
|
||||||
const spoilerText = replyToStatus?.spoilerText;
|
const { spoilerText, visibility, sensitive } = replyToStatus;
|
||||||
if (spoilerText && spoilerTextRef.current) {
|
if (spoilerText && spoilerTextRef.current) {
|
||||||
spoilerTextRef.current.value = spoilerText;
|
spoilerTextRef.current.value = spoilerText;
|
||||||
spoilerTextRef.current.focus();
|
spoilerTextRef.current.focus();
|
||||||
} else {
|
} else {
|
||||||
textareaRef.current?.focus();
|
textareaRef.current.focus();
|
||||||
|
if (replyToStatus.account.id !== currentAccount) {
|
||||||
|
textareaRef.current.value = `@${replyToStatus.account.acct} `;
|
||||||
}
|
}
|
||||||
}, 0);
|
}
|
||||||
return () => clearTimeout(timer);
|
setVisibility(visibility);
|
||||||
}, []);
|
setSensitive(sensitive);
|
||||||
|
}
|
||||||
useEffect(() => {
|
if (draftStatus) {
|
||||||
if (editStatus) {
|
const { status, spoilerText, visibility, sensitive, mediaAttachments } =
|
||||||
|
draftStatus;
|
||||||
|
textareaRef.current.value = status;
|
||||||
|
spoilerTextRef.current.value = spoilerText;
|
||||||
|
setVisibility(visibility);
|
||||||
|
setSensitive(sensitive);
|
||||||
|
setMediaAttachments(mediaAttachments);
|
||||||
|
} else if (editStatus) {
|
||||||
const { visibility, sensitive, mediaAttachments } = editStatus;
|
const { visibility, sensitive, mediaAttachments } = editStatus;
|
||||||
setUIState('loading');
|
setUIState('loading');
|
||||||
(async () => {
|
(async () => {
|
||||||
|
@ -93,7 +106,7 @@ export default ({ onClose, replyToStatus, editStatus }) => {
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
}, [editStatus]);
|
}, [draftStatus, editStatus, replyToStatus]);
|
||||||
|
|
||||||
const textExpanderRef = useRef();
|
const textExpanderRef = useRef();
|
||||||
const textExpanderTextRef = useRef('');
|
const textExpanderTextRef = useRef('');
|
||||||
|
@ -192,13 +205,32 @@ export default ({ onClose, replyToStatus, editStatus }) => {
|
||||||
const beforeUnloadCopy =
|
const beforeUnloadCopy =
|
||||||
'You have unsaved changes. Are you sure you want to discard this post?';
|
'You have unsaved changes. Are you sure you want to discard this post?';
|
||||||
const canClose = () => {
|
const canClose = () => {
|
||||||
// check for status or mediaAttachments
|
|
||||||
const { value, dataset } = textareaRef.current;
|
const { value, dataset } = textareaRef.current;
|
||||||
const containNonIDMediaAttachments =
|
|
||||||
|
// check for non-ID media attachments
|
||||||
|
const hasNonIDMediaAttachments =
|
||||||
mediaAttachments.length > 0 &&
|
mediaAttachments.length > 0 &&
|
||||||
mediaAttachments.some((media) => !media.id);
|
mediaAttachments.some((media) => !media.id);
|
||||||
|
|
||||||
if ((value && value !== dataset?.source) || containNonIDMediaAttachments) {
|
// check if status contains only "@acct", if replying
|
||||||
|
const hasAcct =
|
||||||
|
replyToStatus && value.trim() === `@${replyToStatus.account.acct}`;
|
||||||
|
|
||||||
|
// check if status is different than source
|
||||||
|
const differentThanSource = dataset?.source && value !== dataset.source;
|
||||||
|
|
||||||
|
console.log({
|
||||||
|
value,
|
||||||
|
hasAcct,
|
||||||
|
differentThanSource,
|
||||||
|
hasNonIDMediaAttachments,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
(value && !hasAcct) ||
|
||||||
|
differentThanSource ||
|
||||||
|
hasNonIDMediaAttachments
|
||||||
|
) {
|
||||||
const yes = confirm(beforeUnloadCopy);
|
const yes = confirm(beforeUnloadCopy);
|
||||||
return yes;
|
return yes;
|
||||||
}
|
}
|
||||||
|
@ -223,7 +255,7 @@ export default ({ onClose, replyToStatus, editStatus }) => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="compose-container">
|
<div id="compose-container" class={standalone ? 'standalone' : ''}>
|
||||||
<div class="compose-top">
|
<div class="compose-top">
|
||||||
{currentAccountInfo?.avatarStatic && (
|
{currentAccountInfo?.avatarStatic && (
|
||||||
<Avatar
|
<Avatar
|
||||||
|
@ -232,6 +264,66 @@ export default ({ onClose, replyToStatus, editStatus }) => {
|
||||||
alt={currentAccountInfo.username}
|
alt={currentAccountInfo.username}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{!standalone ? (
|
||||||
|
<span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="light"
|
||||||
|
onClick={() => {
|
||||||
|
// If there are non-ID media attachments (not yet uploaded), show confirmation dialog because they are not going to be passed to the new window
|
||||||
|
const containNonIDMediaAttachments =
|
||||||
|
mediaAttachments.length > 0 &&
|
||||||
|
mediaAttachments.some((media) => !media.id);
|
||||||
|
if (containNonIDMediaAttachments) {
|
||||||
|
const yes = confirm(
|
||||||
|
'You have media attachments that are not yet uploaded. Opening a new window will discard them and you will need to re-attach them. Are you sure you want to continue?',
|
||||||
|
);
|
||||||
|
if (!yes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL('/compose/', window.location);
|
||||||
|
const screenWidth = window.screen.width;
|
||||||
|
const screenHeight = window.screen.height;
|
||||||
|
const left = Math.max(0, (screenWidth - 600) / 2);
|
||||||
|
const top = Math.max(0, (screenHeight - 450) / 2);
|
||||||
|
const width = Math.min(screenWidth, 600);
|
||||||
|
const height = Math.min(screenHeight, 450);
|
||||||
|
const newWin = window.open(
|
||||||
|
url,
|
||||||
|
'compose' + Math.random(),
|
||||||
|
`width=${width},height=${height},left=${left},top=${top}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!newWin) {
|
||||||
|
alert('Looks like your browser is blocking popups.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mediaAttachmentsWithIDs = mediaAttachments.filter(
|
||||||
|
(media) => media.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
newWin.masto = masto;
|
||||||
|
newWin.__COMPOSE__ = {
|
||||||
|
editStatus,
|
||||||
|
replyToStatus,
|
||||||
|
draftStatus: {
|
||||||
|
status: textareaRef.current.value,
|
||||||
|
spoilerText: spoilerTextRef.current.value,
|
||||||
|
visibility,
|
||||||
|
sensitive,
|
||||||
|
mediaAttachments: mediaAttachmentsWithIDs,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
onClose(() => {
|
||||||
|
window.opener.__STATES__.reloadStatusPage++;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon="popout" alt="Pop out" />
|
||||||
|
</button>{' '}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="light close-button"
|
class="light close-button"
|
||||||
|
@ -243,10 +335,66 @@ export default ({ onClose, replyToStatus, editStatus }) => {
|
||||||
>
|
>
|
||||||
<Icon icon="x" />
|
<Icon icon="x" />
|
||||||
</button>
|
</button>
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="light"
|
||||||
|
onClick={() => {
|
||||||
|
// If there are non-ID media attachments (not yet uploaded), show confirmation dialog because they are not going to be passed to the new window
|
||||||
|
const containNonIDMediaAttachments =
|
||||||
|
mediaAttachments.length > 0 &&
|
||||||
|
mediaAttachments.some((media) => !media.id);
|
||||||
|
if (containNonIDMediaAttachments) {
|
||||||
|
const yes = confirm(
|
||||||
|
'You have media attachments that are not yet uploaded. Opening a new window will discard them and you will need to re-attach them. Are you sure you want to continue?',
|
||||||
|
);
|
||||||
|
if (!yes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!window.opener) {
|
||||||
|
alert('Looks like you closed the parent window.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mediaAttachmentsWithIDs = mediaAttachments.filter(
|
||||||
|
(media) => media.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
onClose(() => {
|
||||||
|
window.opener.__STATES__.showCompose = {
|
||||||
|
editStatus,
|
||||||
|
replyToStatus,
|
||||||
|
draftStatus: {
|
||||||
|
status: textareaRef.current.value,
|
||||||
|
spoilerText: spoilerTextRef.current.value,
|
||||||
|
visibility,
|
||||||
|
sensitive,
|
||||||
|
mediaAttachments: mediaAttachmentsWithIDs,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon="popin" alt="Pop in" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!!replyToStatus && (
|
{!!replyToStatus && (
|
||||||
<div class="reply-to">
|
<div class="status-preview">
|
||||||
<Status status={replyToStatus} size="s" />
|
<Status status={replyToStatus} size="s" />
|
||||||
|
<div class="status-preview-legend reply-to">
|
||||||
|
Replying to @
|
||||||
|
{replyToStatus.account.acct || replyToStatus.account.username}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!!editStatus && (
|
||||||
|
<div class="status-preview">
|
||||||
|
<Status status={editStatus} size="s" />
|
||||||
|
<div class="status-preview-legend">Editing source status</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<form
|
<form
|
||||||
|
@ -385,6 +533,7 @@ export default ({ onClose, replyToStatus, editStatus }) => {
|
||||||
<input
|
<input
|
||||||
name="sensitive"
|
name="sensitive"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
checked={sensitive}
|
||||||
disabled={uiState === 'loading' || !!editStatus}
|
disabled={uiState === 'loading' || !!editStatus}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const sensitive = e.target.checked;
|
const sensitive = e.target.checked;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'iconify-icon';
|
||||||
|
|
||||||
const SIZES = {
|
const SIZES = {
|
||||||
s: 12,
|
s: 12,
|
||||||
m: 16,
|
m: 16,
|
||||||
|
@ -35,11 +37,18 @@ const ICONS = {
|
||||||
upload: 'mingcute:upload-3-line',
|
upload: 'mingcute:upload-3-line',
|
||||||
gear: 'mingcute:settings-3-line',
|
gear: 'mingcute:settings-3-line',
|
||||||
more: 'mingcute:more-1-line',
|
more: 'mingcute:more-1-line',
|
||||||
|
external: 'mingcute:external-link-line',
|
||||||
|
popout: 'mingcute:external-link-line',
|
||||||
|
popin: ['mingcute:external-link-line', '180deg'],
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ({ icon, size = 'm', alt, title, class: className = '' }) => {
|
export default ({ icon, size = 'm', alt, title, class: className = '' }) => {
|
||||||
const iconSize = SIZES[size];
|
const iconSize = SIZES[size];
|
||||||
const iconName = ICONS[icon];
|
let iconName = ICONS[icon];
|
||||||
|
let rotate;
|
||||||
|
if (Array.isArray(iconName)) {
|
||||||
|
[iconName, rotate] = iconName;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class={`icon ${className}`}
|
class={`icon ${className}`}
|
||||||
|
@ -52,7 +61,12 @@ export default ({ icon, size = 'm', alt, title, class: className = '' }) => {
|
||||||
lineHeight: 0,
|
lineHeight: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<iconify-icon width={iconSize} height={iconSize} icon={iconName}>
|
<iconify-icon
|
||||||
|
width={iconSize}
|
||||||
|
height={iconSize}
|
||||||
|
icon={iconName}
|
||||||
|
rotate={rotate}
|
||||||
|
>
|
||||||
{alt}
|
{alt}
|
||||||
</iconify-icon>
|
</iconify-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -93,11 +93,13 @@
|
||||||
transform: translateX(5px);
|
transform: translateX(5px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status:not(.small) .container {
|
.status .container {
|
||||||
padding-left: 16px;
|
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
.status:not(.small) .container {
|
||||||
|
padding-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.status > .container > .meta {
|
.status > .container > .meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
55
src/compose.jsx
Normal file
55
src/compose.jsx
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
import './app.css';
|
||||||
|
|
||||||
|
import '@github/time-elements';
|
||||||
|
import { render } from 'preact';
|
||||||
|
import { useEffect, useState } from 'preact/hooks';
|
||||||
|
|
||||||
|
import Compose from './components/compose';
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [uiState, setUIState] = useState('default');
|
||||||
|
|
||||||
|
const { editStatus, replyToStatus, draftStatus } = window.__COMPOSE__ || {};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (uiState === 'closed') {
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
}, [uiState]);
|
||||||
|
|
||||||
|
if (uiState === 'closed') {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>You may close this page now.</p>
|
||||||
|
<p>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
window.close();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Close window
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Compose
|
||||||
|
editStatus={editStatus}
|
||||||
|
replyToStatus={replyToStatus}
|
||||||
|
draftStatus={draftStatus}
|
||||||
|
standalone
|
||||||
|
onClose={(fn = () => {}) => {
|
||||||
|
try {
|
||||||
|
fn();
|
||||||
|
setUIState('closed');
|
||||||
|
} catch (e) {}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(<App />, document.getElementById('app'));
|
|
@ -1,7 +1,6 @@
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
import '@github/time-elements';
|
import '@github/time-elements';
|
||||||
import 'iconify-icon';
|
|
||||||
import { render } from 'preact';
|
import { render } from 'preact';
|
||||||
|
|
||||||
import { App } from './app';
|
import { App } from './app';
|
||||||
|
|
|
@ -1,7 +1,16 @@
|
||||||
import preact from '@preact/preset-vite';
|
import preact from '@preact/preset-vite';
|
||||||
|
import { resolve } from 'path';
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [preact()],
|
plugins: [preact()],
|
||||||
|
build: {
|
||||||
|
rollupOptions: {
|
||||||
|
input: {
|
||||||
|
main: resolve(__dirname, 'index.html'),
|
||||||
|
compose: resolve(__dirname, 'compose/index.html'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue