Browse Source

New architecture; add frontend node server

master
Dwayne Harris 4 years ago
parent
commit
d354196ca8
  1. 9
      config/config.json
  2. 9
      config/dev.json
  3. 9
      config/prod.json
  4. 2715
      package-lock.json
  5. 40
      package.json
  6. 15
      postcss-normalize.d.ts
  7. 21
      postcss-preset-env.d.ts
  8. 14
      src/actions/config.ts
  9. 20
      src/app/actions/apps.ts
  10. 16
      src/app/actions/authentication.ts
  11. 10
      src/app/actions/composer.ts
  12. 0
      src/app/actions/entities.ts
  13. 0
      src/app/actions/forms.ts
  14. 18
      src/app/actions/groups.ts
  15. 2
      src/app/actions/lists.ts
  16. 0
      src/app/actions/notifications.ts
  17. 18
      src/app/actions/posts.ts
  18. 10
      src/app/actions/registration.ts
  19. 9
      src/app/actions/requests.ts
  20. 0
      src/app/actions/theme.ts
  21. 10
      src/app/actions/users.ts
  22. 6
      src/app/api/errors.ts
  23. 14
      src/app/api/fetch.ts
  24. 0
      src/app/api/index.ts
  25. 0
      src/app/app.tsx
  26. 7
      src/app/components/app-list-item.tsx
  27. 20
      src/app/components/app.tsx
  28. 21
      src/app/components/composer.tsx
  29. 0
      src/app/components/controls/button.tsx
  30. 8
      src/app/components/controls/checkbox-field.tsx
  31. 5
      src/app/components/controls/cover-image-field.tsx
  32. 2
      src/app/components/controls/field-label.tsx
  33. 32
      src/app/components/controls/file-field.tsx
  34. 5
      src/app/components/controls/icon-image-field.tsx
  35. 5
      src/app/components/controls/image-field.tsx
  36. 12
      src/app/components/controls/password-field.tsx
  37. 4
      src/app/components/controls/primary-button.tsx
  38. 4
      src/app/components/controls/secondary-button.tsx
  39. 12
      src/app/components/controls/select-field.tsx
  40. 4
      src/app/components/controls/static-field.tsx
  41. 14
      src/app/components/controls/text-field.tsx
  42. 12
      src/app/components/controls/textarea-field.tsx
  43. 17
      src/app/components/controls/theme-field.tsx
  44. 18
      src/app/components/create-group-form.tsx
  45. 20
      src/app/components/create-group-step.tsx
  46. 20
      src/app/components/create-user-form.tsx
  47. 18
      src/app/components/create-user-step.tsx
  48. 2
      src/app/components/footer.tsx
  49. 4
      src/app/components/form-notification.tsx
  50. 18
      src/app/components/group-invitations.tsx
  51. 7
      src/app/components/group-list-item.tsx
  52. 8
      src/app/components/group-logs.tsx
  53. 2
      src/app/components/help-text.tsx
  54. 2
      src/app/components/horizontal-rule.tsx
  55. 4
      src/app/components/level.tsx
  56. 2
      src/app/components/logo.tsx
  57. 4
      src/app/components/member-list-item.tsx
  58. 8
      src/app/components/member-list.tsx
  59. 8
      src/app/components/navigation-menu.tsx
  60. 4
      src/app/components/notification-container.tsx
  61. 4
      src/app/components/notification.tsx
  62. 10
      src/app/components/pages/about.tsx
  63. 28
      src/app/components/pages/admin-apps.tsx
  64. 26
      src/app/components/pages/admin-groups.tsx
  65. 16
      src/app/components/pages/admin.tsx
  66. 20
      src/app/components/pages/apps.tsx
  67. 38
      src/app/components/pages/create-app.tsx
  68. 18
      src/app/components/pages/developers.tsx
  69. 48
      src/app/components/pages/edit-app.tsx
  70. 50
      src/app/components/pages/group-admin.tsx
  71. 20
      src/app/components/pages/groups.tsx
  72. 20
      src/app/components/pages/home.tsx
  73. 6
      src/app/components/pages/loading.tsx
  74. 26
      src/app/components/pages/login.tsx
  75. 40
      src/app/components/pages/register-group.tsx
  76. 28
      src/app/components/pages/register.tsx
  77. 50
      src/app/components/pages/self.tsx
  78. 48
      src/app/components/pages/view-app.tsx
  79. 47
      src/app/components/pages/view-group.tsx
  80. 28
      src/app/components/pages/view-post.tsx
  81. 51
      src/app/components/pages/view-user.tsx
  82. 8
      src/app/components/post-list.tsx
  83. 10
      src/app/components/post.tsx
  84. 2
      src/app/components/progress.tsx
  85. 2
      src/app/components/search.tsx
  86. 2
      src/app/components/section.tsx
  87. 15
      src/app/components/self-info.tsx
  88. 2
      src/app/components/spinner.tsx
  89. 2
      src/app/components/subtitle.tsx
  90. 12
      src/app/components/timeline.tsx
  91. 2
      src/app/components/title.tsx
  92. 2
      src/app/components/user-apps.tsx
  93. 13
      src/app/components/user.tsx
  94. 17
      src/app/constants/index.ts
  95. 7
      src/app/hooks/index.ts
  96. 0
      src/app/images/caret-down.svg
  97. BIN
      src/app/images/favicon-16x16.png
  98. BIN
      src/app/images/favicon-32x32.png
  99. BIN
      src/app/images/favicon.ico
  100. 2
      src/app/index.ejs

9
config/config.json

@ -1,9 +0,0 @@
{
"apiUrl": "http://localhost:5000",
"blobUrl": "https://flexordev.blob.core.windows.net/media/",
"media": {
"defaultMaxSize": 5242880,
"coverMaxSize": 5242880,
"iconMaxSize": 1048576
}
}

9
config/dev.json

@ -1,9 +0,0 @@
{
"apiUrl": "http://api-dev.flexor.cc",
"blobUrl": "https://flexordev.blob.core.windows.net/media/",
"media": {
"defaultMaxSize": 5242880,
"coverMaxSize": 5242880,
"iconMaxSize": 1048576
}
}

9
config/prod.json

@ -1,9 +0,0 @@
{
"apiUrl": "https://api.flexor.cc",
"blobUrl": "https://flexordev.blob.core.windows.net/media/",
"media": {
"defaultMaxSize": 5242880,
"coverMaxSize": 5242880,
"iconMaxSize": 1048576
}
}

2715
package-lock.json
File diff suppressed because it is too large
View File

40
package.json

