forked from abner/for-legacy-web
Merge branch 'master' into cleanup
This commit is contained in:
50
src/components/common/CollapsibleSection.tsx
Normal file
50
src/components/common/CollapsibleSection.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import Details from "../ui/Details";
|
||||
import { State, store } from "../../redux";
|
||||
import { Action } from "../../redux/reducers";
|
||||
import { Children } from "../../types/Preact";
|
||||
import { ChevronDown } from "@styled-icons/boxicons-regular";
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
defaultValue: boolean;
|
||||
|
||||
sticky?: boolean;
|
||||
large?: boolean;
|
||||
|
||||
summary: Children;
|
||||
children: Children;
|
||||
}
|
||||
|
||||
export default function CollapsibleSection({ id, defaultValue, summary, children, ...detailsProps }: Props) {
|
||||
const state: State = store.getState();
|
||||
|
||||
function setState(state: boolean) {
|
||||
if (state === defaultValue) {
|
||||
store.dispatch({
|
||||
type: 'SECTION_TOGGLE_UNSET',
|
||||
id
|
||||
} as Action);
|
||||
} else {
|
||||
store.dispatch({
|
||||
type: 'SECTION_TOGGLE_SET',
|
||||
id,
|
||||
state
|
||||
} as Action);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Details
|
||||
open={state.sectionToggle[id] ?? defaultValue}
|
||||
onToggle={e => setState(e.currentTarget.open)}
|
||||
{...detailsProps}>
|
||||
<summary>
|
||||
<div class="padding">
|
||||
<ChevronDown size={20} />
|
||||
{ summary }
|
||||
</div>
|
||||
</summary>
|
||||
{ children }
|
||||
</Details>
|
||||
)
|
||||
}
|
||||
@@ -24,14 +24,16 @@ const PermissionTooltipBase = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
span {
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
color: var(--secondary-foreground);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: 'Fira Mono';
|
||||
font-family: var(--monoscape-font);
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
border-radius: 6px;
|
||||
margin: .125rem 0 .125rem;
|
||||
|
||||
height: auto;
|
||||
|
||||
max-height: 640px;
|
||||
max-width: min(480px, 100%);
|
||||
|
||||
object-fit: contain;
|
||||
|
||||
&[data-spoiler="true"] {
|
||||
filter: blur(30px);
|
||||
pointer-events: none;
|
||||
@@ -71,7 +78,7 @@
|
||||
}
|
||||
|
||||
pre code {
|
||||
font-family: "Fira Mono", sans-serif;
|
||||
font-family: var(--monoscape-font), sans-serif;
|
||||
}
|
||||
|
||||
&[data-loading="true"] {
|
||||
|
||||
@@ -15,60 +15,33 @@ interface Props {
|
||||
}
|
||||
|
||||
const MAX_ATTACHMENT_WIDTH = 480;
|
||||
const MAX_ATTACHMENT_HEIGHT = 640;
|
||||
|
||||
export default function Attachment({ attachment, hasContent }: Props) {
|
||||
const client = useContext(AppContext);
|
||||
const { openScreen } = useIntermediate();
|
||||
const { filename, metadata } = attachment;
|
||||
const [ spoiler, setSpoiler ] = useState(filename.startsWith("SPOILER_"));
|
||||
const maxWidth = Math.min(useContext(MessageAreaWidthContext), MAX_ATTACHMENT_WIDTH);
|
||||
|
||||
const url = client.generateFileURL(attachment, { width: MAX_ATTACHMENT_WIDTH * 1.5 }, true);
|
||||
let width = 0,
|
||||
height = 0;
|
||||
|
||||
if (metadata.type === 'Image' || metadata.type === 'Video') {
|
||||
let limitingWidth = Math.min(
|
||||
maxWidth,
|
||||
metadata.width
|
||||
);
|
||||
|
||||
let limitingHeight = Math.min(
|
||||
MAX_ATTACHMENT_HEIGHT,
|
||||
metadata.height
|
||||
);
|
||||
|
||||
// Calculate smallest possible WxH.
|
||||
width = Math.min(
|
||||
limitingWidth,
|
||||
limitingHeight * (metadata.width / metadata.height)
|
||||
);
|
||||
|
||||
height = Math.min(
|
||||
limitingHeight,
|
||||
limitingWidth * (metadata.height / metadata.width)
|
||||
);
|
||||
}
|
||||
|
||||
switch (metadata.type) {
|
||||
case "Image": {
|
||||
return (
|
||||
<div
|
||||
style={{ width }}
|
||||
className={styles.container}
|
||||
onClick={() => spoiler && setSpoiler(false)}
|
||||
>
|
||||
{spoiler && (
|
||||
<div className={styles.overflow}>
|
||||
<div style={{ width, height }}>
|
||||
<span><Text id="app.main.channel.misc.spoiler_attachment" /></span>
|
||||
</div>
|
||||
<span><Text id="app.main.channel.misc.spoiler_attachment" /></span>
|
||||
</div>
|
||||
)}
|
||||
<img
|
||||
src={url}
|
||||
alt={filename}
|
||||
width={metadata.width}
|
||||
height={metadata.height}
|
||||
data-spoiler={spoiler}
|
||||
data-has-content={hasContent}
|
||||
className={classNames(styles.attachment, styles.image)}
|
||||
@@ -79,7 +52,6 @@ export default function Attachment({ attachment, hasContent }: Props) {
|
||||
ev.button === 1 &&
|
||||
window.open(url, "_blank")
|
||||
}
|
||||
style={{ width, height }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -102,13 +74,10 @@ export default function Attachment({ attachment, hasContent }: Props) {
|
||||
onClick={() => spoiler && setSpoiler(false)}>
|
||||
{spoiler && (
|
||||
<div className={styles.overflow}>
|
||||
<div style={{ width, height }}>
|
||||
<span><Text id="app.main.channel.misc.spoiler_attachment" /></span>
|
||||
</div>
|
||||
<span><Text id="app.main.channel.misc.spoiler_attachment" /></span>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
style={{ width }}
|
||||
data-spoiler={spoiler}
|
||||
data-has-content={hasContent}
|
||||
className={classNames(styles.attachment, styles.video)}
|
||||
@@ -117,7 +86,6 @@ export default function Attachment({ attachment, hasContent }: Props) {
|
||||
<video
|
||||
src={url}
|
||||
controls
|
||||
style={{ width, height }}
|
||||
onMouseDown={ev =>
|
||||
ev.button === 1 &&
|
||||
window.open(url, "_blank")
|
||||
|
||||
@@ -101,6 +101,7 @@ const PreviewBox = styled.div`
|
||||
|
||||
.icon {
|
||||
height: 100px;
|
||||
width: 100%;
|
||||
margin-bottom: 4px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user