[ABANDONDED] Set of "apps" for the Flexor social network.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

164 lines
4.8 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. import React, { FC, useState, useEffect } from 'react'
  2. import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
  3. import { faSearch } from '@fortawesome/free-solid-svg-icons'
  4. import { Communicator, Theme } from '../../../communicator'
  5. import Gif from './gif'
  6. import { GiphyGif } from '../../../types'
  7. const giphyIcon = require('./giphy.png')
  8. import '../../../styles/default.css'
  9. import '../../../styles/spinner.css'
  10. type APIResponse = GiphyGif[]
  11. interface Props {
  12. communicator: Communicator
  13. }
  14. const useDebounce = (value: string, delay = 500) => {
  15. const [valueD, setValueD] = useState(value)
  16. useEffect(() => {
  17. const handler = setTimeout(() => {
  18. setValueD(value)
  19. }, delay)
  20. return () => {
  21. clearTimeout(handler)
  22. }
  23. }, [value, delay])
  24. return valueD
  25. }
  26. const App: FC<Props> = ({ communicator }) => {
  27. const [posting, setPosting] = useState(false)
  28. const [searching, setSearching] = useState(false)
  29. const [search, setSearch] = useState('')
  30. const searchD = useDebounce(search)
  31. const [gifs, setGifs] = useState<GiphyGif[]>([])
  32. const [selected, setSelected] = useState('')
  33. const [theme, setTheme] = useState<Theme>({
  34. primary: '#333',
  35. primaryAlternate: '#fff',
  36. secondary: '#777',
  37. backgroundPrimary: '#fff',
  38. backgroundSecondary: '#ccc',
  39. text: '#555',
  40. red: '#ff1a1a',
  41. green: '#00802b',
  42. blue: '#005ce6',
  43. })
  44. useEffect(() => {
  45. const init = async () => {
  46. try {
  47. const content = await communicator.init()
  48. if (content!.theme) setTheme(content!.theme)
  49. if (content && content.parent && content.parent.data && content.parent.data.search) {
  50. setSearch(content.parent.data.search)
  51. }
  52. await communicator.setHeight(1000)
  53. } catch (err) {}
  54. }
  55. init()
  56. }, [])
  57. useEffect(() => {
  58. const doTrending = async () => {
  59. try {
  60. const response = await fetch('/api/gifs/home')
  61. setGifs(await response.json() as APIResponse)
  62. } catch (err) {}
  63. }
  64. const doSearch = async () => {
  65. try {
  66. setSearching(true)
  67. const response = await fetch(`/api/gifs/search?q=${search}`)
  68. setGifs(await response.json() as APIResponse)
  69. } catch (err) {}
  70. setSearching(false)
  71. }
  72. if (!search) {
  73. doTrending()
  74. } else if (search.length > 2) {
  75. doSearch()
  76. }
  77. }, [searchD])
  78. const handleSearchChange = (search: string) => {
  79. setSearch(search)
  80. }
  81. const post = async () => {
  82. try {
  83. const gif = gifs.find(g => g.id === selected)
  84. if (!gif) return
  85. setPosting(true)
  86. await communicator.post({
  87. attachments: [{
  88. url: gif.images.fixed_height.url,
  89. text: gif.title,
  90. }],
  91. data: {
  92. search,
  93. },
  94. visible: true,
  95. })
  96. setSearch('')
  97. setSelected('')
  98. } catch (err) {
  99. console.error(err)
  100. }
  101. setPosting(false)
  102. }
  103. return (
  104. <div style={{ backgroundColor: theme.backgroundPrimary }}>
  105. <div className="field">
  106. <div className="control-container">
  107. <div className="icon" style={{ backgroundColor: theme.primary, color: theme.primaryAlternate }}>
  108. <FontAwesomeIcon icon={faSearch} />
  109. </div>
  110. <div className="control">
  111. <input
  112. style={{ backgroundColor: theme.backgroundSecondary, borderColor: theme.secondary, color: theme.text }}
  113. type="text"
  114. placeholder="Search"
  115. value={search}
  116. onChange={(e) => handleSearchChange(e.target.value)} />
  117. </div>
  118. </div>
  119. </div>
  120. <div className="gifs">
  121. {gifs.map(gif => <Gif key={gif.id} gif={gif} selected={gif.id === selected} onSelect={setSelected} />)}
  122. </div>
  123. <nav className="level">
  124. <div>
  125. <img src={giphyIcon} style={{ width: 50, height: 18 }} />
  126. </div>
  127. <div>
  128. <button style={{ backgroundColor: theme.primary, color: theme.primaryAlternate }} onClick={() => post()} disabled={posting || !selected}>
  129. {posting ? <div className="spinner"></div> : <span>Post</span>}
  130. </button>
  131. </div>
  132. </nav>
  133. </div>
  134. )
  135. }
  136. export default App