Skip to content

Commit

Permalink
Working on Share functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisBrownie55 committed Dec 31, 2020
1 parent ae4c340 commit 8091a36
Show file tree
Hide file tree
Showing 49 changed files with 848 additions and 527 deletions.
1 change: 0 additions & 1 deletion .eslintcache

This file was deleted.

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Generated by CRA (eslint internally)
.eslintcache
2 changes: 1 addition & 1 deletion .idea/codestream.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/dictionaries/Christopher.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 2 additions & 8 deletions .idea/private-art-hub-project.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions .idea/runConfigurations/format.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions .idea/runConfigurations/start.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 51 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,34 +51,71 @@ I am using the `framer-motion` package from npm for animation, transitions, and

## User Stories

- [ ] Create characters with:
- [x] Create characters with:
- [x] A name
- [x] A rich-text story (switching to https://github.com/basecamp/trix)
- [x] A rich-text story — uses trix-editor
- [x] Can upload artwork
- [ ] Each work of art can have an `alt`
- [ ] Edit characters:
- [ ] Name
- [ ] Rich-text story
- [ ] Remove old artwork
- [ ] Add new artwork
- [ ] Edit alts
- [ ] Each work of art can have an `alt` — Non-MVP
- [x] Edit characters:
- [x] Name
- [x] Rich-text story — trix-editor
- [x] Remove old artwork
- [x] Add new artwork
- [ ] Edit alts — Non-MVP
- [x] View character
- [x] Name
- [x] Rich-text story — not raw markdown or anything else
- [x] Rich-text story — trix-editor in readonly mode
- [x] Artwork
- [x] Delete character and all related artwork
- [ ] Sharing
- [ ] Share character via unique link
- [ ] Share character with time-sensitive link
- [ ] Un-share character link
- [ ] Share character via unique link <br>
`[{characterId: string, alias: string, shareId: string}]`
- [ ] Un-share character links
- [ ] View shared links
- [ ] Responsive UI
- [x] Mobile
- [ ] Tablet
- [ ] Desktop

## Firebase

My structure right now is:

- collection: users
- document: \<user-id\>
- array field: characters
- string: id
- Array\<string\>: files
- String: name
- String: story

I would like to convert over to sub-collections for more concise saving of an individual character in the `saveCharacterMachine` and, if I'm correct, larger storage size. This would potentially look like the following:

- collection: users
- document: \<user-id\>
- sub-collection: characters
- document: \<id\>
- Array\<string\>: files
- String: name
- String: story

However, this could potentially make obtaining a list of all the characters a bit more difficult.

Another option could be not creating a `users` collection but rather creating a collection for each user — not a document. It would still be a bit more difficult to obtain a list of all characters but this could potentially improve the DX even more.

- collection: \<user-id\>
- document: \<character-id\>
- Array\<string\>: files
- String: name
- String: story

## Testing

```bash
yarn test
```

This application currently has no tests as it is a gift, and I'm on a time crunch.
This application currently has no tests as it is a gift, and I'm on a time crunch. Alongside this, I have no knowledge of testing with Firebase's Firestore and Storage APIs.

## Formatting

Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@fluentui/react": "^7.121.4",
"@reach/auto-id": "^0.12.1",
"@reach/dialog": "^0.11.2",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
Expand All @@ -23,6 +24,7 @@
"react-router-dom": "5",
"react-scripts": "^4.0.1",
"react-transition-group": "^4.4.1",
"trix": "^1.3.1",
"uuid": "^8.2.0",
"wicg-inert": "^3.0.3",
"xss": "^1.0.8",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import {FontIcon, Text} from '@fluentui/react'
import {motion} from 'framer-motion'
import {colors} from '../shared/theme'
import '../styles/action-button.css'
import '../styles/ActionButton.css'

/*
* @param {{ variant: 'round' | 'flat' | 'bold-orange' | 'bold-pink' | 'danger', iconName: string }} options
Expand Down
File renamed without changes.
File renamed without changes.
50 changes: 27 additions & 23 deletions src/components/CharacterCard.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import {Suspense, useMemo} from 'react'
import {Link as RouterLink} from 'react-router-dom'

import {fetchImageURL, useUser} from '../shared/firebase.js'
import {motion} from 'framer-motion'
import {createResource} from '../shared/resources.js'
import {fetchImageURL, useUser} from '../shared/firebase.js'
import {Loading} from './Loading.js'

import '../styles/character-card.css'
import {Center} from './center'
import {Spinner} from '@fluentui/react'
import '../styles/CharacterCard.css'

/**
* @param {{resource: ResourceReader<string>, alt: string}} props
Expand All @@ -19,39 +18,44 @@ function CharacterCardArt({resource, alt}) {

/**
*
* @param {{character: Character}} props
* @returns {JSX.Element}
* @constructor
* @param {Character} character
* @returns {ResourceReader<string>}
*/
export function CharacterCard({character}) {
function useFirstImageResourceCreator(character) {
const {uid} = useUser()
const imageResource = useMemo(() => {
return useMemo(() => {
if (character.files.length > 0) return createResource(fetchImageURL(uid, character.files[0]))
return null
}, [character.files, uid])
}

/**
*
* @param {{character: Character, children: any}} props
* @returns {JSX.Element}
* @constructor
*/
export function CharacterCard({mode, character, children}) {
const imageResource = useFirstImageResourceCreator(character)

return (
<figure className="CharacterCard">
<figure className={`CharacterCard CharacterCard--${mode}`}>
{/*TODO: replace alt with alt from data*/}
{imageResource ? (
<Suspense
fallback={
<Center>
<Spinner />
</Center>
}
>
<CharacterCardArt resource={imageResource} alt={`Art of "${character.name}"`} />
<Suspense fallback={<Loading />}>
<motion.div layoutId={`character-art-${character.id}`}>
<CharacterCardArt resource={imageResource} alt="" />
</motion.div>
</Suspense>
) : (
<span className="CharacterCard__letter">{character.name[0]}</span>
)}

<figcaption className="CharacterCard__overlay">
<p className="CharacterCard__name">{character.name}</p>
<RouterLink to={`/character/${character.id}`} className="CharacterCard__view-button">
View Character
</RouterLink>
<p className="CharacterCard__name" layoutId={`card-title-${character.id}`}>
{character.name}
</p>
{children}
</figcaption>
</figure>
)
Expand Down
64 changes: 64 additions & 0 deletions src/components/CharacterCardList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {useCharacters} from '../shared/firebase'
import {CharacterCard} from './CharacterCard'
import {Link as RouterLink} from 'react-router-dom'
import {Text} from '@fluentui/react'
import {colors} from '../shared/theme'
import {useState} from 'react'
import {ShareDialog} from './ShareDialog'

function CharacterCardWrapper({character, mode}) {
const [status, setStatus] = useState('idle')

return (
<>
<CharacterCard key={character.id} character={character} mode={mode}>
{mode === 'view-characters' && (
<RouterLink to={`/character/${character.id}`} className="CharacterCard__action-button">
View Character
</RouterLink>
)}
{mode === 'share-characters' && (
<button className="CharacterCard__action-button" onClick={() => setStatus('sharing')}>
Share Character
</button>
)}
</CharacterCard>
<ShareDialog isOpen={status === 'sharing'} onDismiss={() => setStatus('idle')} character={character} />
</>
)
}

/**
* Renders a list of `<CharacterCard>`'s from a document and a resource.
* @param {{mode: 'view-characters' | 'share-characters'}} props
* @returns {JSX.Element|[JSX.Element]}
* @constructor
*/
export function CharacterCardList({mode}) {
const characters = useCharacters()

// Render all the Character Cards if there are any.
if (characters.length > 0) {
return characters.map(character => <CharacterCardWrapper character={character} mode={mode} />)
}

// Otherwise, inform the user of how to create a character.
// TODO: Add alt for pride-drawing.svg
return (
<div
style={{
width: '100%',
height: 'calc(100% - 100px)',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
}}
>
<img src="/pride-drawing.svg" alt="" style={{width: 270, height: 196, marginBottom: 35}} />
<Text variant="mediumTitle" as="h2" style={{textAlign: 'center', maxWidth: 232, color: colors.dark}}>
To get started, add some characters with the "New" button.
</Text>
</div>
)
}
29 changes: 29 additions & 0 deletions src/components/Dialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {createContext, useContext} from 'react'

import {Text} from '@fluentui/react'
import {DialogContent, DialogOverlay} from '@reach/dialog'
import {useId} from '@reach/auto-id'

import '../styles/Dialog.css'

const DialogContext = createContext({titleId: ''})

export function DialogTitle(props) {
const {titleId} = useContext(DialogContext)
return <Text id={titleId} variant="title" as="h1" className="Dialog__title" {...props} />
}

export function DialogParagraph(props) {
return <Text variant="medium" {...props} />
}

export function Dialog({isOpen, onDismiss, children, ...props}) {
const titleId = useId('title')
return (
<DialogOverlay isOpen={isOpen} onDismiss={onDismiss} className="Dialog__overlay" {...props}>
<DialogContent className="Dialog" aria-labelledby={titleId}>
<DialogContext.Provider value={{titleId}}>{children}</DialogContext.Provider>
</DialogContent>
</DialogOverlay>
)
}
File renamed without changes.
11 changes: 11 additions & 0 deletions src/components/Loading.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {Spinner} from '@fluentui/react'
import {Center} from './Center'
import {emptyObject} from '../shared/empty'

export function Loading({centerStyle = emptyObject, ...props}) {
return (
<Center style={centerStyle}>
<Spinner {...props} />
</Center>
)
}
File renamed without changes.
Loading

0 comments on commit 8091a36

Please sign in to comment.