Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Url share #147

Merged
merged 39 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
9f00715
api share
dlopezalvas Nov 27, 2023
6660793
primer approach
dlopezalvas Nov 27, 2023
f51c2ff
cosas
dlopezalvas Nov 27, 2023
4c27595
Merge branch 'url-share' of github.com:Program-AR/pilas-bloques-react…
tfloxolodeiro Nov 28, 2023
03553bf
Boton compartir desafio
tfloxolodeiro Nov 28, 2023
cc6d9fc
Removing imports
tfloxolodeiro Nov 28, 2023
d08d81c
shareId
tfloxolodeiro Nov 28, 2023
0e7f709
savebutton
tfloxolodeiro Nov 28, 2023
9dc0063
shareId fix
tfloxolodeiro Nov 28, 2023
326b8b7
intelligence driven development
tfloxolodeiro Nov 28, 2023
c8d8200
arreglando cagadas de diana
dlopezalvas Nov 29, 2023
2ffe770
using shorter id
dlopezalvas Nov 29, 2023
703d669
Merge branch 'url-share' of github.com:Program-AR/pilas-bloques-react…
tfloxolodeiro Dec 1, 2023
06dc08d
cambiando ruta get
tfloxolodeiro Dec 1, 2023
81eb7bb
ChallengeUpsertButton
tfloxolodeiro Dec 1, 2023
c93dfaf
serverError
tfloxolodeiro Dec 1, 2023
e9d9ec7
intl fix
tfloxolodeiro Dec 1, 2023
2872621
tooltip
tfloxolodeiro Dec 1, 2023
49593c3
copy to clippboard (ugly)
dlopezalvas Dec 1, 2023
14824d0
merge url-share
dlopezalvas Dec 1, 2023
f00cc87
files refactor + minWidth modal
dlopezalvas Dec 1, 2023
b9df6f0
fixing input adorment
dlopezalvas Dec 1, 2023
c71387a
translations
dlopezalvas Dec 1, 2023
0affc09
saved correctly
dlopezalvas Dec 1, 2023
3267552
fixing url
dlopezalvas Dec 1, 2023
ad2ab27
Merge branch 'url-share' of github.com:Program-AR/pilas-bloques-react…
tfloxolodeiro Dec 5, 2023
ddaeaf3
changing user challenge resources
dlopezalvas Dec 5, 2023
194fa50
adding .env + share view mode
dlopezalvas Dec 5, 2023
3f52ff5
using pb app url .env
dlopezalvas Dec 5, 2023
0ce1ed2
share tests
tfloxolodeiro Dec 5, 2023
cb37b93
+ tests
tfloxolodeiro Dec 5, 2023
ec92fd8
Merge branch 'url-share' of github.com:Program-AR/pilas-bloques-react…
tfloxolodeiro Dec 5, 2023
a25b047
Merge remote-tracking branch 'origin/fixing-share-component' into url…
tfloxolodeiro Dec 5, 2023
4dad523
fix env test
tfloxolodeiro Dec 5, 2023
a915f09
sharedId
tfloxolodeiro Dec 6, 2023
651e3dc
Update src/components/creator/Editor/ActionButtons/ShareChallenge/Sha…
tfloxolodeiro Dec 11, 2023
d658df5
Update src/test/shareByUrl.test.tsx
tfloxolodeiro Dec 11, 2023
10d1229
ChallengeUpsertButton refactor
tfloxolodeiro Dec 11, 2023
56d65be
Merge branch 'url-share' of github.com:Program-AR/pilas-bloques-react…
tfloxolodeiro Dec 11, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
runs-on: ubuntu-latest
env:
REACT_APP_API_URL: ${{ secrets.API_URL }}
REACT_APP_PB_APP_URL: ${{ secrets.APP_URL }}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

La de arriba y esta pueden ser directamente

        REACT_APP_API_URL: "https://pilasbloques.program.ar/api"
        REACT_APP_PB_APP_URL: "https://pilasbloques.program.ar/online/#/" 

(no sé bien cómo deben ser los caracteres finales, pero puede ser así, no son secretas esas url)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Idem en los otros jobs)

