Last active
January 10, 2022 19:54
-
-
Save vhoyer/8b277a3305452bd1d928bcfc86fa77fa to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @flow | |
import React, { type Element } from 'react' | |
import Clipboard from 'clipboard' | |
import { useRouter } from 'next/router' | |
import Gitmoji from './Gitmoji' | |
import Toolbar from './Toolbar' | |
import useLocalStorage from './hooks/useLocalStorage' | |
import { notify } from '../../utils/notification' | |
type Gitmojis = { | |
code: string, | |
description: string, | |
emoji: string, | |
name: string, | |
} | |
type Props = { | |
gitmojis: Array<Gitmojis>, | |
} | |
const GitmojiList = (props: Props): Element<'div'> => { | |
const router = useRouter() | |
const [searchInput, setSearchInput] = React.useState('') | |
const [isListMode, setIsListMode] = useLocalStorage('isListMode', false) | |
const [pinneds, setPinneds] = useLocalStorage('pinneds', []) | |
const isPinned = (code: string): boolean => { | |
return pinneds.includes(code) | |
} | |
// here we change useMemo to useCallback to simplify stuff | |
// | |
// // here we then change the useCallback (which is used only to maintain the | |
// // reference for a function) to useMemo, because we are not going to have it | |
// // as a function, but a actual cached/memoized value (which is the sorted | |
// // gitmoji list). | |
const gitmojisSorted = React.useMemo( | |
() => { | |
// here we pass the responsibility of ensuring the original property does | |
// not get altered to the function itself, and not to whomever is calling | |
// it e.g. diff when calling: | |
// -sortPinnedGitmojis([...props.gitmojis]) | |
// +sortPinnedGitmojis(props.gitmojis) | |
return [...props.gitmojis].sort((gitmoji) => { | |
if (isPinned(gitmoji.code)) return -1 | |
return 0 | |
}) | |
}, | |
[pinneds, props.gitmojis] | |
) | |
// here we only swap the order of the `if..then/else` because the early | |
// function exit is a special case, and the normal path for it is to filter | |
// the list based on the user input and it's better for readability if the | |
// most expected path of execution is at the bottom of the function with any | |
// possible safe catch before anything run. | |
// // here we change the function declaration and invocation on separated lines | |
// // to an ternary as it was originally, because there is not really a point in | |
// // it being a separated function | |
const gitmojis = !searchInput | |
? gitmojisSorted | |
: gitmojisSorted.filter(({ code, description }) => { | |
const lowerCasedSearch = searchInput.toLowerCase() | |
return ( | |
code.includes(lowerCasedSearch) || | |
description.toLowerCase().includes(lowerCasedSearch) | |
) | |
}) | |
const addPinned = (code: string): void => { | |
setPinneds((current) => [...current, code]) | |
notify( | |
`<p>Gitmoji <span class="gitmoji-code">${code}</span> pinned to the top 😜</p>` | |
) | |
} | |
const removePinned = (code: string): void => { | |
setPinneds((current) => current.filter((pinned) => pinned !== code)) | |
notify( | |
`<p>Gitmoji <span class="gitmoji-code">${code}</span> is unpinned 😜</p>` | |
) | |
} | |
const onPinClick = (code: string): void => { | |
if (isPinned(code)) { | |
removePinned(code) | |
} else { | |
addPinned(code) | |
} | |
} | |
React.useEffect(() => { | |
if (router.query.search) { | |
setSearchInput(router.query.search) | |
} | |
}, [router.query.search]) | |
React.useEffect(() => { | |
if (router.query.search && !searchInput) { | |
router.push('/', undefined, { shallow: true }) | |
} | |
}, [searchInput]) | |
React.useEffect(() => { | |
const clipboard = new Clipboard( | |
'.gitmoji-clipboard-emoji, .gitmoji-clipboard-code' | |
) | |
clipboard.on('success', function (e) { | |
window.ga('send', 'event', 'Gitmoji', 'Copy to Clipboard') | |
const message = e.trigger.classList.contains('gitmoji-clipboard-emoji') | |
? `<p>Hey! Gitmoji ${e.text} copied to the clipboard 😜</p>` | |
: `<p>Hey! Gitmoji <span class="gitmoji-code">${e.text}</span> copied to the clipboard 😜</p>` | |
notify(message) | |
}) | |
return () => clipboard.destroy() | |
}, []) | |
return ( | |
<div className="row" id="gitmoji-list"> | |
<div className="col-xs-12"> | |
<Toolbar | |
isListMode={isListMode} | |
searchInput={searchInput} | |
setIsListMode={setIsListMode} | |
setSearchInput={setSearchInput} | |
/> | |
</div> | |
{gitmojis.length === 0 ? ( | |
<h2>No gitmojis found for search: {searchInput}</h2> | |
) : ( | |
gitmojis.map((gitmoji, index) => ( | |
<Gitmoji | |
code={gitmoji.code} | |
description={gitmoji.description} | |
emoji={gitmoji.emoji} | |
isListMode={isListMode} | |
key={index} | |
name={gitmoji.name} | |
pinned={isPinned(gitmoji.code)} | |
onPinClick={() => onPinClick(gitmoji.code)} | |
/> | |
)) | |
)} | |
</div> | |
) | |
} | |
export default GitmojiList |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment