Replace custom gist implementation with github rendering (#1436)

* Replace custom gist implementation with github rendering

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2021-08-31 22:37:33 +02:00 committed by GitHub
parent 64443a3b64
commit dee494386a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 143 additions and 60 deletions

View file

@ -91,6 +91,7 @@ SPDX-License-Identifier: CC-BY-SA-4.0
- Change editor font to "Fira Code"
- Note tags can be set as yaml-array in frontmatter.
- If only one external login provider is configured, the sign-in button will directly link to it.
- Links in Gist-Frames work only if explicitly opened in new tabs.
---

View file

@ -9,3 +9,18 @@
filter: blur(3px);
}
}
.gist-resizer-row {
display: flex;
justify-content: center;
.gist-resizer {
display: flex;
width: 48px;
height: 5px;
background: white;
border-radius: 90px;
cursor: row-resize;
box-shadow: black 0 0 3px;
}
}

View file

@ -4,77 +4,43 @@
SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import React, { useCallback } from 'react'
import './gist-frame.scss'
import { useResizeGistFrame } from './use-resize-gist-frame'
export interface GistFrameProps {
id: string
}
interface resizeEvent {
size: number
id: string
}
/**
* This component renders a GitHub Gist by placing the gist URL in an {@link HTMLIFrameElement iframe}.
*
* @param id The id of the gist
*/
export const GistFrame: React.FC<GistFrameProps> = ({ id }) => {
const iframeHtml = useMemo(() => {
return `
<html lang="en">
<head>
<base target="_parent">
<title>gist</title>
<style>
* { font-size:12px; }
body{ overflow:hidden; margin: 0;}
</style>
<script type="text/javascript">
function doLoad() {
window.parent.postMessage({eventType: 'gistResize', size: document.body.scrollHeight, id: '${id}'}, '*')
tweakLinks();
}
function tweakLinks() {
document.querySelectorAll(".gist-meta > a").forEach((link) => {
link.rel="noopener noreferer"
link.target="_blank"
})
}
</script>
</head>
<body onload="doLoad()">
<script type="text/javascript" src="https://gist.github.com/${id}.js"></script>
</body>
</html>`
}, [id])
const [frameHeight, onStartResizing] = useResizeGistFrame(150)
const [frameHeight, setFrameHeight] = useState(0)
const sizeMessage = useCallback(
(message: MessageEvent) => {
const data = message.data as resizeEvent
if (data.id !== id) {
return
}
setFrameHeight(data.size)
const onStart = useCallback(
(event) => {
onStartResizing(event)
},
[id]
[onStartResizing]
)
useEffect(() => {
window.addEventListener('message', sizeMessage)
return () => {
window.removeEventListener('message', sizeMessage)
}
}, [sizeMessage])
return (
<iframe
sandbox='allow-scripts allow-top-navigation-by-user-activation allow-popups'
data-cy={'gh-gist'}
width='100%'
height={`${frameHeight}px`}
frameBorder='0'
title={`gist ${id}`}
src={`data:text/html;base64,${btoa(iframeHtml)}`}
/>
<span>
<iframe
sandbox=''
data-cy={'gh-gist'}
width='100%'
height={`${frameHeight}px`}
frameBorder='0'
title={`gist ${id}`}
src={`https://gist.github.com/${id}.pibb`}
/>
<span className={'gist-resizer-row'}>
<span className={'gist-resizer'} onMouseDown={onStart} onTouchStart={onStart} />
</span>
</span>
)
}

View file

@ -0,0 +1,101 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useCallback, useEffect, useRef, useState } from 'react'
/**
* Determines if the left mouse button is pressed in the given event
*
* @param mouseEvent the mouse event that should be checked
* @return {@code true} if the left mouse button is pressed. {@code false} otherwise.
*/
const isLeftMouseButtonPressed = (mouseEvent: MouseEvent): boolean => {
return mouseEvent.buttons === 1
}
/**
* Extracts the absolute vertical position of the mouse or touch point from the event.
*
* @param moveEvent the vertical position of the mouse pointer or the first touch pointer.
* @return the extracted vertical position.
*/
const extractVerticalPointerPosition = (moveEvent: MouseEvent | TouchEvent): number => {
if (isMouseEvent(moveEvent)) {
return moveEvent.pageY
} else {
return moveEvent.touches[0]?.pageY
}
}
/**
* Checks if the given {@link Event} is a {@link MouseEvent}
* @param event the event to check
* @return {@code true} if the given event is a {@link MouseEvent}
*/
const isMouseEvent = (event: Event): event is MouseEvent => {
return (event as MouseEvent).buttons !== undefined
}
export type PointerEvent = MouseEvent | TouchEvent
export type PointerEventHandler = (event: PointerEvent) => void
/**
* Provides logic for resizing a {@link GistFrame gist frame} by dragging an element.
*
* @param initialFrameHeight The initial size for the frame
* @return An array containing the current frame height and function to start the resizing
*/
export const useResizeGistFrame = (initialFrameHeight: number): [number, PointerEventHandler] => {
const [frameHeight, setFrameHeight] = useState(initialFrameHeight)
const lastYPosition = useRef<number | undefined>(undefined)
const onMove = useCallback((moveEvent: MouseEvent | TouchEvent) => {
if (lastYPosition.current === undefined) {
return
}
if (isMouseEvent(moveEvent) && !isLeftMouseButtonPressed(moveEvent)) {
lastYPosition.current = undefined
moveEvent.preventDefault()
return undefined
}
const currentPointerPosition = extractVerticalPointerPosition(moveEvent)
const deltaPointerPosition = currentPointerPosition - lastYPosition.current
lastYPosition.current = currentPointerPosition
setFrameHeight((oldFrameHeight) => Math.max(0, oldFrameHeight + deltaPointerPosition))
moveEvent.preventDefault()
}, [])
const onStartResizing: PointerEventHandler = useCallback((event) => {
lastYPosition.current = extractVerticalPointerPosition(event)
}, [])
const onStopResizing = useCallback(() => {
if (lastYPosition.current !== undefined) {
lastYPosition.current = undefined
}
}, [])
useEffect(() => {
const moveHandler = onMove
const stopResizeHandler = onStopResizing
window.addEventListener('touchmove', moveHandler)
window.addEventListener('mousemove', moveHandler)
window.addEventListener('touchcancel', stopResizeHandler)
window.addEventListener('touchend', stopResizeHandler)
window.addEventListener('mouseup', stopResizeHandler)
return () => {
window.removeEventListener('touchmove', moveHandler)
window.removeEventListener('mousemove', moveHandler)
window.removeEventListener('touchcancel', stopResizeHandler)
window.removeEventListener('touchend', stopResizeHandler)
window.removeEventListener('mouseup', stopResizeHandler)
}
}, [onMove, onStopResizing])
return [frameHeight, onStartResizing]
}