REACT_APP_GOOGLE_ANALYTICS_KEY: ${{ secrets.GOOGLE_ANALYTICS_KEY }}
REACT_APP_VERSION: ${{github.ref_name}}
if: startsWith(github.ref, 'refs/tags')
Expand All @@ -55,6 +56,7 @@ jobs:
runs-on: macos-latest
env:
REACT_APP_API_URL: ${{ secrets.API_URL }}
REACT_APP_PB_APP_URL: ${{ secrets.APP_URL }}
REACT_APP_GOOGLE_ANALYTICS_KEY: ${{ secrets.GOOGLE_ANALYTICS_KEY }}
REACT_APP_VERSION: ${{github.ref_name}}
if: startsWith(github.ref, 'refs/tags')
Expand All @@ -77,6 +79,7 @@ jobs:
runs-on: windows-latest
env:
REACT_APP_API_URL: ${{ secrets.API_URL }}
REACT_APP_PB_APP_URL: ${{ secrets.APP_URL }}
REACT_APP_GOOGLE_ANALYTICS_KEY: ${{ secrets.GOOGLE_ANALYTICS_KEY }}
REACT_APP_VERSION: ${{github.ref_name}}
if: startsWith(github.ref, 'refs/tags')
Expand All @@ -99,6 +102,7 @@ jobs:
runs-on: ubuntu-latest
env:
REACT_APP_API_URL: ${{ secrets.API_URL }}
REACT_APP_PB_APP_URL: ${{ secrets.APP_URL }}
REACT_APP_GOOGLE_ANALYTICS_KEY: ${{ secrets.GOOGLE_ANALYTICS_KEY }}
REACT_APP_VERSION: ${{github.ref_name}}
if: github.event_name == 'push' && github.ref == 'refs/heads/develop'
Expand Down
15 changes: 13 additions & 2 deletions locales/en-us/creator.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,16 @@
"buttons": {
"discardChallenge": "Discard challenge",
"discardChallengeShort": "Discard",
"download": "Download",
"download": "Download challenge",
"downloadShort": "Download",
"share": "Share challenge",
"shareUrl": "Share via url",
"shareUrlShort": "Share",
"save": "Save challenge",
"saveShort": "Save",
"savedCorrectly": "Challenge was saved correctly",
"copyToClipboard": "Copy to clipboard",
"copiedToClipboard": "Copied to clipboard",
"preview": "Challenge preview",
"previewShort": "Preview",
"keepEditing": "Keep editing",
Expand All @@ -108,7 +117,9 @@

},
"editorHeader": "Edit challenge",
"previewModeHeader": "Challenge preview"
"previewModeHeader": "Challenge preview",
"serverError": "There's a problem with the server, try again later",
"loginWarning": "You need to be logged in to share a challenge via url."
},
"title": {
"title": "Write the challenge's title for ",
Expand Down
15 changes: 13 additions & 2 deletions locales/es-ar/creator.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,16 @@
"buttons": {
"discardChallenge": "Descartar desafío",
"discardChallengeShort": "Descartar",
"download": "Descargar",
"download": "Descargar desafío",
"downloadShort": "Descargar",
"share": "Compartir desafío",
"shareUrl": "Compartir por url",
"shareUrlShort": "Compartir",
"copyToClipboard": "Copiar al portapapeles",
"copiedToClipboard": "Copiado al portapapeles",
"save": "Guardar desafío",
"saveShort": "Guardar",
"savedCorrectly": "El desafío fue guardado correctamente",
"preview": "Ver desafío",
"previewShort": "Ver",
"keepEditing": "Seguir editando",
Expand All @@ -106,7 +115,9 @@
"loadChallengeShort": "Abrir"
},
"editorHeader": "Editar desafío",
"previewModeHeader": "Ver desafío"
"previewModeHeader": "Ver desafío",
"serverError": "Problema con el servidor, intente más tarde",
"loginWarning": "Para compartir un desafío por URL es necesario estar loggeado."
},
"title": {
"title": "Escribí el título del desafío para ",
Expand Down
1 change: 1 addition & 0 deletions sample.env
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
REACT_APP_API_URL=http://localhost:3001
REACT_APP_PB_APP_URL=http://localhost:3000
REACT_APP_VERSION=$npm_package_version
REACT_APP_GOOGLE_ANALYTICS_KEY=G-xxxxxx #Not necessary, can be omitted for development
16 changes: 14 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import { useLocation } from 'react-router-dom';
import ReactGA from "react-ga4";
import { CreatorViewMode } from './components/creator/Editor/CreatorViewMode';
import { useThemeContext } from './theme/ThemeContext';
import { SharedChallengeView } from './components/creator/SharedChallengeView';
import { PilasBloquesApi } from './pbApi';
import { Ember } from './emberCommunication';

const AnalyticsComponent = () => {
const location = useLocation();
Expand All @@ -38,6 +41,16 @@ const router = createHashRouter([{
element: <Home/>,
errorElement: <PBError />
},
{
path: "/desafio/guardado/:id",
element: <SharedChallengeView/>,
errorElement: <PBError />,
loader: async ({ params }) => {
const challenge = await PilasBloquesApi.getSharedChallenge(params.id!);
Ember.importChallenge(challenge)
return challenge
},
},
{
path: "/libros/:id",
element: <BookView/>,
Expand Down Expand Up @@ -103,5 +116,4 @@ function App() {
);
}

export default App;

export default App;
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { CreatorActionButton } from "../CreatorActionButton";
import DownloadIcon from '@mui/icons-material/Download';
import { useContext, useState } from "react";
import { Dialog, DialogContent, DialogTitle, InputAdornment, Stack, TextField } from "@mui/material";
import { CreatorContext } from "../../CreatorContext";
import { ShareButtons, CopyToClipboardButton } from "./ShareModalButtons";
import { useTranslation } from "react-i18next";

export const ShareButton = () => {

const [dialogOpen, setDialogOpen] = useState<boolean>(false)

return <>
<ShareDialog open={dialogOpen} setDialogOpen={setDialogOpen} />
<CreatorActionButton onClick={() => { setDialogOpen(true) }} startIcon={<DownloadIcon />} nametag='share' isshortversion={true} />
</>

}

const ShareDialog = ({ open, setDialogOpen }: { open: boolean, setDialogOpen: (open: boolean) => void }) => {

const { t } = useTranslation('creator');

return <>
<Dialog open={open} onClose={() => { setDialogOpen(false) }}>
<DialogTitle>{t('editor.buttons.share')}</DialogTitle>
<DialogContent >
<ShareModal />
</DialogContent>
</Dialog >
</>
}

export const ShareModal = () => {
const { sharedId } = useContext(CreatorContext)

const sharedLink = process.env.REACT_APP_PB_APP_URL + `/#/desafio/guardado/${sharedId}`

return <Stack>
{sharedId ?
<Stack direction='row'>
<TextField
sx={{ width: '100%', margin: 1}}
defaultValue={sharedLink}
InputProps={{
readOnly: true,
endAdornment: (
<InputAdornment position="end">
<CopyToClipboardButton textToCopy={sharedLink} />
</InputAdornment>
)
}}
/>
</Stack>
: <></>
}
<ShareButtons />
</Stack>
}




Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Estos componentes quedaron re prolijos, gente, felicitaciones ❤️ . Estoy contento con cómo quedó. Hice un comment abajo sobre un posible refactor y pequeño cambio de lógica.

Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import ShareIcon from '@mui/icons-material/Share';
import SaveIcon from '@mui/icons-material/Save';
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import { ReactNode, useContext, useState } from "react"
import { IconButtonTooltip } from "../../SceneEdition/IconButtonTooltip"
import { Snackbar, Stack, Tooltip } from "@mui/material"
import { CreatorContext } from '../../CreatorContext';
import { LocalStorage } from '../../../../../localStorage';
import { PilasBloquesApi } from '../../../../../pbApi';
import { CreatorActionButton } from '../CreatorActionButton';
import { DialogSnackbar } from '../../../../dialogSnackbar/DialogSnackbar';
import { useTranslation } from 'react-i18next';
import { SerializedChallenge } from '../../../../serializedChallenge';
import { DownloadButton } from '../DownloadButton';

export const CopyToClipboardButton = ({ textToCopy }: { textToCopy: string }) => {

const [openSnackbar, setOpenSnackbar] = useState(false)

const { t } = useTranslation('creator');

const handleClick = () => {
setOpenSnackbar(true)
navigator.clipboard.writeText(textToCopy)
}

return <>
<IconButtonTooltip icon={<ContentCopyIcon />} onClick={handleClick} tooltip={t('editor.buttons.copyToClipboard')} />
<Snackbar
open={openSnackbar}
onClose={() => setOpenSnackbar(false)}
autoHideDuration={2000}
message={t('editor.buttons.copiedToClipboard')}
/>
</>
}

export const ShareButtons = () => {
const { sharedId } = useContext(CreatorContext)

return <>
<Stack direction="row" justifyContent="space-between" alignItems='center'>
{sharedId ? <SaveButton /> : <ShareUrlButton />}
tfloxolodeiro marked this conversation as resolved.
Show resolved Hide resolved
<DownloadButton />
</Stack>
</>
}

const ShareUrlButton = () => {

const shareChallenge = async (): Promise<string> => {
const challenge: SerializedChallenge = LocalStorage.getCreatorChallenge()!
const sharedChallenge = await PilasBloquesApi.shareChallenge(challenge)
return sharedChallenge.sharedId
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Esta lógica está repetida en ambos ShareUrlButton y SaveButton. Debería estar abajo en ChallengeUpsertButton.
Y si me apurás, también quiero el Snackbar del SaveButton en el botón compartir, porque después de todo estás guardando, está bien que le usuarie sepa que estamos guardando en ese momento.

el parámetro challengeUpsert no debería ser necesario.

}

return <ChallengeUpsertButton Icon={<ShareIcon />} nametag="shareUrl" challengeUpsert={shareChallenge} />
}
const SaveButton = () => {
const [openSnackbar, setOpenSnackbar] = useState(false)

const { t } = useTranslation('creator');

const saveChallenge = async (): Promise<string> => {
const savedChallenge = await PilasBloquesApi.saveChallenge(LocalStorage.getCreatorChallenge()!)
setOpenSnackbar(true)
return savedChallenge.sharedId
}

return <>
<ChallengeUpsertButton Icon={<SaveIcon />} nametag="save" challengeUpsert={saveChallenge} />
<Snackbar
open={openSnackbar}
onClose={() => setOpenSnackbar(false)}
autoHideDuration={2000}
message={t('editor.buttons.savedCorrectly')}
/>
</>
}

export const ChallengeUpsertButton = ({ Icon, challengeUpsert, nametag }: { Icon: ReactNode, nametag: string, challengeUpsert: () => Promise<string> }) => {

const { setSharedId } = useContext(CreatorContext)
const userLoggedIn = !!LocalStorage.getUser()
const [serverError, setServerError] = useState<boolean>(false)
const { t } = useTranslation('creator');

const handleClick = async () => {
try {
setSharedId(await challengeUpsert())
}
catch (error) {
setServerError(true)
}
}

return <>
<Tooltip title={!userLoggedIn ? t('editor.loginWarning') : ''} followCursor>
<div>
<CreatorActionButton data-testid="upsertButton" onClick={handleClick} disabled={!userLoggedIn} startIcon={Icon} variant='contained' nametag={nametag} />
</div>
</Tooltip>
<DialogSnackbar open={serverError} onClose={() => setServerError(false)} message={t('editor.serverError')} />
</>
}
14 changes: 10 additions & 4 deletions src/components/creator/Editor/CreatorContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export type CreatorContextType = {
index: number;
setIndex: (index: number) => void;
setMaps: (maps: any) => void;
maps: SceneMap[]
maps: SceneMap[];
sharedId: string
setSharedId: (id: string) => void
};

const defaultCreatorContext = {
Expand All @@ -22,7 +24,9 @@ const defaultCreatorContext = {
index: 0,
setIndex: () => { },
setMaps: () => { },
maps: defaultChallenge('Duba').scene.maps
maps: defaultChallenge('Duba').scene.maps,
sharedId: "",
setSharedId: (id: string) => {}
}

export const CreatorContext = React.createContext<CreatorContextType>(defaultCreatorContext);
Expand All @@ -39,6 +43,7 @@ export const CreatorContextProvider: React.FC<CreatorProviderProps> = ({ childre
const challenge = LocalStorage.getCreatorChallenge() || defaultChallenge("Duba")
const [maps, setMaps] = useState(challenge.scene.maps)
const [index, setIndex] = useState(defaultIndex)
const [sharedId, setSharedId] = useState(challenge.sharedId || "")

const currentMap = maps[index] || challenge.scene.maps[index]

Expand All @@ -49,11 +54,12 @@ export const CreatorContextProvider: React.FC<CreatorProviderProps> = ({ childre

useEffect(() => {
challenge.scene.maps = maps
challenge.sharedId = sharedId
LocalStorage.saveCreatorChallenge(challenge)
}, [maps, challenge])
}, [maps, challenge, sharedId])

return (
<CreatorContext.Provider value={{ selectedTool, setSelectedTool, currentMap, setCurrentMap, index, setIndex, setMaps, maps}}>
<CreatorContext.Provider value={{ selectedTool, setSelectedTool, currentMap, setCurrentMap, sharedId: sharedId, setSharedId: setSharedId, index, setIndex, setMaps, maps}}>
{children}
</CreatorContext.Provider>
);
Expand Down
6 changes: 3 additions & 3 deletions src/components/creator/Editor/CreatorViewMode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,20 @@ export const CreatorViewMode = () => {
return (<>
{challengeExists ? (
<>
<Header CenterComponent={<CreatorViewHeader challenge={challengeBeingEdited} />} SubHeader={<EditorSubHeader viewButton={<ReturnToEditionButton />} />} />
<Header CenterComponent={<CreatorViewHeader title={challengeBeingEdited.title} />} SubHeader={<EditorSubHeader viewButton={<ReturnToEditionButton />} />} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No entendí por qué pasa esto ahora...

<EmberView height='calc(100% - var(--creator-subheader-height))' path={EMBER_IMPORTED_CHALLENGE_PATH} />
</>
) : <></>}
</>)
}

const CreatorViewHeader = ({ challenge }: { challenge: SerializedChallenge }) => {
export const CreatorViewHeader = ({ title }: { title: string }) => {
const { t } = useTranslation('creator')

return <BetaBadge smaller={true}>
<PBreadcrumbs>
<HeaderText text={t("editor.previewModeHeader")} />
<Typography>{challenge.title}</Typography>
<Typography>{title}</Typography>
</PBreadcrumbs>
</BetaBadge>

Expand Down
4 changes: 2 additions & 2 deletions src/components/creator/Editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import { SceneEdition } from "./SceneEdition/SceneEdition";
import { CreatorContextProvider } from "./CreatorContext";
import { CreatorSubHeader } from "./EditorSubHeader/CreatorSubHeader";
import { useTranslation } from "react-i18next";
import { DownloadButton } from "./ActionButtons/DownloadButton";
import { DiscardChallengeButton } from "./ActionButtons/DiscardChallengeButton";
import { PreviewButton } from "./ActionButtons/PreviewButton";
import { BetaBadge } from "../BetaBadge";
import { useThemeContext } from "../../../theme/ThemeContext";
import { useNavigate } from "react-router-dom";
import { LocalStorage } from "../../../localStorage";
import { useEffect } from "react";
import { ShareButton } from "./ActionButtons/ShareChallenge/ShareButton";

export const CreatorEditor = () => {
const { theme } = useThemeContext()
Expand Down Expand Up @@ -51,5 +51,5 @@ export const EditorSubHeader = (props: EditorSubHeaderProps) =>
<CreatorSubHeader>
<DiscardChallengeButton />
{props.viewButton}
<DownloadButton />
<ShareButton/>
</CreatorSubHeader>
Loading