Dwayne Harris
5 years ago
20 changed files with 372 additions and 363 deletions
-
64src/actions/apps.ts
-
107src/actions/groups.ts
-
40src/actions/lists.ts
-
47src/actions/posts.ts
-
3src/components/pages/self.tsx
-
9src/components/pages/view-user.tsx
-
17src/components/post-list.tsx
-
40src/components/post.tsx
-
35src/components/user.tsx
-
50src/reducers/apps.ts
-
93src/reducers/groups.ts
-
42src/reducers/lists.ts
-
17src/selectors/apps.ts
-
2src/selectors/composer.ts
-
24src/selectors/groups.ts
-
15src/selectors/lists.ts
-
9src/selectors/posts.ts
-
6src/store/index.ts
-
33src/styles/app.scss
-
82src/types/store.ts
@ -0,0 +1,40 @@ |
|||
import { Action } from 'redux' |
|||
import { EntityListKey } from 'src/types' |
|||
|
|||
export interface ListAppendAction extends Action { |
|||
type: 'LISTS_APPEND' |
|||
payload: { |
|||
key: string |
|||
entities: string[] |
|||
continuation?: string |
|||
} |
|||
} |
|||
|
|||
export interface ListSetAction extends Action { |
|||
type: 'LISTS_SET' |
|||
payload: { |
|||
key: string |
|||
entities: string[] |
|||
continuation?: string |
|||
} |
|||
} |
|||
|
|||
export type ListsActions = ListAppendAction | ListSetAction |
|||
|
|||
export const listAppend = (key: string, entities: string[], continuation?: string): ListAppendAction => ({ |
|||
type: 'LISTS_APPEND', |
|||
payload: { |
|||
key, |
|||
entities, |
|||
continuation, |
|||
}, |
|||
}) |
|||
|
|||
export const listSet = (key: string, entities: string[], continuation?: string): ListSetAction => ({ |
|||
type: 'LISTS_SET', |
|||
payload: { |
|||
key, |
|||
entities, |
|||
continuation, |
|||
}, |
|||
}) |
@ -0,0 +1,17 @@ |
|||
import React, { FC } from 'react' |
|||
|
|||
import { Post } from 'src/types' |
|||
|
|||
import PostComponent from 'src/components/post' |
|||
|
|||
interface Props { |
|||
posts: Post[] |
|||
} |
|||
|
|||
const PostList: FC<Props> = ({ posts }) => ( |
|||
<div className="post-list"> |
|||
{posts.map(post => <PostComponent post={post} />)} |
|||
</div> |
|||
) |
|||
|
|||
export default PostList |
@ -0,0 +1,40 @@ |
|||
import React, { FC } from 'react' |
|||
import { Link } from 'react-router-dom' |
|||
import moment from 'moment' |
|||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' |
|||
import { faClock } from '@fortawesome/free-solid-svg-icons' |
|||
|
|||
import { Post } from 'src/types' |
|||
|
|||
import User from 'src/components/user' |
|||
|
|||
interface Props { |
|||
post: Post |
|||
} |
|||
|
|||
const PostComponent: FC<Props> = ({ post }) => ( |
|||
<div className="post"> |
|||
<p>{post.text}</p> |
|||
|
|||
<div className="post-info"> |
|||
<div> |
|||
<User user={post.user} /> |
|||
</div> |
|||
|
|||
<div> |
|||
Awards |
|||
</div> |
|||
|
|||
<div> |
|||
<span className="icon"> |
|||
<FontAwesomeIcon icon={faClock} /> |
|||
</span> |
|||
<Link to={`/p/${post.id}`} className="has-text-primary"> |
|||
{moment(post.created).format('MMMM Do, h:mm A')} |
|||
</Link> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
) |
|||
|
|||
export default PostComponent |
@ -0,0 +1,35 @@ |
|||
import React, { FC } from 'react' |
|||
import { useSelector } from 'react-redux' |
|||
import { Link } from 'react-router-dom' |
|||
|
|||
import { getConfig } from 'src/selectors' |
|||
import { urlForBlob } from 'src/utils' |
|||
import { AppState, User, Config } from 'src/types' |
|||
|
|||
interface Props { |
|||
user: User |
|||
} |
|||
|
|||
const UserComponent: FC<Props> = ({ user }) => { |
|||
const config = useSelector<AppState, Config>(getConfig) |
|||
const imageUrl = user && user.imageUrl ? urlForBlob(config, user.imageUrl) : undefined |
|||
|
|||
return ( |
|||
<div className="user"> |
|||
{imageUrl && |
|||
<div className="avatar"> |
|||
<img src={imageUrl} style={{ width: 32 }} /> |
|||
</div> |
|||
} |
|||
<div> |
|||
<Link to={`/u/${user.id}`}> |
|||
<span className="is-size-5">{user.name}</span> <span className="is-size-6 has-text-weight-bold">@{user.id}</span> |
|||
</Link> |
|||
<br /> |
|||
{user.group && <Link className="is-size-6 has-text-success" to={`/c/${user.group.id}`}>{user.group.name}</Link>} |
|||
</div> |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export default UserComponent |
@ -1,50 +0,0 @@ |
|||
import { Reducer } from 'redux' |
|||
|
|||
import { AppsActions } from '../actions/apps' |
|||
import { AppsState } from '../types' |
|||
|
|||
const initialState: AppsState = { |
|||
items: [], |
|||
continuation: undefined, |
|||
created: { |
|||
items: [], |
|||
continuation: undefined |
|||
} |
|||
} |
|||
|
|||
const reducer: Reducer<AppsState, AppsActions> = (state = initialState, action) => { |
|||
switch (action.type) { |
|||
case 'APPS_APPEND_APPS': |
|||
return { |
|||
...state, |
|||
items: action.payload.items, |
|||
continuation: action.payload.continuation, |
|||
} |
|||
case 'APPS_CLEAR_APPS': |
|||
return { |
|||
...state, |
|||
items: [], |
|||
continuation: undefined, |
|||
} |
|||
case 'APPS_APPEND_CREATED_APPS': |
|||
return { |
|||
...state, |
|||
created: { |
|||
items: action.payload.items, |
|||
continuation: action.payload.continuation, |
|||
}, |
|||
} |
|||
case 'APPS_CLEAR_CREATED_APPS': |
|||
return { |
|||
...state, |
|||
created: { |
|||
items: [], |
|||
continuation: undefined, |
|||
}, |
|||
} |
|||
default: |
|||
return state |
|||
} |
|||
} |
|||
|
|||
export default reducer |
@ -1,93 +0,0 @@ |
|||
import { Reducer } from 'redux' |
|||
|
|||
import { GroupsActions } from '../actions/groups' |
|||
import { GroupsState } from '../types' |
|||
|
|||
const initialState: GroupsState = { |
|||
items: [], |
|||
continuation: undefined, |
|||
admin: { |
|||
invitations: { |
|||
items: [], |
|||
continuation: undefined, |
|||
}, |
|||
logs: { |
|||
items: [], |
|||
continuation: undefined, |
|||
} |
|||
} |
|||
} |
|||
|
|||
const reducer: Reducer<GroupsState, GroupsActions> = (state = initialState, action) => { |
|||
switch (action.type) { |
|||
case 'GROUPS_APPEND_GROUPS': |
|||
return { |
|||
...state, |
|||
items: [ |
|||
...state.items, |
|||
...action.payload.items, |
|||
], |
|||
continuation: action.payload.continuation, |
|||
} |
|||
case 'GROUPS_CLEAR_GROUPS': |
|||
return { |
|||
...state, |
|||
items: [], |
|||
continuation: undefined, |
|||
} |
|||
case 'GROUPS_APPEND_LOGS': |
|||
return { |
|||
...state, |
|||
admin: { |
|||
...state.admin, |
|||
logs: { |
|||
items: [ |
|||
...state.admin.logs.items, |
|||
...action.payload.items, |
|||
], |
|||
continuation: action.payload.continuation, |
|||
}, |
|||
}, |
|||
} |
|||
case 'GROUPS_CLEAR_LOGS': |
|||
return { |
|||
...state, |
|||
admin: { |
|||
...state.admin, |
|||
logs: { |
|||
items: [], |
|||
continuation: undefined, |
|||
}, |
|||
}, |
|||
} |
|||
case 'GROUPS_APPEND_INVITATIONS': |
|||
return { |
|||
...state, |
|||
admin: { |
|||
...state.admin, |
|||
invitations: { |
|||
items: [ |
|||
...state.admin.logs.items, |
|||
...action.payload.items, |
|||
], |
|||
continuation: action.payload.continuation, |
|||
}, |
|||
}, |
|||
} |
|||
case 'GROUPS_CLEAR_INVITATIONS': |
|||
return { |
|||
...state, |
|||
admin: { |
|||
...state.admin, |
|||
invitations: { |
|||
items: [], |
|||
continuation: undefined, |
|||
}, |
|||
}, |
|||
} |
|||
default: |
|||
return state |
|||
} |
|||
} |
|||
|
|||
export default reducer |
@ -0,0 +1,42 @@ |
|||
import { Reducer } from 'redux' |
|||
|
|||
import { ListsActions } from '../actions/lists' |
|||
import { EntityListsState, EntityList } from '../types' |
|||
|
|||
const initialState: EntityListsState = {} |
|||
|
|||
const reducer: Reducer<EntityListsState, ListsActions> = (state = initialState, action) => { |
|||
switch (action.type) { |
|||
case 'LISTS_APPEND': |
|||
const list = state[action.payload.key] || { |
|||
entities: [], |
|||
continuation: undefined, |
|||
checked: 0, |
|||
} |
|||
|
|||
return { |
|||
...state, |
|||
[action.payload.key]: { |
|||
entities: [ |
|||
...list.entities, |
|||
...action.payload.entities, |
|||
], |
|||
continuation: action.payload.continuation, |
|||
checked: Date.now(), |
|||
}, |
|||
} |
|||
case 'LISTS_SET': |
|||
return { |
|||
...state, |
|||
[action.payload.key]: { |
|||
entities: action.payload.entities, |
|||
continuation: action.payload.continuation, |
|||
checked: Date.now(), |
|||
} |
|||
} |
|||
default: |
|||
return state |
|||
} |
|||
} |
|||
|
|||
export default reducer |
@ -1,5 +1,16 @@ |
|||
import { denormalize } from 'src/utils/normalization' |
|||
import { AppState, EntityType, App, Installation } from 'src/types' |
|||
import { AppState, EntityType, App, EntityListKey } from 'src/types' |
|||
|
|||
export const getApps = (state: AppState) => denormalize(state.apps.items, EntityType.App, state.entities) as App[] |
|||
export const getCreatedApps = (state: AppState) => denormalize(state.apps.created.items, EntityType.App, state.entities) as App[] |
|||
export const getApps = (state: AppState) => { |
|||
const entityList = state.lists[EntityListKey.Apps] |
|||
if (!entityList) return [] |
|||
|
|||
return denormalize(entityList.entities, EntityType.App, state.entities) as App[] |
|||
} |
|||
|
|||
export const getCreatedApps = (state: AppState) => { |
|||
const entityList = state.lists[EntityListKey.CreatedApps] |
|||
if (!entityList) return [] |
|||
|
|||
return denormalize(entityList.entities, EntityType.App, state.entities) as App[] |
|||
} |
@ -0,0 +1,15 @@ |
|||
import { AppState } from 'src/types' |
|||
|
|||
export const getEntities = (state: AppState, name: string) => { |
|||
const entityList = state.lists[name] |
|||
if (!entityList) return [] |
|||
|
|||
return entityList.entities |
|||
} |
|||
|
|||
export const getLastChecked = (state: AppState, name: string) => { |
|||
const entityList = state.lists[name] |
|||
if (!entityList) return 0 |
|||
|
|||
return entityList.checked |
|||
} |
@ -0,0 +1,9 @@ |
|||
import { denormalize } from 'src/utils/normalization' |
|||
import { AppState, Post, EntityType } from 'src/types' |
|||
|
|||
export const getUserPosts = (state: AppState, id: string) => { |
|||
const entityList = state.lists[`posts:${id}`] |
|||
if (!entityList) return [] |
|||
|
|||
return denormalize(entityList.entities, EntityType.Post, state.entities) as Post[] |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue