[ABANDONED] React/Redux front end 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.

155 lines
5.2 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 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
5 years ago
5 years ago
5 years ago
  1. // view-app.tsx
  2. // Copyright (C) 2020 Dwayne Harris
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU General Public License as published by
  5. // the Free Software Foundation, either version 3 of the License, or
  6. // (at your option) any later version.
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU General Public License for more details.
  11. // You should have received a copy of the GNU General Public License
  12. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  13. import React, { FC, useEffect } from 'react'
  14. import { useSelector, useDispatch } from 'react-redux'
  15. import { Link, useParams, useHistory } from 'react-router-dom'
  16. import { faPlusSquare, faMinusSquare } from '@fortawesome/free-solid-svg-icons'
  17. import moment from 'moment'
  18. import { handleApiError } from '../../api/errors'
  19. import { fetchApp, installApp, uninstallApp } from '../../actions/apps'
  20. import { fetchInstallations } from '../../actions/composer'
  21. import { getAuthenticatedUserId } from '../../selectors/authentication'
  22. import { getInstallations } from '../../selectors/composer'
  23. import { getEntity } from '../../selectors/entities'
  24. import { getIsFetching } from '../../selectors/requests'
  25. import { useTheme } from '../../hooks'
  26. import { setTitle } from '../../utils'
  27. import { AppState, AppThunkDispatch, EntityType, App, RequestKey, LevelItem } from '../../types'
  28. import Title from '../../components/title'
  29. import Section from '../../components/section'
  30. import HorizontalRule from '../../components/horizontal-rule'
  31. import Button from '../../components/controls/button'
  32. import Level from '../../components/level'
  33. import Loading from '../../components/pages/loading'
  34. interface Params {
  35. id: string
  36. }
  37. const ViewApp: FC = () => {
  38. const { id } = useParams<Params>()
  39. const theme = useTheme()
  40. const app = useSelector<AppState, App | undefined>(state => getEntity<App>(state, EntityType.App, id))
  41. const installations = useSelector(getInstallations)
  42. const selfId = useSelector(getAuthenticatedUserId)
  43. const fetching = useSelector<AppState, boolean>(state => getIsFetching(state, RequestKey.InstallApp) || getIsFetching(state, RequestKey.UninstallApp))
  44. const dispatch = useDispatch<AppThunkDispatch>()
  45. const history = useHistory()
  46. useEffect(() => {
  47. try {
  48. dispatch(fetchApp(id))
  49. dispatch(fetchInstallations())
  50. } catch (err) {
  51. handleApiError(err, dispatch, history)
  52. }
  53. }, [])
  54. useEffect(() => {
  55. if (app) setTitle(app.name)
  56. }, [app])
  57. if (!app) return <Loading />
  58. const isCreator = app.user.id === selfId
  59. const installed = !!installations.find(i => i.app.id === app.id)
  60. const renderButton = () => {
  61. if (installed) {
  62. const handleClick = async () => {
  63. await dispatch(uninstallApp(id))
  64. await dispatch(fetchApp(id))
  65. }
  66. return (
  67. <Button text="Uninstall" icon={faMinusSquare} loading={fetching} onClick={() => handleClick()} color="#fff" backgroundColor={theme.red} />
  68. )
  69. } else {
  70. const handleClick = async () => {
  71. await dispatch(installApp(id))
  72. await dispatch(fetchApp(id))
  73. }
  74. return (
  75. <Button text="Install" icon={faPlusSquare} loading={fetching} onClick={() => handleClick()} color={theme.primaryAlternate} backgroundColor={theme.primary} />
  76. )
  77. }
  78. }
  79. const items: LevelItem[] = []
  80. items.push({
  81. label: 'Users',
  82. content: app.users,
  83. })
  84. items.push({
  85. label: 'Rating',
  86. content: app.rating.toString(),
  87. })
  88. if (app.companyName) {
  89. items.push({
  90. label: 'Company',
  91. content: app.companyName,
  92. })
  93. }
  94. items.push({
  95. label: 'Updated',
  96. content: moment(app.updated).format('MMMM Do, YYYY'),
  97. })
  98. return (
  99. <div>
  100. <Section>
  101. {app.coverImageUrl &&
  102. <div className="cover-image">
  103. <img src={app.coverImageUrl} />
  104. </div>
  105. }
  106. <div className="header">
  107. {app.imageUrl &&
  108. <div className="image">
  109. <img src={app.imageUrl} />
  110. </div>
  111. }
  112. <div>
  113. <Title>{app.name}</Title>
  114. <p style={{ color: theme.text }}>{app.about}</p>
  115. </div>
  116. </div>
  117. <Level items={items} />
  118. <div style={{ textAlign: 'center', padding: '1rem' }}>
  119. {renderButton()}
  120. </div>
  121. <HorizontalRule />
  122. <div className="buttons">
  123. {isCreator && <Link style={{ color: theme.secondary }} to={`/a/${id}/edit`}>View/Edit App</Link>}
  124. </div>
  125. </Section>
  126. </div>
  127. )
  128. }
  129. export default ViewApp