@ -7,22 +7,17 @@
"defaults"
],
"scripts": {
"start": "webpack-dev-server --config webpack.config.ts",
"build": "npm run build:dev",
"build:dev": "webpack --mode production --config webpack.config.ts",
"build:prod": "webpack --mode production --config webpack.config.ts",
"build:communicator": "webpack --config etc/communicator/webpack.config.ts",
"deploy:batch:dev": "az storage blob upload-batch --source ./dist/ -d '$web' --account-name flexordev",
"deploy:batch:prod": "az storage blob upload-batch --source ./dist/ -d '$web' --account-name flexor",
"deploy:config:dev": "az storage blob upload --file ./config/dev.json --name config.json --container-name '$web' --account-name flexordev",
"deploy:config:prod": "az storage blob upload --file ./config/prod.json --name config.json --container-name '$web' --account-name flexor",
"deploy:dev": "run-s deploy:batch:dev deploy:config:dev",
"deploy:prod": "run-s deploy:batch:prod deploy:config:prod"
"start": "node dist/server.js",
"prestart": "npm run build",
"build": "run-p build:app build:server build:communicator",
"build:app": "webpack --mode production --config src/app/webpack.config.ts",
"build:server": "tsc -p src/server/tsconfig.json",
"build:communicator": "webpack --mode production --config src/communicator/webpack.config.ts"
},
"devDependencies": {
"@types/copy-webpack-plugin": "^5.0.0",
"@types/html-webpack-plugin": "^3.2.1",
"@types/lodash": "^4.14.149",
"@types/mini-css-extract-plugin": "^0.8.0",
"@types/react": "^16.9.17",
"@types/react-dom": "^16.9.4",
"@types/react-redux": "^7.1.5",
@ -31,24 +26,21 @@
"@types/uuid": "^3.4.6",
"@types/webpack": "^4.41.0",
"@types/webpack-bundle-analyzer": "^2.13.3",
"@types/webpack-dev-server": "^3.9.0",
"@types/zxcvbn": "^4.4.0",
"copy-webpack-plugin": "^5.1.1",
"css-loader": "^3.4.0",
"file-loader": "^5.0.2",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.9.0",
"normalize.css": "^8.0.1",
"npm-run-all": "^4.1.5",
"postcss-loader": "^3.0.0",
"postcss-normalize": "^8.0.1",
"postcss-preset-env": "^6.7.0",
"style-loader": "^1.1.1",
"pino-pretty": "^3.5.0",
"style-loader": "^1.1.2",
"ts-loader": "^6.2.1",
"ts-node": "^8.5.4",
"typescript": "^3.7.4",
"webpack": "^4.41.4",
"webpack": "^4.41.5",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.10.1"
"webpack-cli": "^3.3.10"
},
"dependencies": {
"@azure/storage-blob": "^12.0.1",
@ -56,6 +48,10 @@
"@fortawesome/fontawesome-svg-core": "^1.2.26",
"@fortawesome/free-solid-svg-icons": "^5.12.0",
"@fortawesome/react-fontawesome": "^0.1.8",
"dotenv": "^8.2.0",
"fastify": "^2.11.0",
"fastify-http-proxy": "^2.3.0",
"fastify-static": "^2.5.1",
"history": "^4.10.1",
"lodash": "^4.17.15",
"moment": "^2.24.0",
@ -63,7 +59,7 @@
"react-dom": "^16.12.0",
"react-redux": "^7.1.3",
"react-router-dom": "^5.1.2",
"redux": "^4.0.4",
"redux": "^4.0.5",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0",
"reselect": "^4.0.0",

15
postcss-normalize.d.ts

@ -1,15 +0,0 @@
declare module 'postcss-normalize' {
import {
plugin, Plugin, ParserInput,
Result, LazyResult, Root, ProcessOptions
} from 'postcss'
interface PluginOptions {
allowDuplicates?: boolean
forceImport?: string | boolean
browsers?: string
}
const PostcssNormalize: Plugin<PluginOptions>
export default PostcssNormalize
}

21
postcss-preset-env.d.ts

@ -1,21 +0,0 @@
declare module 'postcss-preset-env' {
import {
plugin, Plugin, ParserInput,
Result, LazyResult, Root, ProcessOptions
} from 'postcss'
interface PluginOptions {
stage?: number
features?: any
browsers?: string
insertBefore?: any
insertAfter?: any
autoprefixer?: any
preserve?: boolean
importFrom?: string
exportTo?: string
}
const PostcssPresetEnv: Plugin<PluginOptions>
export default PostcssPresetEnv
}

14
src/actions/config.ts

@ -1,14 +0,0 @@
import { Action } from 'redux'
import { Config } from 'src/types'
export interface SetConfigAction extends Action {
type: 'CONFIG_SET'
payload: Config
}
export type ConfigActions = SetConfigAction
export const setConfig = (config: Config): SetConfigAction => ({
type: 'CONFIG_SET',
payload: config
})

20
src/actions/apps.ts → src/app/actions/apps.ts

@ -1,13 +1,13 @@
import { apiFetch } from 'src/api'
import { setEntities } from 'src/actions/entities'
import { startRequest, finishRequest } from 'src/actions/requests'
import { setFieldNotification } from 'src/actions/forms'
import { listSet, listAppend } from 'src/actions/lists'
import { objectToQuerystring } from 'src/utils'
import { normalize } from 'src/utils/normalization'
import { EntityListKey, Entity } from 'src/types'
import { AppThunkAction, RequestKey, EntityType, App, AvailabilityResponse, NotificationType }from 'src/types'
import { apiFetch } from '../api'
import { setEntities } from '../actions/entities'
import { startRequest, finishRequest } from '../actions/requests'
import { setFieldNotification } from '../actions/forms'
import { listSet, listAppend } from '../actions/lists'
import { objectToQuerystring } from '../utils'
import { normalize } from '../utils/normalization'
import { EntityListKey, Entity } from '../types'
import { AppThunkAction, RequestKey, EntityType, App, AvailabilityResponse, NotificationType }from '../types'
interface AppsResponse {
apps: App[]

16
src/actions/authentication.ts → src/app/actions/authentication.ts

@ -1,17 +1,17 @@
import { Action } from 'redux'
import { apiFetch } from 'src/api'
import { setEntities } from 'src/actions/entities'
import { startRequest, finishRequest } from 'src/actions/requests'
import { normalize } from 'src/utils/normalization'
import { apiFetch } from '../api'
import { setEntities } from '../actions/entities'
import { startRequest, finishRequest } from '../actions/requests'
import { normalize } from '../utils/normalization'
import {
LOCAL_STORAGE_ACCESS_TOKEN_KEY,
LOCAL_STORAGE_REFRESH_TOKEN_KEY,
LOCAL_STORAGE_ACCESS_TOKEN_AT_KEY,
} from 'src/constants'
LOCAL_STORAGE_ACCESS_TOKEN_EXPIRES_AT_KEY,
} from '../constants'
import { AppThunkAction, Entity, RequestKey, EntityType, Settings } from 'src/types'
import { AppThunkAction, Entity, RequestKey, EntityType, Settings } from '../types'
export interface SetCheckedAction extends Action {
type: 'AUTHENTICATION_SET_CHECKED'
@ -95,7 +95,7 @@ export const authenticate = (name: string, password: string): AppThunkAction<str
localStorage.setItem(LOCAL_STORAGE_ACCESS_TOKEN_KEY, response.access)
localStorage.setItem(LOCAL_STORAGE_REFRESH_TOKEN_KEY, response.refresh)
if (response.expires) localStorage.setItem(LOCAL_STORAGE_ACCESS_TOKEN_AT_KEY, response.expires.toString())
if (response.expires) localStorage.setItem(LOCAL_STORAGE_ACCESS_TOKEN_EXPIRES_AT_KEY, response.expires.toString())
dispatch(finishRequest(RequestKey.Authenticate, true))
await dispatch(fetchSelf())

10
src/actions/composer.ts → src/app/actions/composer.ts

@ -1,10 +1,10 @@
import { Action } from 'redux'
import { setEntities } from 'src/actions/entities'
import { startRequest, finishRequest } from 'src/actions/requests'
import { apiFetch } from 'src/api'
import { normalize } from 'src/utils/normalization'
import { AppThunkAction, Installation, RequestKey, EntityType, Settings } from 'src/types'
import { setEntities } from '../actions/entities'
import { startRequest, finishRequest } from '../actions/requests'
import { apiFetch } from '../api'
import { normalize } from '../utils/normalization'
import { AppThunkAction, Installation, RequestKey, EntityType, Settings } from '../types'
export interface SetInstallationsAction extends Action {
type: 'COMPOSER_SET_INSTALLATIONS'

0
src/actions/entities.ts → src/app/actions/entities.ts

0
src/actions/forms.ts → src/app/actions/forms.ts

18
src/actions/groups.ts → src/app/actions/groups.ts

@ -1,13 +1,11 @@
import { Action } from 'redux'
import { apiFetch } from 'src/api'
import { setEntities } from 'src/actions/entities'
import { listSet, listAppend } from 'src/actions/lists'
import { startRequest, finishRequest } from 'src/actions/requests'
import { objectToQuerystring } from 'src/utils'
import { normalize } from 'src/utils/normalization'
import { AppThunkAction, Entity, RequestKey, EntityType, User, EntityListKey } from 'src/types'
import { apiFetch } from '../api'
import { setEntities } from '../actions/entities'
import { listSet, listAppend } from '../actions/lists'
import { startRequest, finishRequest } from '../actions/requests'
import { objectToQuerystring } from '../utils'
import { normalize } from '../utils/normalization'
import { AppThunkAction, Entity, RequestKey, EntityType, User, EntityListKey } from '../types'
export const fetchGroup = (id: string): AppThunkAction => {
return async dispatch => {

2
src/actions/lists.ts → src/app/actions/lists.ts

@ -1,5 +1,5 @@
import { Action } from 'redux'
import { EntityListKey } from 'src/types'
import { EntityListKey } from '../types'
export interface ListAppendAction extends Action {
type: 'LISTS_APPEND'

0
src/actions/notifications.ts → src/app/actions/notifications.ts

18
src/actions/posts.ts → src/app/actions/posts.ts

@ -1,13 +1,11 @@
import { Action } from 'redux'
import { apiFetch } from 'src/api'
import { setEntities } from 'src/actions/entities'
import { listSet, listAppend } from 'src/actions/lists'
import { startRequest, finishRequest } from 'src/actions/requests'
import { objectToQuerystring } from 'src/utils'
import { normalize } from 'src/utils/normalization'
import { AppThunkAction, Entity, RequestKey, EntityType, User, Post, Attachment, EntityListKey } from 'src/types'
import { apiFetch } from '../api'
import { setEntities } from '../actions/entities'
import { listSet, listAppend } from '../actions/lists'
import { startRequest, finishRequest } from '../actions/requests'
import { objectToQuerystring } from '../utils'
import { normalize } from '../utils/normalization'
import { AppThunkAction, Entity, RequestKey, EntityType, User, Post, Attachment, EntityListKey } from '../types'
interface CreatePostResponse {
id: string

10
src/actions/registration.ts → src/app/actions/registration.ts

@ -1,15 +1,15 @@
import { Action } from 'redux'
import { apiFetch } from 'src/api'
import { setFieldNotification } from 'src/actions/forms'
import { startRequest, finishRequest } from 'src/actions/requests'
import { apiFetch } from '../api'
import { setFieldNotification } from '../actions/forms'
import { startRequest, finishRequest } from '../actions/requests'
import {
LOCAL_STORAGE_ACCESS_TOKEN_KEY,
LOCAL_STORAGE_REFRESH_TOKEN_KEY,
} from 'src/constants'
} from '../constants'
import { AppThunkAction, NotificationType, RequestKey, AvailabilityResponse } from 'src/types'
import { AppThunkAction, NotificationType, RequestKey, AvailabilityResponse } from '../types'
export interface SetStepAction extends Action {
type: 'REGISTRATION_SET_STEP'

9
src/actions/requests.ts → src/app/actions/requests.ts

@ -1,30 +1,31 @@
import { Action } from 'redux'
import { RequestKey } from '../types'
export interface StartRequestAction extends Action {
type: 'REQUESTS_START_REQUEST'
payload: {
id: string
id: RequestKey
}
}
export interface FinishRequestAction extends Action {
type: 'REQUESTS_FINISH_REQUEST'
payload: {
id: string
id: RequestKey
succeeded: boolean
}
}
export type RequestsActions = StartRequestAction | FinishRequestAction
export const startRequest = (id: string): StartRequestAction => ({
export const startRequest = (id: RequestKey): StartRequestAction => ({
type: 'REQUESTS_START_REQUEST',
payload: {
id,
},
})
export const finishRequest = (id: string, succeeded: boolean): FinishRequestAction => ({
export const finishRequest = (id: RequestKey, succeeded: boolean): FinishRequestAction => ({
type: 'REQUESTS_FINISH_REQUEST',
payload: {
id,

0
src/actions/theme.ts → src/app/actions/theme.ts

10
src/actions/users.ts → src/app/actions/users.ts

@ -1,8 +1,8 @@
import { apiFetch } from 'src/api'
import { setEntities } from 'src/actions/entities'
import { startRequest, finishRequest } from 'src/actions/requests'
import { AppThunkAction, Entity, RequestKey, EntityType } from 'src/types'
import { normalize } from 'src/utils/normalization'
import { apiFetch } from '../api'
import { setEntities } from '../actions/entities'
import { startRequest, finishRequest } from '../actions/requests'
import { AppThunkAction, Entity, RequestKey, EntityType } from '../types'
import { normalize } from '../utils/normalization'
export const fetchUser = (id: string): AppThunkAction => {
return async dispatch => {

6
src/api/errors.ts → src/app/api/errors.ts

@ -1,8 +1,8 @@
import { History } from 'history'
import { setFieldNotification } from 'src/actions/forms'
import { showNotification } from 'src/actions/notifications'
import { AppThunkDispatch, FormNotification, NotificationType } from 'src/types'
import { setFieldNotification } from '../actions/forms'
import { showNotification } from '../actions/notifications'
import { AppThunkDispatch, FormNotification, NotificationType } from '../types'
export function handleApiError(err: HttpError, dispatch: AppThunkDispatch, history?: History) {
console.error('Error:', err)

14
src/api/fetch.ts → src/app/api/fetch.ts

@ -5,14 +5,15 @@ import {
ServerError,
} from './errors'
import { pathJoin } from '../utils'
import {
LOCAL_STORAGE_ACCESS_TOKEN_KEY,
LOCAL_STORAGE_ACCESS_TOKEN_AT_KEY,
LOCAL_STORAGE_ACCESS_TOKEN_EXPIRES_AT_KEY,
LOCAL_STORAGE_REFRESH_TOKEN_KEY,
} from '../constants'
import { FetchOptions, FormNotification, NotificationType } from '../types'
import getConfig from '../config'
interface RefreshResponse {
id: string
@ -71,7 +72,6 @@ const getResponseData = async (response: Response) => {
export const apiFetch: APIFetch = async (options: FetchOptions) => {
const { path, method = 'get', body } = options
const contentType = 'application/json'
const config = await getConfig()
const doFetch = async () => {
const headers = new Headers({
@ -84,7 +84,7 @@ export const apiFetch: APIFetch = async (options: FetchOptions) => {
const accessToken = localStorage.getItem(LOCAL_STORAGE_ACCESS_TOKEN_KEY)
if (accessToken) headers.append('Authorization', `Bearer ${accessToken}`)
return await fetch(`${config.apiUrl}${path}`, {
return await fetch(`/${pathJoin('api', path)}`, {
headers,
method,
body: body ? JSON.stringify(body) : undefined,
@ -96,7 +96,7 @@ export const apiFetch: APIFetch = async (options: FetchOptions) => {
const refreshToken = localStorage.getItem(LOCAL_STORAGE_REFRESH_TOKEN_KEY)
if (accessToken && refreshToken) {
const refreshResponse = await fetch(`${config.apiUrl}/v1/refresh`, {
const refreshResponse = await fetch('/api/v1/refresh', {
headers: new Headers({
'Content-Type': contentType,
'Authorization': `Bearer ${accessToken}`
@ -114,7 +114,7 @@ export const apiFetch: APIFetch = async (options: FetchOptions) => {
const data = await getResponseData(refreshResponse) as RefreshResponse
localStorage.setItem(LOCAL_STORAGE_ACCESS_TOKEN_KEY, data.access)
localStorage.setItem(LOCAL_STORAGE_ACCESS_TOKEN_AT_KEY, data.expires.toString())
localStorage.setItem(LOCAL_STORAGE_ACCESS_TOKEN_EXPIRES_AT_KEY, data.expires.toString())
localStorage.setItem(LOCAL_STORAGE_REFRESH_TOKEN_KEY, data.refresh)
const secondResponse = await doFetch()
@ -126,7 +126,7 @@ export const apiFetch: APIFetch = async (options: FetchOptions) => {
}
}
const accessTokenExpiresAt = localStorage.getItem(LOCAL_STORAGE_ACCESS_TOKEN_AT_KEY)
const accessTokenExpiresAt = localStorage.getItem(LOCAL_STORAGE_ACCESS_TOKEN_EXPIRES_AT_KEY)
if (accessTokenExpiresAt && Date.now() >= parseInt(accessTokenExpiresAt, 10)) {
return await doRefresh()
}

0
src/api/index.ts → src/app/api/index.ts

0
src/app.tsx → src/app/app.tsx

7
src/components/app-list-item.tsx → src/app/components/app-list-item.tsx

@ -1,7 +1,7 @@
import React, { FC } from 'react'
import { Link } from 'react-router-dom'
import { useConfig, useTheme } from 'src/hooks'
import { App } from 'src/types'
import { useTheme } from '../hooks'
import { App } from '../types'
interface Props {
app: App
@ -9,13 +9,12 @@ interface Props {
const AppListItem: FC<Props> = ({ app }) => {
const theme = useTheme()
const config = useConfig()
return (
<div className="app-list-item" style={{ backgroundColor: theme.backgroundPrimary, borderColor: theme.backgroundSecondary }}>
{app.imageUrl &&
<div className="image">
<img src={`${config.blobUrl}${app.imageUrl}`} />
<img src={app.imageUrl} />
</div>
}
<div>

20
src/components/app.tsx → src/app/components/app.tsx

@ -2,17 +2,15 @@ import React, { FC, useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import { handleApiError } from 'src/api/errors'
import { fetchSelf, setChecked } from 'src/actions/authentication'
import { setConfig } from 'src/actions/config'
import { setTheme } from 'src/actions/theme'
import { getAuthenticatedUser } from 'src/selectors/authentication'
import { getFetching } from 'src/selectors'
import { handleApiError } from '../api/errors'
import { fetchSelf, setChecked } from '../actions/authentication'
import { setTheme } from '../actions/theme'
import { getAuthenticatedUser } from '../selectors/authentication'
import { getFetching } from '../selectors'
import getConfig from 'src/config'
import { LOCAL_STORAGE_ACCESS_TOKEN_KEY } from 'src/constants'
import { useDeepCompareEffect, useTheme } from 'src/hooks'
import { AppThunkDispatch } from 'src/types'
import { LOCAL_STORAGE_ACCESS_TOKEN_KEY } from '../constants'
import { useDeepCompareEffect, useTheme } from '../hooks'
import { AppThunkDispatch } from '../types'
import Footer from './footer'
import Logo from './logo'
@ -61,8 +59,6 @@ const App: FC = () => {
} else {
dispatch(setChecked())
}
dispatch(setConfig(await getConfig()))
}
useEffect(() => {

21
src/components/composer.tsx → src/app/components/composer.tsx

@ -1,21 +1,21 @@
import React, { FC, useState, useEffect, useRef } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { getOrigin } from 'src/utils'
import { useConfig, useDeepCompareEffect, useTheme } from 'src/hooks'
import { getOrigin } from '../utils'
import { useDeepCompareEffect, useTheme } from '../hooks'
import {
fetchInstallations,
setSelectedInstallation,
setHeight as setComposerHeight,
setError as setComposerError,
saveInstallationSettings,
} from 'src/actions/composer'
import { showNotification } from 'src/actions/notifications'
import { createPost } from 'src/actions/posts'
import { getInstallations, getSelectedInstallation, getError, getHeight as getComposerHeight } from 'src/selectors/composer'
import { getColorScheme } from 'src/selectors/theme'
import { AppThunkDispatch, NotificationType, Post } from 'src/types'
import { IncomingMessageData, OutgoingMessageData } from 'src/types/communicator'
} from '../actions/composer'
import { showNotification } from '../actions/notifications'
import { createPost } from '../actions/posts'
import { getInstallations, getSelectedInstallation, getError, getHeight as getComposerHeight } from '../selectors/composer'
import { getColorScheme } from '../selectors/theme'
import { AppThunkDispatch, NotificationType, Post } from '../types'
import { IncomingMessageData, OutgoingMessageData } from '../types/communicator'
interface LimiterCollection {
[key: string]: number
@ -33,7 +33,6 @@ const Composer: FC<Props> = ({ parent, onPost }) => {
const installation = useSelector(getSelectedInstallation)
const height = useSelector(getComposerHeight)
const error = useSelector(getError)
const config = useConfig()
const dispatch = useDispatch<AppThunkDispatch>()
const ref = useRef<HTMLIFrameElement>(null)
const [limiters, setLimiters] = useState<LimiterCollection>({})
@ -214,7 +213,7 @@ const Composer: FC<Props> = ({ parent, onPost }) => {
<div className="installations" style={{ backgroundColor: theme.backgroundPrimary }}>
{installations.map(i => (
<div key={i.id} className={installation && installation.id === i.id ? 'selected' : ''} style={{ borderColor: theme.backgroundSecondary }} onClick={() => handleClick(i.id)}>
<img src={`${config.blobUrl}${i.app.iconImageUrl}`} alt={i.app.name} style={{ width: 32 }} />
<img src={i.app.iconImageUrl} alt={i.app.name} style={{ width: 32 }} />
<p style={{ color: theme.text }}>{i.app.name}</p>
</div>
))}

0
src/components/controls/button.tsx → src/app/components/controls/button.tsx

8
src/components/controls/checkbox-field.tsx → src/app/components/controls/checkbox-field.tsx

@ -1,9 +1,9 @@
import React, { FC } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { useTheme } from 'src/hooks'
import { setFieldValue } from 'src/actions/forms'
import { getFieldValue } from 'src/selectors/forms'
import { AppState } from 'src/types'
import { useTheme } from '../../hooks'
import { setFieldValue } from '../../actions/forms'
import { getFieldValue } from '../../selectors/forms'
import { AppState } from '../../types'
interface Props {
name: string

5
src/components/controls/cover-image-field.tsx → src/app/components/controls/cover-image-field.tsx

@ -1,5 +1,5 @@
import React, { FC } from 'react'
import { useConfig } from 'src/hooks'
import { MEDIA_COVER_MAX_SIZE } from '../../constants'
import FileField from './file-field'
interface Props {
@ -9,8 +9,7 @@ interface Props {
}
const CoverImageField: FC<Props> = ({ name, label = 'Cover Image', help = 'Approx 400 x 200. Max 5 MBs.' }) => {
const config = useConfig()
return <FileField name={name} label={label} help={help} previewWidth={200} maxSize={config.media.coverMaxSize} />
return <FileField name={name} label={label} help={help} previewWidth={200} maxSize={MEDIA_COVER_MAX_SIZE} />
}
export default CoverImageField

2
src/components/controls/field-label.tsx → src/app/components/controls/field-label.tsx

@ -1,5 +1,5 @@
import React, { FC } from 'react'
import { useTheme } from 'src/hooks'
import { useTheme } from '../../hooks'
const FieldLabel: FC = ({ children }) => {
const theme = useTheme()

32
src/components/controls/file-field.tsx → src/app/components/controls/file-field.tsx

@ -4,15 +4,16 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faUpload } from '@fortawesome/free-solid-svg-icons'
import { BlockBlobClient, AnonymousCredential } from '@azure/storage-blob'
import { useConfig, useTheme } from 'src/hooks'
import { setFieldValue } from 'src/actions/forms'
import { showNotification } from 'src/actions/notifications'
import { getFieldValue } from 'src/selectors/forms'
import { apiFetch } from 'src/api/fetch'
import { AppState, AppThunkDispatch, SasResponse, NotificationType } from 'src/types'
import { useTheme } from '../../hooks'
import { setFieldValue } from '../../actions/forms'
import { showNotification } from '../../actions/notifications'
import { getFieldValue } from '../../selectors/forms'
import { apiFetch } from '../../api/fetch'
import { MEDIA_DEFAULT_MAX_SIZE } from '../../constants'
import { AppState, AppThunkDispatch, SasResponse, NotificationType } from '../../types'
import Progress from 'src/components/progress'
import FieldLabel from 'src/components/controls/field-label'
import Progress from '../../components/progress'
import FieldLabel from '../../components/controls/field-label'
interface Props {
name: string
@ -24,11 +25,10 @@ interface Props {
const FileField: FC<Props> = props => {
const theme = useTheme()
const value = useSelector<AppState, boolean>(state => getFieldValue<boolean>(state, props.name, false))
const config = useConfig()
const value = useSelector<AppState, string>(state => getFieldValue<string>(state, props.name, ''))
const dispatch = useDispatch<AppThunkDispatch>()
const { name, label, help, previewWidth = 128, maxSize = config.media.defaultMaxSize } = props
const { name, label, help, previewWidth = 128, maxSize = MEDIA_DEFAULT_MAX_SIZE } = props
const [progress, setProgress] = useState(0)
const [uploading, setUploading] = useState(false)
@ -45,13 +45,13 @@ const FileField: FC<Props> = props => {
}
const ext = file.name.substring(file.name.lastIndexOf('.'))
const { sas, id } = await apiFetch<SasResponse>({ path: '/v1/sas' })
const { sas, blobUrl, id } = await apiFetch<SasResponse>({ path: '/v1/sas' })
const filename = `${id}${ext}`
setUploading(true)
try {
const blockBlobClient = new BlockBlobClient(`${config.blobUrl}${filename}?${sas}`, new AnonymousCredential())
const blockBlobClient = new BlockBlobClient(`${blobUrl}/${filename}?${sas}`, new AnonymousCredential())
await blockBlobClient.uploadBrowserData(file, {
onProgress: p => {
setProgress((p.loadedBytes / file.size) * 100)
@ -69,7 +69,7 @@ const FileField: FC<Props> = props => {
}
})
dispatch(setFieldValue(name, filename))
dispatch(setFieldValue(name, `${blobUrl}/${filename}`))
setUploaded(true)
} catch (err) {
console.error(err)
@ -105,9 +105,9 @@ const FileField: FC<Props> = props => {
<FieldLabel>{label}</FieldLabel>
{value &&
<div style={{ padding: '1rem 0px' }}>
<img src={`${config.blobUrl}${value}`} style={{ width: previewWidth }} />
<img src={value} style={{ width: previewWidth }} />
<div style={{ color: theme.secondary, fontSize: '0.8rem' }}>
{value}
{value.split('/').pop()}
&nbsp;&nbsp;
(<a style={{ color: theme.red }} onClick={() => handleDelete()}>Delete</a>)
</div>

5
src/components/controls/icon-image-field.tsx → src/app/components/controls/icon-image-field.tsx

@ -1,5 +1,5 @@
import React, { FC } from 'react'
import { useConfig } from 'src/hooks'
import { MEDIA_ICON_MAX_SIZE } from '../../constants'
import FileField from './file-field'
interface Props {
@ -9,8 +9,7 @@ interface Props {
}
const IconImageField: FC<Props> = ({ name, label = 'Icon Image', help = 'Approx 32 x 32. Max 1 MB.' }) => {
const config = useConfig()
return <FileField name={name} label={label} help={help} previewWidth={32} maxSize={config.media.iconMaxSize} />
return <FileField name={name} label={label} help={help} previewWidth={32} maxSize={MEDIA_ICON_MAX_SIZE} />
}
export default IconImageField

5
src/components/controls/image-field.tsx → src/app/components/controls/image-field.tsx

@ -1,5 +1,5 @@
import React, { FC } from 'react'
import { useConfig } from 'src/hooks'
import { MEDIA_DEFAULT_MAX_SIZE } from '../../constants'
import FileField from './file-field'
interface Props {
@ -9,8 +9,7 @@ interface Props {
}
const ImageField: FC<Props> = ({ name, label = 'Image', help = 'Approx 128 x 128. Max 5 MBs.' }) => {
const config = useConfig()
return <FileField name={name} label={label} help={help} previewWidth={64} maxSize={config.media.defaultMaxSize} />
return <FileField name={name} label={label} help={help} previewWidth={64} maxSize={MEDIA_DEFAULT_MAX_SIZE} />
}
export default ImageField

12
src/components/controls/password-field.tsx → src/app/components/controls/password-field.tsx

@ -3,13 +3,13 @@ import { useSelector, useDispatch } from 'react-redux'
import zxcvbn from 'zxcvbn'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faKey } from '@fortawesome/free-solid-svg-icons'
import { useTheme } from 'src/hooks'
import { setFieldValue } from 'src/actions/forms'
import { getFieldValue, getFieldNotification } from 'src/selectors/forms'
import { AppState, FormNotification, NotificationType } from 'src/types'
import { useTheme } from '../../hooks'
import { setFieldValue } from '../../actions/forms'
import { getFieldValue, getFieldNotification } from '../../selectors/forms'
import { AppState, FormNotification, NotificationType } from '../../types'
import FieldLabel from 'src/components/controls/field-label'
import FormNotificationComponent from 'src/components/form-notification'
import FieldLabel from '../../components/controls/field-label'
import FormNotificationComponent from '../../components/form-notification'
export interface Props {
placeholder?: string

4
src/components/controls/primary-button.tsx → src/app/components/controls/primary-button.tsx

@ -1,9 +1,9 @@
import React, { FC, MouseEventHandler } from 'react'
import noop from 'lodash/noop'
import { IconDefinition } from '@fortawesome/fontawesome-common-types'
import { useTheme } from 'src/hooks'
import { useTheme } from '../../hooks'
import Button from 'src/components/controls/button'
import Button from '../../components/controls/button'
export interface Props {
text: string

4
src/components/controls/secondary-button.tsx → src/app/components/controls/secondary-button.tsx

@ -1,9 +1,9 @@
import React, { FC, MouseEventHandler } from 'react'
import noop from 'lodash/noop'
import { IconDefinition } from '@fortawesome/fontawesome-common-types'
import { useTheme } from 'src/hooks'
import { useTheme } from '../../hooks'
import Button from 'src/components/controls/button'
import Button from '../../components/controls/button'
export interface Props {
text: string

12
src/components/controls/select-field.tsx → src/app/components/controls/select-field.tsx

@ -2,13 +2,13 @@ import React, { FC } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { IconDefinition } from '@fortawesome/fontawesome-svg-core'
import { useTheme } from 'src/hooks'
import { setFieldValue } from 'src/actions/forms'
import { getFieldValue, getFieldNotification } from 'src/selectors/forms'
import { AppState, FormNotification } from 'src/types'
import { useTheme } from '../../hooks'
import { setFieldValue } from '../../actions/forms'
import { getFieldValue, getFieldNotification } from '../../selectors/forms'
import { AppState, FormNotification } from '../../types'
import FieldLabel from 'src/components/controls/field-label'
import FormNotificationComponent from 'src/components/form-notification'
import FieldLabel from '../../components/controls/field-label'
import FormNotificationComponent from '../../components/form-notification'
interface SelectOptions {
[value: string]: string

4
src/components/controls/static-field.tsx → src/app/components/controls/static-field.tsx

@ -1,9 +1,9 @@
import React, { FC } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { IconDefinition } from '@fortawesome/free-solid-svg-icons'
import { useTheme } from 'src/hooks'
import { useTheme } from '../../hooks'
import FieldLabel from 'src/components/controls/field-label'
import FieldLabel from '../../components/controls/field-label'
interface Props {
label: string

14
src/components/controls/text-field.tsx → src/app/components/controls/text-field.tsx

@ -3,14 +3,14 @@ import { useSelector, useDispatch } from 'react-redux'
import noop from 'lodash/noop'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { IconDefinition } from '@fortawesome/fontawesome-common-types'
import { useTheme } from 'src/hooks'
import { setFieldValue } from 'src/actions/forms'
import { getFieldValue, getFieldNotification } from 'src/selectors/forms'
import { AppState, FormNotification, NotificationType } from 'src/types'
import { useTheme } from '../../hooks'
import { setFieldValue } from '../../actions/forms'
import { getFieldValue, getFieldNotification } from '../../selectors/forms'
import { AppState, FormNotification, NotificationType } from '../../types'
import FieldLabel from 'src/components/controls/field-label'
import FormNotificationComponent from 'src/components/form-notification'
import HelpText from 'src/components/help-text'
import FieldLabel from '../../components/controls/field-label'
import FormNotificationComponent from '../../components/form-notification'
import HelpText from '../../components/help-text'
interface Props {
name: string

12
src/components/controls/textarea-field.tsx → src/app/components/controls/textarea-field.tsx

@ -1,13 +1,13 @@
import React, { FC, FocusEventHandler } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import noop from 'lodash/noop'
import { useTheme } from 'src/hooks'
import { setFieldValue } from 'src/actions/forms'
import { getFieldValue, getFieldNotification } from 'src/selectors/forms'
import { AppState, FormNotification, NotificationType } from 'src/types'
import { useTheme } from '../../hooks'
import { setFieldValue } from '../../actions/forms'
import { getFieldValue, getFieldNotification } from '../../selectors/forms'
import { AppState, FormNotification, NotificationType } from '../../types'
import FieldLabel from 'src/components/controls/field-label'
import FormNotificationComponent from 'src/components/form-notification'
import FieldLabel from '../../components/controls/field-label'
import FormNotificationComponent from '../../components/form-notification'
export interface Props {
name: string

17
src/components/controls/theme-field.tsx → src/app/components/controls/theme-field.tsx

@ -1,15 +1,14 @@
import React, { FC, useState, useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import capitalize from 'lodash/capitalize'
import { useTheme } from 'src/hooks'
import { setFieldValue } from 'src/actions/forms'
import { setTheme } from 'src/actions/theme'
import { getFieldValue } from 'src/selectors/forms'
import { getThemeName } from 'src/selectors/theme'
import { AppState, Theme, ColorScheme } from 'src/types'
import FieldLabel from 'src/components/controls/field-label'
import themes from 'src/themes'
import { useTheme } from '../../hooks'
import { setFieldValue } from '../../actions/forms'
import { setTheme } from '../../actions/theme'
import { getFieldValue } from '../../selectors/forms'
import { AppState, Theme, ColorScheme } from '../../types'
import FieldLabel from '../../components/controls/field-label'
import themes from '../../themes'
export interface Props {
name: string

18
src/components/create-group-form.tsx → src/app/components/create-group-form.tsx

@ -3,16 +3,16 @@ import { useDispatch } from 'react-redux'
import { Link } from 'react-router-dom'
import { faIdCard } from '@fortawesome/free-solid-svg-icons'
import { checkGroupAvailability } from 'src/actions/registration'
import { useTheme } from 'src/hooks'
import { checkGroupAvailability } from '../actions/registration'
import { useTheme } from '../hooks'
import CheckboxField from 'src/components/controls/checkbox-field'
import TextField from 'src/components/controls/text-field'
import SelectField from 'src/components/controls/select-field'
import ImageField from 'src/components/controls/image-field'
import CoverImageField from 'src/components/controls/cover-image-field'
import IconImageField from 'src/components/controls/icon-image-field'
import ThemeField from 'src/components/controls/theme-field'
import CheckboxField from '../components/controls/checkbox-field'
import TextField from '../components/controls/text-field'
import SelectField from '../components/controls/select-field'
import ImageField from '../components/controls/image-field'
import CoverImageField from '../components/controls/cover-image-field'
import IconImageField from '../components/controls/icon-image-field'
import ThemeField from '../components/controls/theme-field'
const CreateGroupForm: FC = () => {
const theme = useTheme()

20
src/components/create-group-step.tsx → src/app/components/create-group-step.tsx

@ -2,19 +2,19 @@ import React, { FC } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { faArrowLeft } from '@fortawesome/free-solid-svg-icons'
import { setFieldNotification } from 'src/actions/forms'
import { showNotification } from 'src/actions/notifications'
import { setStep } from 'src/actions/registration'
import { getForm } from 'src/selectors/forms'
import { setFieldNotification } from '../actions/forms'
import { showNotification } from '../actions/notifications'
import { setStep } from '../actions/registration'
import { getForm } from '../selectors/forms'
import { valueFromForm } from 'src/utils'
import { MAX_ID_LENGTH } from 'src/constants'
import { AppThunkDispatch, NotificationType } from 'src/types'
import { valueFromForm } from '../utils'
import { MAX_ID_LENGTH } from '../constants'
import { AppThunkDispatch, NotificationType } from '../types'
import CreateGroupForm from './create-group-form'
import HorizontalRule from 'src/components/horizontal-rule'
import PrimaryButton from 'src/components/controls/primary-button'
import SecondaryButton from 'src/components/controls/secondary-button'
import HorizontalRule from '../components/horizontal-rule'
import PrimaryButton from '../components/controls/primary-button'
import SecondaryButton from '../components/controls/secondary-button'
interface Props {
register: () => void

20
src/components/create-user-form.tsx → src/app/components/create-user-form.tsx

@ -2,17 +2,17 @@ import React, { FC } from 'react'
import { useDispatch } from 'react-redux'
import { Link } from 'react-router-dom'
import { faEnvelope, faIdCard, faUserShield } from '@fortawesome/free-solid-svg-icons'
import { checkUserAvailability } from 'src/actions/registration'
import { PRIVACY_OPTIONS } from 'src/constants'
import { useTheme } from 'src/hooks'
import { checkUserAvailability } from '../actions/registration'
import { PRIVACY_OPTIONS } from '../constants'
import { useTheme } from '../hooks'
import CheckboxField from 'src/components/controls/checkbox-field'
import TextField from 'src/components/controls/text-field'
import PasswordField from 'src/components/controls/password-field'
import SelectField from 'src/components/controls/select-field'
import ImageField from 'src/components/controls/image-field'
import CoverImageField from 'src/components/controls/cover-image-field'
import ThemeField from 'src/components/controls/theme-field'
import CheckboxField from '../components/controls/checkbox-field'
import TextField from '../components/controls/text-field'
import PasswordField from '../components/controls/password-field'
import SelectField from '../components/controls/select-field'
import ImageField from '../components/controls/image-field'
import CoverImageField from '../components/controls/cover-image-field'
import ThemeField from '../components/controls/theme-field'
const CreateUserForm: FC = () => {
const theme = useTheme()

18
src/components/create-user-step.tsx → src/app/components/create-user-step.tsx

@ -2,18 +2,18 @@ import React, { FC } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import zxcvbn from 'zxcvbn'
import { setFieldNotification } from 'src/actions/forms'
import { showNotification } from 'src/actions/notifications'
import { setStep } from 'src/actions/registration'
import { getForm } from 'src/selectors/forms'
import { MAX_ID_LENGTH, MAX_NAME_LENGTH } from 'src/constants'
import { valueFromForm } from 'src/utils'
import { setFieldNotification } from '../actions/forms'
import { showNotification } from '../actions/notifications'
import { setStep } from '../actions/registration'
import { getForm } from '../selectors/forms'
import { MAX_ID_LENGTH, MAX_NAME_LENGTH } from '../constants'
import { valueFromForm } from '../utils'
import { AppThunkDispatch, NotificationType } from 'src/types'
import { AppThunkDispatch, NotificationType } from '../types'
import CreateUserForm from './create-user-form'
import HorizontalRule from 'src/components/horizontal-rule'
import PrimaryButton from 'src/components/controls/primary-button'
import HorizontalRule from '../components/horizontal-rule'
import PrimaryButton from '../components/controls/primary-button'
const CreateUserStep: FC = () => {
const form = useSelector(getForm)

2
src/components/footer.tsx → src/app/components/footer.tsx

@ -1,6 +1,6 @@
import React, { FC } from 'react'
import { Link } from 'react-router-dom'
import { useTheme } from 'src/hooks'
import { useTheme } from '../hooks'
const Divider: FC = () => <>&nbsp;&nbsp;&#9900;&nbsp;&nbsp;</>

4
src/components/form-notification.tsx → src/app/components/form-notification.tsx

@ -1,6 +1,6 @@
import React, { FC } from 'react'
import { useTheme } from 'src/hooks'
import { NotificationType, FormNotification } from 'src/types'
import { useTheme } from '../hooks'
import { NotificationType, FormNotification } from '../types'
interface Props {
notification: FormNotification

18
src/components/group-invitations.tsx → src/app/components/group-invitations.tsx

@ -4,17 +4,17 @@ import { Link, useHistory } from 'react-router-dom'
import { faCheckCircle, faStopwatch, faPauseCircle } from '@fortawesome/free-solid-svg-icons'
import moment from 'moment'
import { useTheme } from 'src/hooks'
import { handleApiError } from 'src/api/errors'
import { fetchInvitations, createInvitation } from 'src/actions/groups'
import { getInvitations } from 'src/selectors/groups'
import { getFieldValue } from 'src/selectors/forms'
import { useTheme } from '../hooks'
import { handleApiError } from '../api/errors'
import { fetchInvitations, createInvitation } from '../actions/groups'
import { getInvitations } from '../selectors/groups'
import { getFieldValue } from '../selectors/forms'
import { AppState, AppThunkDispatch } from 'src/types'
import { AppState, AppThunkDispatch } from '../types'
import PrimaryButton from 'src/components/controls/primary-button'
import Subtitle from 'src/components/subtitle'
import SelectField from 'src/components/controls/select-field'
import PrimaryButton from '../components/controls/primary-button'
import Subtitle from '../components/subtitle'
import SelectField from '../components/controls/select-field'
interface Props {
group: string

7
src/components/group-list-item.tsx → src/app/components/group-list-item.tsx

@ -1,7 +1,7 @@
import React, { FC } from 'react'
import { Link } from 'react-router-dom'
import { useTheme, useConfig } from 'src/hooks'
import { Group } from 'src/types'
import { useTheme } from '../hooks'
import { Group } from '../types'
interface Props {
group: Group
@ -9,13 +9,12 @@ interface Props {
const GroupListItem: FC<Props> = ({ group }) => {
const theme = useTheme()
const config = useConfig()
return (
<div className="group-list-item" style={{ backgroundColor: theme.backgroundPrimary, borderColor: theme.backgroundSecondary }}>
{group.imageUrl &&
<div className="image">
<img src={`${config.blobUrl}${group.imageUrl}`} />
<img src={group.imageUrl} />
</div>
}
<div>

8
src/components/group-logs.tsx → src/app/components/group-logs.tsx

@ -3,10 +3,10 @@ import { useSelector, useDispatch } from 'react-redux'
import { Link } from 'react-router-dom'
import moment from 'moment'
import { handleApiError } from 'src/api/errors'
import { fetchLogs } from 'src/actions/groups'
import { getLogs } from 'src/selectors/groups'
import { useTheme } from 'src/hooks'
import { handleApiError } from '../api/errors'
import { fetchLogs } from '../actions/groups'
import { getLogs } from '../selectors/groups'
import { useTheme } from '../hooks'
interface Props {
group: string

2
src/components/help-text.tsx → src/app/components/help-text.tsx

@ -1,5 +1,5 @@
import React, { FC } from 'react'
import { useTheme } from 'src/hooks'
import { useTheme } from '../hooks'
const Notification: FC = ({ children }) => {
const theme = useTheme()

2
src/components/horizontal-rule.tsx → src/app/components/horizontal-rule.tsx

@ -1,5 +1,5 @@
import React, { FC } from 'react'
import { useTheme } from 'src/hooks'
import { useTheme } from '../hooks'
const HorizontalRule: FC = () => {
const theme = useTheme()

4
src/components/level.tsx → src/app/components/level.tsx

@ -1,6 +1,6 @@
import React, { FC } from 'react'
import { useTheme } from 'src/hooks'
import { LevelItem } from 'src/types'
import { useTheme } from '../hooks'
import { LevelItem } from '../types'
interface Props {
items: LevelItem[]

2
src/components/logo.tsx → src/app/components/logo.tsx

@ -1,6 +1,6 @@
import React, { FC } from 'react'
import { useHistory } from 'react-router-dom'
import { useTheme } from 'src/hooks'
import { useTheme } from '../hooks'
const Logo: FC = () => {
const theme = useTheme()

4
src/components/member-list-item.tsx → src/app/components/member-list-item.tsx

@ -1,8 +1,8 @@
import React, { FC } from 'react'
import { Link } from 'react-router-dom'
import capitalize from 'lodash/capitalize'
import { useTheme } from 'src/hooks'
import { User, GroupMembershipType } from 'src/types'
import { useTheme } from '../hooks'
import { User, GroupMembershipType } from '../types'
interface Props {
member: User

8
src/components/member-list.tsx → src/app/components/member-list.tsx

@ -2,10 +2,10 @@ import React, { FC, useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { useHistory } from 'react-router-dom'
import { handleApiError } from 'src/api/errors'
import { fetchGroupMembers } from 'src/actions/groups'
import { getGroupMembers } from 'src/selectors/groups'
import { AppState, User, AppThunkDispatch } from 'src/types'
import { handleApiError } from '../api/errors'
import { fetchGroupMembers } from '../actions/groups'
import { getGroupMembers } from '../selectors/groups'
import { AppState, User, AppThunkDispatch } from '../types'
import MemberListItem from './member-list-item'

8
src/components/navigation-menu.tsx → src/app/components/navigation-menu.tsx

@ -3,10 +3,10 @@ import { Link } from 'react-router-dom'
import { useSelector, useDispatch } from 'react-redux'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faStream, faPaperPlane, faSun, faMoon } from '@fortawesome/free-solid-svg-icons'
import { useTheme } from 'src/hooks'
import { setColorScheme } from 'src/actions/theme'
import { getColorScheme } from 'src/selectors/theme'
import { ColorScheme } from 'src/types'
import { useTheme } from '../hooks'
import { setColorScheme } from '../actions/theme'
import { getColorScheme } from '../selectors/theme'
import { ColorScheme } from '../types'
const NavigationMenu: FC = () => {
const theme = useTheme()

4
src/components/notification-container.tsx → src/app/components/notification-container.tsx

@ -3,8 +3,8 @@ import { useSelector, useDispatch } from 'react-redux'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faDoorOpen } from '@fortawesome/free-solid-svg-icons'
import { setNotificationAuto, removeNotification } from 'src/actions/notifications'
import { getNotifications } from 'src/selectors'
import { setNotificationAuto, removeNotification } from '../actions/notifications'
import { getNotifications } from '../selectors'
import Notification from './notification'

4
src/components/notification.tsx → src/app/components/notification.tsx

@ -1,8 +1,8 @@
import React, { FC, MouseEventHandler } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faWindowClose } from '@fortawesome/free-solid-svg-icons'
import { useTheme } from 'src/hooks'
import { NotificationType } from 'src/types'
import { useTheme } from '../hooks'
import { NotificationType } from '../types'
interface Props {
id: string

10
src/components/pages/about.tsx → src/app/components/pages/about.tsx

@ -1,11 +1,11 @@
import React, { FC, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { useTheme } from 'src/hooks'
import { setTitle } from 'src/utils'
import { useTheme } from '../../hooks'
import { setTitle } from '../../utils'
import Section from 'src/components/section'
import Title from 'src/components/title'
import Subtitle from 'src/components/subtitle'
import Section from '../../components/section'
import Title from '../../components/title'
import Subtitle from '../../components/subtitle'
const About: FC = () => {
const theme = useTheme()

28
src/components/pages/admin-apps.tsx → src/app/components/pages/admin-apps.tsx

@ -1,21 +1,21 @@
import React, { FC, useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { useHistory } from 'react-router-dom'
import { useAuthenticationCheck, useTheme } from 'src/hooks'
import { fetchPendingApps, activateApp, setPreinstall } from 'src/actions/apps'
import { getAuthenticatedUser, getChecked } from 'src/selectors/authentication'
import { getPendingApps } from 'src/selectors/apps'
import { handleApiError } from 'src/api/errors'
import { setTitle } from 'src/utils'
import { AppThunkDispatch } from 'src/types'
import { useAuthenticationCheck, useTheme } from '../../hooks'
import { fetchPendingApps, activateApp, setPreinstall } from '../../actions/apps'
import { getAuthenticatedUser, getChecked } from '../../selectors/authentication'
import { getPendingApps } from '../../selectors/apps'
import { handleApiError } from '../../api/errors'
import { setTitle } from '../../utils'
import { AppThunkDispatch } from '../../types'
import Section from 'src/components/section'
import Title from 'src/components/title'
import Loading from 'src/components/pages/loading'
import AppListItem from 'src/components/app-list-item'
import HorizontalRule from 'src/components/horizontal-rule'
import PrimaryButton from 'src/components/controls/primary-button'
import SecondaryButton from 'src/components/controls/secondary-button'
import Section from '../../components/section'
import Title from '../../components/title'
import Loading from '../../components/pages/loading'
import AppListItem from '../../components/app-list-item'
import HorizontalRule from '../../components/horizontal-rule'
import PrimaryButton from '../../components/controls/primary-button'
import SecondaryButton from '../../components/controls/secondary-button'
const AdminApps: FC = () => {
useAuthenticationCheck()

26
src/components/pages/admin-groups.tsx → src/app/components/pages/admin-groups.tsx

@ -1,20 +1,20 @@
import React, { FC, useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { useHistory } from 'react-router-dom'
import { useAuthenticationCheck, useTheme } from 'src/hooks'
import { fetchPendingGroups, activateGroup } from 'src/actions/groups'
import { getAuthenticatedUser, getChecked } from 'src/selectors/authentication'
import { getPendingGroups } from 'src/selectors/groups'
import { handleApiError } from 'src/api/errors'
import { setTitle } from 'src/utils'
import { AppThunkDispatch } from 'src/types'
import { useAuthenticationCheck, useTheme } from '../../hooks'
import { fetchPendingGroups, activateGroup } from '../../actions/groups'
import { getAuthenticatedUser, getChecked } from '../../selectors/authentication'
import { getPendingGroups } from '../../selectors/groups'
import { handleApiError } from '../../api/errors'
import { setTitle } from '../../utils'
import { AppThunkDispatch } from '../../types'
import Section from 'src/components/section'
import Title from 'src/components/title'
import Loading from 'src/components/pages/loading'
import GroupListItem from 'src/components/group-list-item'
import HorizontalRule from 'src/components//horizontal-rule'
import PrimaryButton from 'src/components/controls/primary-button'
import Section from '../../components/section'
import Title from '../../components/title'
import Loading from '../../components/pages/loading'
import GroupListItem from '../../components/group-list-item'
import HorizontalRule from '../../components//horizontal-rule'
import PrimaryButton from '../../components/controls/primary-button'
const AdminGroups: FC = () => {
useAuthenticationCheck()

16
src/components/pages/admin.tsx → src/app/components/pages/admin.tsx

@ -1,14 +1,14 @@
import React, { FC, useEffect } from 'react'
import { useSelector } from 'react-redux'
import { Link, useHistory } from 'react-router-dom'
import { useTheme, useAuthenticationCheck } from 'src/hooks'
import { getAuthenticatedUser, getChecked } from 'src/selectors/authentication'
import { setTitle } from 'src/utils'
import Section from 'src/components/section'
import Title from 'src/components/title'
import HorizontalRule from 'src/components/horizontal-rule'
import Loading from 'src/components/pages/loading'
import { useTheme, useAuthenticationCheck } from '../../hooks'
import { getAuthenticatedUser, getChecked } from '../../selectors/authentication'
import { setTitle } from '../../utils'
import Section from '../../components/section'
import Title from '../../components/title'
import HorizontalRule from '../../components/horizontal-rule'
import Loading from '../../components/pages/loading'
const Admin: FC = () => {
useAuthenticationCheck()

20
src/components/pages/apps.tsx → src/app/components/pages/apps.tsx

@ -1,16 +1,16 @@
import React, { FC, useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { fetchApps } from 'src/actions/apps'
import { getApps } from 'src/selectors/apps'
import { useTheme } from 'src/hooks'
import { setTitle } from 'src/utils'
import { AppThunkDispatch } from 'src/types'
import Title from 'src/components/title'
import Section from 'src/components/section'
import HorizontalRule from 'src/components/horizontal-rule'
import AppListItem from 'src/components/app-list-item'
import { fetchApps } from '../../actions/apps'
import { getApps } from '../../selectors/apps'
import { useTheme } from '../../hooks'
import { setTitle } from '../../utils'
import { AppThunkDispatch } from '../../types'
import Title from '../../components/title'
import Section from '../../components/section'
import HorizontalRule from '../../components/horizontal-rule'
import AppListItem from '../../components/app-list-item'
const Apps: FC = () => {
const theme = useTheme()

38
src/components/pages/create-app.tsx → src/app/components/pages/create-app.tsx

@ -3,25 +3,25 @@ import { useDispatch, useSelector } from 'react-redux'
import { Link, useHistory } from 'react-router-dom'
import { faCheckCircle, faIdCard, faLink, faAddressBook, faCodeBranch } from '@fortawesome/free-solid-svg-icons'
import { checkAppAvailability, createApp } from 'src/actions/apps'
import { initForm, initField, setFieldNotification } from 'src/actions/forms'
import { showNotification } from 'src/actions/notifications'
import { getForm } from 'src/selectors/forms'
import { getIsFetching } from 'src/selectors/requests'
import { useTheme } from 'src/hooks'
import { setTitle, valueFromForm } from 'src/utils'
import { AppState, NotificationType, AppThunkDispatch, RequestKey } from 'src/types'
import Title from 'src/components/title'
import Section from 'src/components/section'
import HorizontalRule from 'src/components/horizontal-rule'
import PrimaryButton from 'src/components/controls/primary-button'
import TextField from 'src/components/controls/text-field'
import TextareaField from 'src/components/controls/textarea-field'
import CheckboxField from 'src/components/controls/checkbox-field'
import ImageField from 'src/components/controls/image-field'
import CoverImageField from 'src/components/controls/cover-image-field'
import IconImageField from 'src/components/controls/icon-image-field'
import { checkAppAvailability, createApp } from '../../actions/apps'
import { initForm, initField, setFieldNotification } from '../../actions/forms'
import { showNotification } from '../../actions/notifications'
import { getForm } from '../../selectors/forms'
import { getIsFetching } from '../../selectors/requests'
import { useTheme } from '../../hooks'
import { setTitle, valueFromForm } from '../../utils'
import { AppState, NotificationType, AppThunkDispatch, RequestKey } from '../../types'
import Title from '../../components/title'
import Section from '../../components/section'
import HorizontalRule from '../../components/horizontal-rule'
import PrimaryButton from '../../components/controls/primary-button'
import TextField from '../../components/controls/text-field'
import TextareaField from '../../components/controls/textarea-field'
import CheckboxField from '../../components/controls/checkbox-field'
import ImageField from '../../components/controls/image-field'
import CoverImageField from '../../components/controls/cover-image-field'
import IconImageField from '../../components/controls/icon-image-field'
const CreateApp: FC = () => {
const theme = useTheme()

18
src/components/pages/developers.tsx → src/app/components/pages/developers.tsx

@ -3,16 +3,16 @@ import { useSelector, useDispatch } from 'react-redux'
import { useHistory } from 'react-router-dom'
import { faPlusCircle } from '@fortawesome/free-solid-svg-icons'
import { fetchCreatedApps } from 'src/actions/apps'
import { getCreatedApps } from 'src/selectors/apps'
import { setTitle } from 'src/utils'
import { AppThunkDispatch } from 'src/types'
import { fetchCreatedApps } from '../../actions/apps'
import { getCreatedApps } from '../../selectors/apps'
import { setTitle } from '../../utils'
import { AppThunkDispatch } from '../../types'
import Title from 'src/components/title'
import Subtitle from 'src/components/subtitle'
import Section from 'src/components/section'
import HorizontalRule from 'src/components/horizontal-rule'
import PrimaryButton from 'src/components/controls/primary-button'
import Title from '../../components/title'
import Subtitle from '../../components/subtitle'
import Section from '../../components/section'
import HorizontalRule from '../../components/horizontal-rule'
import PrimaryButton from '../../components/controls/primary-button'
const Developers: FC = () => {
const apps = useSelector(getCreatedApps)

48
src/components/pages/edit-app.tsx → src/app/components/pages/edit-app.tsx

@ -3,30 +3,30 @@ import { useSelector, useDispatch } from 'react-redux'
import { useParams, useHistory } from 'react-router-dom'
import { faIdCard, faCheckCircle, faKey, faShieldAlt, faLink, faAddressBook, faCodeBranch } from '@fortawesome/free-solid-svg-icons'
import { handleApiError } from 'src/api/errors'
import { fetchApp, updateApp } from 'src/actions/apps'
import { initForm, initField, setFieldNotification } from 'src/actions/forms'
import { showNotification } from 'src/actions/notifications'
import { getAuthenticatedUserId } from 'src/selectors/authentication'
import { getEntity } from 'src/selectors/entities'
import { getForm } from 'src/selectors/forms'
import { getIsFetching } from 'src/selectors/requests'
import { useAuthenticationCheck, useDeepCompareEffect, useTheme } from 'src/hooks'
import { setTitle, valueFromForm } from 'src/utils'
import { AppState, AppThunkDispatch, EntityType, App, NotificationType, RequestKey } from 'src/types'
import Title from 'src/components/title'
import Section from 'src/components/section'
import HorizontalRule from 'src/components/horizontal-rule'
import Loading from 'src/components/pages/loading'
import TextField from 'src/components/controls/text-field'
import TextareaField from 'src/components/controls/textarea-field'
import ImageField from 'src/components/controls/image-field'
import CoverImageField from 'src/components/controls/cover-image-field'
import IconImageField from 'src/components/controls/icon-image-field'
import StaticField from 'src/components/controls/static-field'
import PrimaryButton from 'src/components/controls/primary-button'
import { handleApiError } from '../../api/errors'
import { fetchApp, updateApp } from '../../actions/apps'
import { initForm, initField, setFieldNotification } from '../../actions/forms'
import { showNotification } from '../../actions/notifications'
import { getAuthenticatedUserId } from '../../selectors/authentication'
import { getEntity } from '../../selectors/entities'
import { getForm } from '../../selectors/forms'
import { getIsFetching } from '../../selectors/requests'
import { useAuthenticationCheck, useDeepCompareEffect, useTheme } from '../../hooks'
import { setTitle, valueFromForm } from '../../utils'
import { AppState, AppThunkDispatch, EntityType, App, NotificationType, RequestKey } from '../../types'
import Title from '../../components/title'
import Section from '../../components/section'
import HorizontalRule from '../../components/horizontal-rule'
import Loading from '../../components/pages/loading'
import TextField from '../../components/controls/text-field'
import TextareaField from '../../components/controls/textarea-field'
import ImageField from '../../components/controls/image-field'
import CoverImageField from '../../components/controls/cover-image-field'
import IconImageField from '../../components/controls/icon-image-field'
import StaticField from '../../components/controls/static-field'
import PrimaryButton from '../../components/controls/primary-button'
interface Params {
id: string

50
src/components/pages/group-admin.tsx → src/app/components/pages/group-admin.tsx

@ -3,14 +3,14 @@ import { useSelector, useDispatch } from 'react-redux'
import { Link, useParams, useHistory } from 'react-router-dom'
import { faCheckCircle, faIdCard } from '@fortawesome/free-solid-svg-icons'
import { handleApiError } from 'src/api/errors'
import { initForm, initField } from 'src/actions/forms'
import { fetchGroup, updateGroup } from 'src/actions/groups'
import { getEntity } from 'src/selectors/entities'
import { getForm } from 'src/selectors/forms'
import { useDeepCompareEffect, useTheme } from 'src/hooks'
import { setTitle, valueFromForm } from 'src/utils'
import { handleApiError } from '../../api/errors'
import { initForm, initField } from '../../actions/forms'
import { fetchGroup, updateGroup } from '../../actions/groups'
import { getEntity } from '../../selectors/entities'
import { getForm } from '../../selectors/forms'
import { useDeepCompareEffect, useTheme } from '../../hooks'
import { setTitle, valueFromForm } from '../../utils'
import {
AppState,
AppThunkDispatch,
@ -18,23 +18,23 @@ import {
GroupMembershipType,
Tab,
EntityType,
} from 'src/types'
import Title from 'src/components/title'
import Subtitle from 'src/components/subtitle'
import Section from 'src/components/section'
import HorizontalRule from 'src/components/horizontal-rule'
import PrimaryButton from 'src/components/controls/primary-button'
import MemberList from 'src/components/member-list'
import GroupInvitations from 'src/components/group-invitations'
import GroupLogs from 'src/components/group-logs'
import Loading from 'src/components/pages/loading'
import TextareaField from 'src/components/controls/textarea-field'
import ImageField from 'src/components/controls/image-field'
import CoverImageField from 'src/components/controls/cover-image-field'
import IconImageField from 'src/components/controls/icon-image-field'
import StaticField from 'src/components/controls/static-field'
} from '../../types'
import Title from '../../components/title'
import Subtitle from '../../components/subtitle'
import Section from '../../components/section'
import HorizontalRule from '../../components/horizontal-rule'
import PrimaryButton from '../../components/controls/primary-button'
import MemberList from '../../components/member-list'
import GroupInvitations from '../../components/group-invitations'
import GroupLogs from '../../components/group-logs'
import Loading from '../../components/pages/loading'
import TextareaField from '../../components/controls/textarea-field'
import ImageField from '../../components/controls/image-field'
import CoverImageField from '../../components/controls/cover-image-field'
import IconImageField from '../../components/controls/icon-image-field'
import StaticField from '../../components/controls/static-field'
interface Params {
id: string

20
src/components/pages/groups.tsx → src/app/components/pages/groups.tsx

@ -3,16 +3,16 @@ import { useSelector, useDispatch } from 'react-redux'
import { useHistory } from 'react-router-dom'
import { faPlusCircle } from '@fortawesome/free-solid-svg-icons'
import { fetchGroups } from 'src/actions/groups'
import { getGroups } from 'src/selectors/groups'
import { setTitle } from 'src/utils'
import { AppThunkDispatch } from 'src/types'
import Title from 'src/components/title'
import Section from 'src/components/section'
import HorizontalRule from 'src/components/horizontal-rule'
import GroupListItem from 'src/components/group-list-item'
import PrimaryButton from 'src/components/controls/primary-button'
import { fetchGroups } from '../../actions/groups'
import { getGroups } from '../../selectors/groups'
import { setTitle } from '../../utils'
import { AppThunkDispatch } from '../../types'
import Title from '../../components/title'
import Section from '../../components/section'
import HorizontalRule from '../../components/horizontal-rule'
import GroupListItem from '../../components/group-list-item'
import PrimaryButton from '../../components/controls/primary-button'
const Groups: FC = () => {
const groups = useSelector(getGroups)

20
src/components/pages/home.tsx → src/app/components/pages/home.tsx

@ -1,16 +1,16 @@
import React, { FC, useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { fetchTimeline } from 'src/actions/posts'
import { getAuthenticated } from 'src/selectors/authentication'
import { setTitle } from 'src/utils'
import { AppThunkDispatch } from 'src/types'
import Title from 'src/components/title'
import Composer from 'src/components/composer'
import Timeline from 'src/components/timeline'
import Section from 'src/components/section'
import Subtitle from 'src/components/subtitle'
import { fetchTimeline } from '../../actions/posts'
import { getAuthenticated } from '../../selectors/authentication'
import { setTitle } from '../../utils'
import { AppThunkDispatch } from '../../types'
import Title from '../../components/title'
import Composer from '../../components/composer'
import Timeline from '../../components/timeline'
import Section from '../../components/section'
import Subtitle from '../../components/subtitle'
const Home: FC = () => {
const authenticated = useSelector(getAuthenticated)

6
src/components/pages/loading.tsx → src/app/components/pages/loading.tsx

@ -1,9 +1,9 @@
import React, { FC } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
import { useTheme } from 'src/hooks'
import Title from 'src/components/title'
import Section from 'src/components/section'
import { useTheme } from '../../hooks'
import Title from '../../components/title'
import Section from '../../components/section'
const Loading: FC = () => {
const theme = useTheme()

26
src/components/pages/login.tsx → src/app/components/pages/login.tsx

@ -3,21 +3,21 @@ import { useSelector, useDispatch } from 'react-redux'
import { useHistory } from 'react-router'
import { faIdCard } from '@fortawesome/free-solid-svg-icons'
import { handleApiError } from 'src/api/errors'
import { authenticate } from 'src/actions/authentication'
import { showNotification } from 'src/actions/notifications'
import { getChecked, getAuthenticated } from 'src/selectors/authentication'
import { getFieldValue } from 'src/selectors/forms'
import { getIsFetching } from 'src/selectors/requests'
import { initForm, initField, setFieldNotification } from 'src/actions/forms'
import { handleApiError } from '../../api/errors'
import { authenticate } from '../../actions/authentication'
import { showNotification } from '../../actions/notifications'
import { getChecked, getAuthenticated } from '../../selectors/authentication'
import { getFieldValue } from '../../selectors/forms'
import { getIsFetching } from '../../selectors/requests'
import { initForm, initField, setFieldNotification } from '../../actions/forms'
import Title from 'src/components/title'
import Section from 'src/components/section'
import TextField from 'src/components/controls/text-field'
import PasswordField from 'src/components/controls/password-field'
import PrimaryButton from 'src/components/controls/primary-button'
import Title from '../../components/title'
import Section from '../../components/section'
import TextField from '../../components/controls/text-field'
import PasswordField from '../../components/controls/password-field'
import PrimaryButton from '../../components/controls/primary-button'
import { AppState, RequestKey, NotificationType } from 'src/types'
import { AppState, RequestKey, NotificationType } from '../../types'
const Login: FC = () => {
const checked = useSelector(getChecked)

40
src/components/pages/register-group.tsx → src/app/components/pages/register-group.tsx

@ -3,26 +3,26 @@ import { useSelector, useDispatch } from 'react-redux'
import { useParams, useHistory } from 'react-router'
import { faUserPlus } from '@fortawesome/free-solid-svg-icons'
import { handleApiError } from 'src/api/errors'
import { fetchGroup } from 'src/actions/groups'
import { initField } from 'src/actions/forms'
import { showNotification } from 'src/actions/notifications'
import { register } from 'src/actions/registration'
import { getEntity } from 'src/selectors/entities'
import { getForm } from 'src/selectors/forms'
import { setTitle, valueFromForm, getDefaultThemeName } from 'src/utils'
import { useDeepCompareEffect } from 'src/hooks'
import { AppState, AppThunkDispatch, Group, EntityType, NotificationType } from 'src/types'
import Title from 'src/components/title'
import Subtitle from 'src/components/subtitle'
import Section from 'src/components/section'
import HorizontalRule from 'src/components/horizontal-rule'
import PrimaryButton from 'src/components/controls/primary-button'
import Loading from 'src/components/pages/loading'
import CreateUserForm from 'src/components/create-user-form'
import { handleApiError } from '../../api/errors'
import { fetchGroup } from '../../actions/groups'
import { initField } from '../../actions/forms'
import { showNotification } from '../../actions/notifications'
import { register } from '../../actions/registration'
import { getEntity } from '../../selectors/entities'
import { getForm } from '../../selectors/forms'
import { setTitle, valueFromForm, getDefaultThemeName } from '../../utils'
import { useDeepCompareEffect } from '../../hooks'
import { AppState, AppThunkDispatch, Group, EntityType, NotificationType } from '../../types'
import Title from '../../components/title'
import Subtitle from '../../components/subtitle'
import Section from '../../components/section'
import HorizontalRule from '../../components/horizontal-rule'
import PrimaryButton from '../../components/controls/primary-button'
import Loading from '../../components/pages/loading'
import CreateUserForm from '../../components/create-user-form'
interface Params {
id: string

28
src/components/pages/register.tsx → src/app/components/pages/register.tsx

@ -2,20 +2,20 @@ import React, { FC, useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { useHistory } from 'react-router'
import { handleApiError } from 'src/api/errors'
import { getForm } from 'src/selectors/forms'
import { getStep } from 'src/selectors/registration'
import { initForm, initField } from 'src/actions/forms'
import { showNotification } from 'src/actions/notifications'
import { createGroup, register } from 'src/actions/registration'
import { setTitle, valueFromForm, getDefaultThemeName } from 'src/utils'
import { AppThunkDispatch, NotificationType } from 'src/types'
import Title from 'src/components/title'
import Section from 'src/components/section'
import HorizontalRule from 'src/components/horizontal-rule'
import CreateGroupStep from 'src/components/create-group-step'
import CreateUserStep from 'src/components/create-user-step'
import { handleApiError } from '../../api/errors'
import { getForm } from '../../selectors/forms'
import { getStep } from '../../selectors/registration'
import { initForm, initField } from '../../actions/forms'
import { showNotification } from '../../actions/notifications'
import { createGroup, register } from '../../actions/registration'
import { setTitle, valueFromForm, getDefaultThemeName } from '../../utils'
import { AppThunkDispatch, NotificationType } from '../../types'
import Title from '../../components/title'
import Section from '../../components/section'
import HorizontalRule from '../../components/horizontal-rule'
import CreateGroupStep from '../../components/create-group-step'
import CreateUserStep from '../../components/create-user-step'
const Register: FC = () => {
const stepIndex = useSelector(getStep)

50
src/components/pages/self.tsx → src/app/components/pages/self.tsx

@ -3,31 +3,31 @@ import { useSelector, useDispatch } from 'react-redux'
import { useHistory } from 'react-router-dom'
import { faDoorOpen, faCheckCircle, faIdCard, faEnvelope, faUserShield, faUserCircle } from '@fortawesome/free-solid-svg-icons'
import { unauthenticate, updateSelf } from 'src/actions/authentication'
import { initForm, initField } from 'src/actions/forms'
import { getAuthenticatedUser } from 'src/selectors/authentication'
import { getForm } from 'src/selectors/forms'
import { handleApiError } from 'src/api/errors'
import { PRIVACY_OPTIONS } from 'src/constants'
import { useAuthenticationCheck, useDeepCompareEffect } from 'src/hooks'
import { setTitle, valueFromForm } from 'src/utils'
import Title from 'src/components/title'
import Subtitle from 'src/components/subtitle'
import Section from 'src/components/section'
import HorizontalRule from 'src/components/horizontal-rule'
import PrimaryButton from 'src/components/controls/primary-button'
import SecondaryButton from 'src/components/controls/secondary-button'
import Loading from 'src/components/pages/loading'
import TextField from 'src/components/controls/text-field'
import TextareaField from 'src/components/controls/textarea-field'
import SelectField from 'src/components/controls/select-field'
import CheckboxField from 'src/components/controls/checkbox-field'
import ImageField from 'src/components/controls/image-field'
import CoverImageField from 'src/components/controls/cover-image-field'
import ThemeField from 'src/components/controls/theme-field'
import StaticField from 'src/components/controls/static-field'
import { unauthenticate, updateSelf } from '../../actions/authentication'
import { initForm, initField } from '../../actions/forms'
import { getAuthenticatedUser } from '../../selectors/authentication'
import { getForm } from '../../selectors/forms'
import { handleApiError } from '../../api/errors'
import { PRIVACY_OPTIONS } from '../../constants'
import { useAuthenticationCheck, useDeepCompareEffect } from '../../hooks'
import { setTitle, valueFromForm } from '../../utils'
import Title from '../../components/title'
import Subtitle from '../../components/subtitle'
import Section from '../../components/section'
import HorizontalRule from '../../components/horizontal-rule'
import PrimaryButton from '../../components/controls/primary-button'
import SecondaryButton from '../../components/controls/secondary-button'
import Loading from '../../components/pages/loading'
import TextField from '../../components/controls/text-field'
import TextareaField from '../../components/controls/textarea-field'
import SelectField from '../../components/controls/select-field'
import CheckboxField from '../../components/controls/checkbox-field'
import ImageField from '../../components/controls/image-field'
import CoverImageField from '../../components/controls/cover-image-field'
import ThemeField from '../../components/controls/theme-field'
import StaticField from '../../components/controls/static-field'
const Self: FC = () => {
const dispatch = useDispatch()

48
src/components/pages/view-app.tsx → src/app/components/pages/view-app.tsx

@ -4,24 +4,24 @@ import { Link, useParams, useHistory } from 'react-router-dom'
import { faPlusSquare, faMinusSquare } from '@fortawesome/free-solid-svg-icons'
import moment from 'moment'
import { handleApiError } from 'src/api/errors'
import { fetchApp, installApp, uninstallApp } from 'src/actions/apps'
import { fetchInstallations } from 'src/actions/composer'
import { getAuthenticatedUserId } from 'src/selectors/authentication'
import { getInstallations } from 'src/selectors/composer'
import { getEntity } from 'src/selectors/entities'
import { getIsFetching } from 'src/selectors/requests'
import { useConfig, useTheme } from 'src/hooks'
import { setTitle, urlForBlob } from 'src/utils'
import { AppState, AppThunkDispatch, EntityType, App, RequestKey, LevelItem } from 'src/types'
import Title from 'src/components/title'
import Section from 'src/components/section'
import HorizontalRule from 'src/components/horizontal-rule'
import Button from 'src/components/controls/button'
import Level from 'src/components/level'
import Loading from 'src/components/pages/loading'
import { handleApiError } from '../../api/errors'
import { fetchApp, installApp, uninstallApp } from '../../actions/apps'
import { fetchInstallations } from '../../actions/composer'
import { getAuthenticatedUserId } from '../../selectors/authentication'
import { getInstallations } from '../../selectors/composer'
import { getEntity } from '../../selectors/entities'
import { getIsFetching } from '../../selectors/requests'
import { useTheme } from '../../hooks'
import { setTitle } from '../../utils'
import { AppState, AppThunkDispatch, EntityType, App, RequestKey, LevelItem } from '../../types'
import Title from '../../components/title'
import Section from '../../components/section'
import HorizontalRule from '../../components/horizontal-rule'
import Button from '../../components/controls/button'
import Level from '../../components/level'
import Loading from '../../components/pages/loading'
interface Params {
id: string
@ -35,7 +35,6 @@ const ViewApp: FC = () => {
const selfId = useSelector(getAuthenticatedUserId)
const fetching = useSelector<AppState, boolean>(state => getIsFetching(state, RequestKey.InstallApp) || getIsFetching(state, RequestKey.UninstallApp))
const dispatch = useDispatch<AppThunkDispatch>()
const config = useConfig()
const history = useHistory()
useEffect(() => {
@ -78,9 +77,6 @@ const ViewApp: FC = () => {
}
}
const imageUrl = app.imageUrl ? urlForBlob(config, app.imageUrl) : undefined
const coverImageUrl = app.coverImageUrl ? urlForBlob(config, app.coverImageUrl) : undefined
const items: LevelItem[] = []
items.push({
label: 'Users',
@ -106,16 +102,16 @@ const ViewApp: FC = () => {
return (
<div>
<Section>
{coverImageUrl &&
{app.coverImageUrl &&
<div className="cover-image">
<img src={coverImageUrl} />
<img src={app.coverImageUrl} />
</div>
}
<div className="header">
{imageUrl &&
{app.imageUrl &&
<div className="image">
<img src={imageUrl} />
<img src={app.imageUrl} />
</div>
}
<div>

47
src/components/pages/view-group.tsx → src/app/components/pages/view-group.tsx

@ -4,24 +4,24 @@ import { useParams, useHistory } from 'react-router-dom'
import { faEdit, faUserCheck, faBan } from '@fortawesome/free-solid-svg-icons'
import moment from 'moment'
import { handleApiError } from 'src/api/errors'
import { setTheme } from 'src/actions/theme'
import { fetchGroup } from 'src/actions/groups'
import { getAuthenticated } from 'src/selectors/authentication'
import { getEntity } from 'src/selectors/entities'
import { getThemeName } from 'src/selectors/theme'
import { useDeepCompareEffect, useConfig, useTheme, useSetting } from 'src/hooks'
import { setTitle, urlForBlob } from 'src/utils'
import { AppState, EntityType, Group, GroupMembershipType, AppThunkDispatch, LevelItem } from 'src/types'
import Title from 'src/components/title'
import Level from 'src/components/level'
import Section from 'src/components/section'
import PrimaryButton from 'src/components/controls/primary-button'
import Button from 'src/components/controls/button'
import Loading from 'src/components/pages/loading'
import HorizontalRule from 'src/components/horizontal-rule'
import { handleApiError } from '../../api/errors'
import { setTheme } from '../../actions/theme'
import { fetchGroup } from '../../actions/groups'
import { getAuthenticated } from '../../selectors/authentication'
import { getEntity } from '../../selectors/entities'
import { getThemeName } from '../../selectors/theme'
import { useDeepCompareEffect, useTheme, useSetting } from '../../hooks'
import { setTitle } from '../../utils'
import { AppState, EntityType, Group, GroupMembershipType, AppThunkDispatch, LevelItem } from '../../types'
import Title from '../../components/title'
import Level from '../../components/level'
import Section from '../../components/section'
import PrimaryButton from '../../components/controls/primary-button'
import Button from '../../components/controls/button'
import Loading from '../../components/pages/loading'
import HorizontalRule from '../../components/horizontal-rule'
interface Params {
id: string
@ -35,7 +35,6 @@ const ViewGroup: FC = () => {
const group = useSelector<AppState, Group | undefined>(state => getEntity<Group>(state, EntityType.Group, id))
const authenticated = useSelector(getAuthenticated)
const dispatch = useDispatch<AppThunkDispatch>()
const config = useConfig()
const history = useHistory()
const allowThemeChange = useSetting<boolean>('allowThemeChange', true)
@ -62,8 +61,6 @@ const ViewGroup: FC = () => {
const isAdmin = group.membership === GroupMembershipType.Admin
const isMember = !!group.membership
const imageUrl = group.imageUrl ? urlForBlob(config, group.imageUrl) : undefined
const coverImageUrl = group.coverImageUrl ? urlForBlob(config, group.coverImageUrl) : undefined
const items: LevelItem[] = []
items.push({
@ -90,16 +87,16 @@ const ViewGroup: FC = () => {
return (
<div>
<Section>
{coverImageUrl &&
{group.coverImageUrl &&
<div className="cover-image">
<img src={coverImageUrl} />
<img src={group.coverImageUrl} />
</div>
}
<div className="header">
{imageUrl &&
{group.imageUrl &&
<div className="image">
<img src={imageUrl} />
<img src={group.imageUrl} />
</div>
}
<div>

28
src/components/pages/view-post.tsx → src/app/components/pages/view-post.tsx

@ -4,22 +4,22 @@ import { useParams, useHistory } from 'react-router-dom'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faArrowsAltV } from '@fortawesome/free-solid-svg-icons'
import { handleApiError } from 'src/api/errors'
import { fetchPost } from 'src/actions/posts'
import { getAuthenticated, getChecked } from 'src/selectors/authentication'
import { getEntity } from 'src/selectors/entities'
import { getPostParents, getPostChildren } from 'src/selectors/posts'
import { handleApiError } from '../../api/errors'
import { fetchPost } from '../../actions/posts'
import { getAuthenticated, getChecked } from '../../selectors/authentication'
import { getEntity } from '../../selectors/entities'
import { getPostParents, getPostChildren } from '../../selectors/posts'
import { setTitle } from 'src/utils'
import { AppState, AppThunkDispatch, EntityType, Post } from 'src/types'
import { setTitle } from '../../utils'
import { AppState, AppThunkDispatch, EntityType, Post } from '../../types'
import Title from 'src/components/title'
import Subtitle from 'src/components/subtitle'
import Section from 'src/components/section'
import Loading from 'src/components/pages/loading'
import PostComponent from 'src/components/post'
import PostList from 'src/components/post-list'
import Composer from 'src/components/composer'
import Title from '../../components/title'
import Subtitle from '../../components/subtitle'
import Section from '../../components/section'
import Loading from '../../components/pages/loading'
import PostComponent from '../../components/post'
import PostList from '../../components/post-list'
import Composer from '../../components/composer'
interface Params {
id: string

51
src/components/pages/view-user.tsx → src/app/components/pages/view-user.tsx

@ -5,26 +5,26 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faUserPlus, faUserMinus, faUserClock, faBan } from '@fortawesome/free-solid-svg-icons'
import moment from 'moment'
import { handleApiError } from 'src/api/errors'
import { fetchUser, subscribe, unsubscribe } from 'src/actions/users'
import { fetchUserPosts } from 'src/actions/posts'
import { setTheme } from 'src/actions/theme'
import { getEntity } from 'src/selectors/entities'
import { getAuthenticatedUser, getChecked } from 'src/selectors/authentication'
import { getUserPosts } from 'src/selectors/posts'
import { getThemeName } from 'src/selectors/theme'
import { useDeepCompareEffect, useConfig, useTheme, useSetting } from 'src/hooks'
import { setTitle, urlForBlob } from 'src/utils'
import { AppState, EntityType, User, Post, AppThunkDispatch, LevelItem } from 'src/types'
import Title from 'src/components/title'
import Subtitle from 'src/components/subtitle'
import Level from 'src/components/level'
import PostList from 'src/components/post-list'
import Section from 'src/components/section'
import HorizontalRule from 'src/components/horizontal-rule'
import Loading from 'src/components/pages/loading'
import { handleApiError } from '../../api/errors'
import { fetchUser, subscribe, unsubscribe } from '../../actions/users'
import { fetchUserPosts } from '../../actions/posts'
import { setTheme } from '../../actions/theme'
import { getEntity } from '../../selectors/entities'
import { getAuthenticatedUser, getChecked } from '../../selectors/authentication'
import { getUserPosts } from '../../selectors/posts'
import { getThemeName } from '../../selectors/theme'
import { useDeepCompareEffect, useTheme, useSetting } from '../../hooks'
import { setTitle } from '../../utils'
import { AppState, EntityType, User, Post, AppThunkDispatch, LevelItem } from '../../types'
import Title from '../../components/title'
import Subtitle from '../../components/subtitle'
import Level from '../../components/level'
import PostList from '../../components/post-list'
import Section from '../../components/section'
import HorizontalRule from '../../components/horizontal-rule'
import Loading from '../../components/pages/loading'
interface Params {
id: string
@ -40,7 +40,6 @@ const ViewUser: FC = () => {
const user = useSelector<AppState, User | undefined>(state => getEntity<User>(state, EntityType.User, id))
const posts = useSelector<AppState, Post[]>(state => getUserPosts(state, id))
const dispatch = useDispatch<AppThunkDispatch>()
const config = useConfig()
const history = useHistory()
const allowThemeChange = useSetting<boolean>('allowThemeChange', true)
@ -72,8 +71,6 @@ const ViewUser: FC = () => {
const isSelf = self && self.id === user.id
const isGroup = self && self.group && user.group && self.group.id === user.group.id
const imageUrl = user.imageUrl ? urlForBlob(config, user.imageUrl) : undefined
const coverImageUrl = user.coverImageUrl ? urlForBlob(config, user.coverImageUrl) : undefined
const subscription = self && user.subscriptions ? user.subscriptions.find(subscription => subscription.from === self.id && subscription.to === user.id) : undefined
const subscribed = subscription && !subscription.pending
const subscriptionPending = subscription && subscription.pending
@ -99,16 +96,16 @@ const ViewUser: FC = () => {
return (
<div>
<Section>
{coverImageUrl &&
{user.coverImageUrl &&
<div className="cover-image">
<img src={coverImageUrl} />
<img src={user.coverImageUrl} />
</div>
}
<div className="header">
{imageUrl &&
{user.imageUrl &&
<div className="image">
<img src={imageUrl} style={{ width: 128 }} />
<img src={user.imageUrl} style={{ width: 128 }} />
</div>
}
<div>

8
src/components/post-list.tsx → src/app/components/post-list.tsx

@ -1,9 +1,9 @@
import React, { FC, useState } from 'react'
import { useTheme } from 'src/hooks'
import { classNames } from 'src/utils'
import { Post, ClassDictionary } from 'src/types'
import { useTheme } from '../hooks'
import { classNames } from '../utils'
import { Post, ClassDictionary } from '../types'
import PostComponent from 'src/components/post'
import PostComponent from '../components/post'
interface Props {
posts: Post[]

10
src/components/post.tsx → src/app/components/post.tsx

@ -4,12 +4,12 @@ import { Link } from 'react-router-dom'
import moment from 'moment'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faClock, faReplyAll, faExclamationCircle } from '@fortawesome/free-solid-svg-icons'
import { useTheme } from 'src/hooks'
import { setEntities } from 'src/actions/entities'
import { normalize } from 'src/utils/normalization'
import { Post, EntityType } from 'src/types'
import { useTheme } from '../hooks'
import { setEntities } from '../actions/entities'
import { normalize } from '../utils/normalization'
import { Post, EntityType } from '../types'
import User from 'src/components/user'
import User from '../components/user'
interface Props {
post: Post

2
src/components/progress.tsx → src/app/components/progress.tsx

@ -1,5 +1,5 @@
import React, { FC } from 'react'
import { useTheme } from 'src/hooks'
import { useTheme } from '../hooks'
interface Props {
value: number

2
src/components/search.tsx → src/app/components/search.tsx

@ -1,5 +1,5 @@
import React, { FC } from 'react'
import { useTheme } from 'src/hooks'
import { useTheme } from '../hooks'
const App: FC = () => {
const theme = useTheme()

2
src/components/section.tsx → src/app/components/section.tsx

@ -1,5 +1,5 @@
import React, { FC } from 'react'
import { useTheme } from 'src/hooks'
import { useTheme } from '../hooks'
const Section: FC = ({ children }) => {
const theme = useTheme()

15
src/components/self-info.tsx → src/app/components/self-info.tsx

@ -1,15 +1,13 @@
import React, { FC } from 'react'
import { useSelector } from 'react-redux'
import { Link } from 'react-router-dom'
import { getAuthenticatedUser } from 'src/selectors/authentication'
import { useConfig, useTheme } from 'src/hooks'
import { urlForBlob } from 'src/utils'
import { getAuthenticatedUser } from '../selectors/authentication'
import { useTheme } from '../hooks'
import HorizontalRule from 'src/components/horizontal-rule'
import HorizontalRule from '../components/horizontal-rule'
const SelfInfo: FC = () => {
const theme = useTheme()
const config = useConfig()
const user = useSelector(getAuthenticatedUser)
if (!user) {
@ -23,8 +21,7 @@ const SelfInfo: FC = () => {
}
const group = user.group
const imageUrl = user.imageUrl ? urlForBlob(config, user.imageUrl) : undefined
const groupImageUrl = group && group.iconImageUrl ? urlForBlob(config, group.iconImageUrl) : undefined
const groupImageUrl = group && group.iconImageUrl ? group.iconImageUrl : undefined
const name = () => {
if (user.name) {
@ -40,9 +37,9 @@ const SelfInfo: FC = () => {
return (
<div className="user-info" style={{ backgroundColor: theme.primary, color: theme.primaryAlternate }}>
{imageUrl &&
{user.imageUrl &&
<div className="image">
<img src={imageUrl} style={{ width: 32 }} />
<img src={user.imageUrl} style={{ width: 32 }} />
</div>
}
<div>

2
src/components/spinner.tsx → src/app/components/spinner.tsx

@ -1,7 +1,7 @@
import React, { FC } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
import { useTheme } from 'src/hooks'
import { useTheme } from '../hooks'
const Spinner: FC = () => {
const theme = useTheme()

2
src/components/subtitle.tsx → src/app/components/subtitle.tsx

@ -1,5 +1,5 @@
import React, { FC } from 'react'
import { useTheme } from 'src/hooks'
import { useTheme } from '../hooks'
const Subtitle: FC = ({ children }) => {
const theme = useTheme()

12
src/components/timeline.tsx → src/app/components/timeline.tsx

@ -2,14 +2,14 @@ import React, { FC, useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { useHistory } from 'react-router-dom'
import { handleApiError } from 'src/api/errors'
import { fetchTimeline } from 'src/actions/posts'
import { getTimeline } from 'src/selectors/posts'
import { getAuthenticated } from 'src/selectors/authentication'
import { handleApiError } from '../api/errors'
import { fetchTimeline } from '../actions/posts'
import { getTimeline } from '../selectors/posts'
import { getAuthenticated } from '../selectors/authentication'
import { AppThunkDispatch } from 'src/types'
import { AppThunkDispatch } from '../types'
import PostList from 'src/components/post-list'
import PostList from '../components/post-list'
const Timeline: FC = () => {
const authenticated = useSelector(getAuthenticated)

2
src/components/title.tsx → src/app/components/title.tsx

@ -1,5 +1,5 @@
import React, { FC } from 'react'
import { useTheme } from 'src/hooks'
import { useTheme } from '../hooks'
const Title: FC = ({ children }) => {
const theme = useTheme()

2
src/components/user-apps.tsx → src/app/components/user-apps.tsx

@ -3,7 +3,7 @@ import { Link } from 'react-router-dom'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faPlusCircle } from '@fortawesome/free-solid-svg-icons'
import { setTitle } from 'src/utils'
import { setTitle } from '../utils'
const UserApps: FC = () => {
useEffect(() => {

13
src/components/user.tsx → src/app/components/user.tsx

@ -1,8 +1,7 @@
import React, { FC } from 'react'
import { Link } from 'react-router-dom'
import { useTheme, useConfig } from 'src/hooks'
import { urlForBlob } from 'src/utils'
import { User } from 'src/types'
import { useTheme } from '../hooks'
import { User } from '../types'
interface Props {
user: User
@ -10,15 +9,13 @@ interface Props {
const UserComponent: FC<Props> = ({ user }) => {
const theme = useTheme()
const config = useConfig()
const imageUrl = user && user.imageUrl ? urlForBlob(config, user.imageUrl) : undefined
const groupImageUrl = user.group && user.group.iconImageUrl ? urlForBlob(config, user.group.iconImageUrl) : undefined
const groupImageUrl = user?.group?.iconImageUrl
return (
<div className="user">
{imageUrl &&
{user.imageUrl &&
<div className="image">
<img src={imageUrl} style={{ width: 32 }} />
<img src={user.imageUrl} style={{ width: 32 }} />
</div>
}
<div>

17
src/app/constants/index.ts

@ -0,0 +1,17 @@
export const MAX_ID_LENGTH = 40
export const MAX_NAME_LENGTH = 80
export const LOCAL_STORAGE_ACCESS_TOKEN_KEY = 'access-token'
export const LOCAL_STORAGE_ACCESS_TOKEN_EXPIRES_AT_KEY = 'access-token-expires-at'
export const LOCAL_STORAGE_REFRESH_TOKEN_KEY = 'refresh-token'
export const MEDIA_DEFAULT_MAX_SIZE = 5242880
export const MEDIA_COVER_MAX_SIZE = 5242880
export const MEDIA_ICON_MAX_SIZE = 1048576
export const PRIVACY_OPTIONS = {
public: 'Anyone can see your posts',
group: 'Only the people in your community can see your posts',
subscribers: 'Only your subscribers can see your posts',
private: 'Nobody can see your posts',
}

7
src/hooks/index.ts → src/app/hooks/index.ts

@ -3,9 +3,8 @@ import { useSelector } from 'react-redux'
import { useHistory } from 'react-router-dom'
import isEqual from 'lodash/isEqual'
import { getAuthenticated, getChecked, getAuthenticatedUser } from 'src/selectors/authentication'
import { getTheme } from 'src/selectors/theme'
import { getConfig } from 'src/selectors'
import { getAuthenticated, getChecked, getAuthenticatedUser } from '../selectors/authentication'
import { getTheme } from '../selectors/theme'
export const useAuthenticationCheck = () => {
const checked = useSelector(getChecked)
@ -17,8 +16,6 @@ export const useAuthenticationCheck = () => {
}, [checked, authenticated])
}
export const useConfig = () => useSelector(getConfig)
const useDeepCompareMemoize = (value: any) => {
const ref = useRef()

0
src/images/caret-down.svg → src/app/images/caret-down.svg

BIN
src/app/images/favicon-16x16.png

After

Width: 16  |  Height: 16  |  Size: 357 B

BIN
src/app/images/favicon-32x32.png

After

Width: 32  |  Height: 32  |  Size: 455 B

BIN
src/app/images/favicon.ico

2
src/index.ejs → src/app/index.ejs

@ -4,9 +4,9 @@
<meta charset="utf-8">
<title>Flexor</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="/favicon-32x32.png">
</head>
<body>
<div id="app"></div>
</body>
<!-- Version: <% version %>-->
</html>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save