Skip to content

Instantly share code, notes, and snippets.

@vhoyer
Last active January 10, 2022 19:54
Show Gist options
  • Save vhoyer/8b277a3305452bd1d928bcfc86fa77fa to your computer and use it in GitHub Desktop.
Save vhoyer/8b277a3305452bd1d928bcfc86fa77fa to your computer and use it in GitHub Desktop.
// @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