[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.

177 lines
6.6 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
4 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
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, useEffect, useState } from 'react'
  2. import { useSelector, useDispatch } from 'react-redux'
  3. import { useParams, useHistory } from 'react-router-dom'
  4. import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
  5. import { faUserPlus, faUserMinus, faUserClock, faBan } from '@fortawesome/free-solid-svg-icons'
  6. import moment from 'moment'
  7. import { handleApiError } from '../../api/errors'
  8. import { fetchUser, subscribe, unsubscribe } from '../../actions/users'
  9. import { fetchUserPosts } from '../../actions/posts'
  10. import { setTheme } from '../../actions/theme'
  11. import { getEntity } from '../../selectors/entities'
  12. import { getAuthenticatedUser, getChecked } from '../../selectors/authentication'
  13. import { getUserPosts } from '../../selectors/posts'
  14. import { getThemeName } from '../../selectors/theme'
  15. import { useDeepCompareEffect, useTheme, useSetting } from '../../hooks'
  16. import { setTitle } from '../../utils'
  17. import { AppState, EntityType, User, Post, AppThunkDispatch, LevelItem } from '../../types'
  18. import Title from '../../components/title'
  19. import Subtitle from '../../components/subtitle'
  20. import Level from '../../components/level'
  21. import PostList from '../../components/post-list'
  22. import Section from '../../components/section'
  23. import HorizontalRule from '../../components/horizontal-rule'
  24. import Loading from '../../components/pages/loading'
  25. interface Params {
  26. id: string
  27. }
  28. const ViewUser: FC = () => {
  29. const { id } = useParams<Params>()
  30. const theme = useTheme()
  31. const themeName = useSelector(getThemeName)
  32. const [selectedThemeName] = useState(themeName)
  33. const checked = useSelector(getChecked)
  34. const self = useSelector(getAuthenticatedUser)
  35. const user = useSelector<AppState, User | undefined>(state => getEntity<User>(state, EntityType.User, id))
  36. const posts = useSelector<AppState, Post[]>(state => getUserPosts(state, id))
  37. const dispatch = useDispatch<AppThunkDispatch>()
  38. const history = useHistory()
  39. const allowThemeChange = useSetting<boolean>('allowThemeChange', true)
  40. useEffect(() => {
  41. const init = async () => {
  42. try {
  43. await dispatch(fetchUser(id))
  44. await dispatch(fetchUserPosts(id))
  45. } catch (err) {
  46. handleApiError(err, dispatch, history)
  47. }
  48. }
  49. if (checked) init()
  50. }, [checked])
  51. useDeepCompareEffect(() => {
  52. if (user) {
  53. setTitle(user.name)
  54. if (allowThemeChange && user.theme) dispatch(setTheme(user.theme))
  55. }
  56. return () => {
  57. if (allowThemeChange && selectedThemeName) dispatch(setTheme(selectedThemeName))
  58. }
  59. }, [user])
  60. if (!user) return <Loading />
  61. const isSelf = self && self.id === user.id
  62. const isGroup = self && self.group && user.group && self.group.id === user.group.id
  63. const subscription = self && user.subscriptions ? user.subscriptions.find(subscription => subscription.from === self.id && subscription.to === user.id) : undefined
  64. const subscribed = subscription && !subscription.pending
  65. const subscriptionPending = subscription && subscription.pending
  66. const items: LevelItem[] = []
  67. items.push({
  68. label: 'Posts',
  69. content: user.posts,
  70. })
  71. items.push({
  72. label: 'Awards',
  73. content: user.awards,
  74. })
  75. items.push({
  76. label: 'Points',
  77. content: user.points,
  78. })
  79. items.push({
  80. label: 'Joined',
  81. content: moment(user.created).format('MMMM Do, YYYY'),
  82. })
  83. return (
  84. <div>
  85. <Section>
  86. {user.coverImageUrl &&
  87. <div className="cover-image">
  88. <img src={user.coverImageUrl} />
  89. </div>
  90. }
  91. <div className="header">
  92. {user.imageUrl &&
  93. <div className="image">
  94. <img src={user.imageUrl} style={{ width: 128 }} />
  95. </div>
  96. }
  97. <div>
  98. <Title>{user.name}</Title>
  99. <p style={{ color: theme.text }}>{user.about}</p>
  100. </div>
  101. </div>
  102. <Level items={items} />
  103. <HorizontalRule />
  104. <div className="buttons">
  105. {subscribed &&
  106. <button style={{ backgroundColor: theme.red, color: 'white' }} onClick={() => dispatch(unsubscribe(user.id))}>
  107. <span className="icon">
  108. <FontAwesomeIcon icon={faUserMinus} />
  109. </span>
  110. <span>Unsusbcribe</span>
  111. </button>
  112. }
  113. {subscriptionPending &&
  114. <button style={{ backgroundColor: theme.blue, color: 'white' }}>
  115. <span className="icon">
  116. <FontAwesomeIcon icon={faUserClock} />
  117. </span>
  118. <span>Pending</span>
  119. </button>
  120. }
  121. {self && !isSelf && !subscribed && !subscriptionPending &&
  122. <button style={{ backgroundColor: theme.green, color: 'white' }} onClick={() => dispatch(subscribe(user.id))}>
  123. <span className="icon">
  124. <FontAwesomeIcon icon={faUserPlus} />
  125. </span>
  126. <span>Subscribe</span>
  127. </button>
  128. }
  129. {!isSelf &&
  130. <button style={{ backgroundColor: theme.red, color: 'white' }}>
  131. <span className="icon">
  132. <FontAwesomeIcon icon={faBan} />
  133. </span>
  134. <span>Block</span>
  135. </button>
  136. }
  137. {user.group && !isGroup &&
  138. <button style={{ backgroundColor: theme.red, color: 'white' }}>
  139. <span className="icon">
  140. <FontAwesomeIcon icon={faBan} />
  141. </span>
  142. <span>Block Community: {user.group.name}</span>
  143. </button>
  144. }
  145. </div>
  146. </Section>
  147. <div style={{ padding: '0px 1rem' }}>
  148. <Subtitle>Posts</Subtitle>
  149. </div>
  150. <PostList posts={posts} />
  151. </div>
  152. )
  153. }
  154. export default ViewUser