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

File access dialog #640

Merged
merged 28 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c21d16e
add drop downs to the open dialog
OlegMoshkovich Mar 3, 2023
193370e
pulled selector into a separate component
OlegMoshkovich Mar 3, 2023
610957e
clean up
OlegMoshkovich Mar 14, 2023
5a4364c
add the tests
OlegMoshkovich Mar 15, 2023
9e0126a
style the open dialog
OlegMoshkovich Mar 15, 2023
2b5a28e
add test for files
OlegMoshkovich Mar 15, 2023
596a39a
Merge branch 'main' into fileSystem
OlegMoshkovich Mar 20, 2023
1b35c43
address comments
OlegMoshkovich Mar 20, 2023
c3277a1
Merge branch 'fileSystem' of https://github.com/OlegMoshkovich/Share …
OlegMoshkovich Mar 20, 2023
dd63271
clean up
OlegMoshkovich Mar 20, 2023
49a3bd7
updatee tests
OlegMoshkovich Mar 20, 2023
ed32f6e
clean up logs
OlegMoshkovich Mar 20, 2023
10ea66a
Merge branch 'main' into fileSystem
OlegMoshkovich Mar 21, 2023
aec021a
subtract ...
OlegMoshkovich Mar 21, 2023
aa4c8ec
Merge branch 'fileSystem' of https://github.com/OlegMoshkovich/Share …
OlegMoshkovich Mar 21, 2023
ba2cf52
test
OlegMoshkovich Mar 22, 2023
6a16633
and at symbol to the orgname
OlegMoshkovich Mar 22, 2023
2d4c7d1
add at symbol
OlegMoshkovich Mar 22, 2023
224339e
populate js doc description
OlegMoshkovich Mar 22, 2023
afeec13
simplify selection logic
OlegMoshkovich Mar 22, 2023
6662967
revert the change
OlegMoshkovich Mar 22, 2023
ce11d08
change properties to include name
OlegMoshkovich Mar 22, 2023
607092f
fix the navigate path
OlegMoshkovich Mar 22, 2023
1d323a9
clean up
OlegMoshkovich Mar 22, 2023
a50cf1e
simplify the navigate path
OlegMoshkovich Mar 22, 2023
548a81c
Clean github functions (#672)
ronmetavese Mar 23, 2023
5e5d5a6
few style chnages
OlegMoshkovich Mar 23, 2023
abddf05
fix conflict
OlegMoshkovich Mar 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
109 changes: 101 additions & 8 deletions src/Components/OpenModelControl.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import React, {useState} from 'react'
import React, {useState, useEffect} from 'react'
import {useNavigate} from 'react-router-dom'
import Box from '@mui/material/Box'
import MenuItem from '@mui/material/MenuItem'
import Paper from '@mui/material/Paper'
import Typography from '@mui/material/Typography'
import TextField from '@mui/material/TextField'
import useTheme from '@mui/styles/useTheme'
import {useAuth0} from '@auth0/auth0-react'
import useStore from '../store/useStore'
import Dialog from './Dialog'
import {TooltipIconButton} from './Buttons'
import Selector from './Selector'
import OpenIcon from '../assets/icons/Open.svg'
import UploadIcon from '../assets/icons/Upload.svg'
import {handleBeforeUnload} from '../utils/event'
import {getOrganizations, getRepositories, getFiles, getUserRepositories} from '../utils/GitHub'
import {RectangularButton} from '../Components/Buttons'


/**
Expand All @@ -20,7 +25,25 @@ import {handleBeforeUnload} from '../utils/event'
*/
export default function OpenModelControl({fileOpen}) {
const [isDialogDisplayed, setIsDialogDisplayed] = useState(false)
const [orgNamesArr, setOrgNamesArray] = useState(['..'])
const {user} = useAuth0()
const theme = useTheme()
const accessToken = useStore((state) => state.accessToken)
useEffect(() => {
/**
* Asynchronously fetch organizations
*
* @return {Array} organizations
*/
async function fetchOrganizations() {
const orgs = await getOrganizations(accessToken)
const orgNamesFetched = Object.keys(orgs).map((key) => orgs[key].login)
pablo-mayrgundter marked this conversation as resolved.
Show resolved Hide resolved
const orgNames = [...orgNamesFetched, user ? user.nickname : '']
setOrgNamesArray(orgNames)
return orgs
}
fetchOrganizations()
}, [accessToken, user])


return (
Expand All @@ -47,6 +70,7 @@ export default function OpenModelControl({fileOpen}) {
isDialogDisplayed={isDialogDisplayed}
setIsDialogDisplayed={setIsDialogDisplayed}
fileOpen={fileOpen}
orgNamesArr={orgNamesArr}
/>
}
</Box>
Expand All @@ -59,11 +83,50 @@ export default function OpenModelControl({fileOpen}) {
* @param {Function} setIsDialogDisplayed
* @return {object} React component
*/
function OpenModelDialog({isDialogDisplayed, setIsDialogDisplayed, fileOpen}) {
function OpenModelDialog({isDialogDisplayed, setIsDialogDisplayed, fileOpen, orgNamesArr}) {
const {isAuthenticated, user} = useAuth0()
// const isAuthenticated = true
const [selectedOrg, setSelectedOrg] = useState('')
const [selectedRepo, setSelectedRepo] = useState('')
const [selectedFile, setSelectedFile] = useState('')
const [repoNamesArr, setRepoNamesArr] = useState(['...'])
const [filesArr, setFilesArr] = useState(['...'])
const theme = useTheme()
const navigate = useNavigate()
const accessToken = useStore((state) => state.accessToken)


const openFile = () => {
fileOpen()
setIsDialogDisplayed(false)
}

const selectOrg = async (org) => {
setSelectedOrg(org)
pablo-mayrgundter marked this conversation as resolved.
Show resolved Hide resolved
let repos
if (orgNamesArr[org] === user.nickname) {
repos = await getUserRepositories(user.nickname, accessToken)
} else {
repos = await getRepositories(orgNamesArr[org], accessToken)
}
const repoNames = Object.keys(repos).map((key) => repos[key].name)
setRepoNamesArr(repoNames)
}

const selectRepo = async (repo) => {
setSelectedRepo(repo)
const owner = orgNamesArr[selectedOrg]
const files = await getFiles(repoNamesArr[repo], owner, accessToken)
const fileNames = Object.keys(files).map((key) => files[key].name)
setFilesArr(fileNames)
}

const navigateToFile = () => {
if (filesArr[selectedFile].includes('.ifc')) {
navigate({pathname: `/share/v/gh/${orgNamesArr[selectedOrg]}/${repoNamesArr[selectedRepo]}/main/${filesArr[selectedFile]}`})
}
}

return (
<Dialog
icon={<OpenIcon/>}
Expand All @@ -81,18 +144,46 @@ function OpenModelDialog({isDialogDisplayed, setIsDialogDisplayed, fileOpen}) {
textAlign: 'left',
}}
>
<ModelFileSelector setIsDialogDisplayed={setIsDialogDisplayed}/>
<p>Models hosted on GitHub are opened by inserting the link to the file into the Search.</p>
<SampleModelFileSelector setIsDialogDisplayed={setIsDialogDisplayed}/>
<p>Visit our {' '}
<a
target="_blank"
href='https://github.com/bldrs-ai/Share/wiki/Open-IFC-model-hosted-on-GitHub'
href='https://github.com/bldrs-ai/Share/wiki/GitHub-model-hosting'
rel="noreferrer"
>
wiki
</a> to learn more.
</a> to learn more about GitHub hosting.
</p>
<p>Models opened from local drive cannot yet be saved or shared.</p>
{isAuthenticated ?
<Box>
<Selector label={'Organization'} list={orgNamesArr} selected={selectedOrg} setSelected={selectOrg}/>
<Selector label={'Repository'} list={repoNamesArr} selected={selectedRepo} setSelected={selectRepo} testId={'Repository'}/>
<Selector label={'File'} list={filesArr} selected={selectedFile} setSelected={setSelectedFile} testId={'File'}/>
{selectedFile !== '' &&
<Box sx={{textAlign: 'center', marginTop: '4px'}}>
<RectangularButton title={'Load file'} icon={<UploadIcon/>} onClick={navigateToFile}/>
</Box>
}
</Box> :
<Typography
variant={'h4'}
sx={{
backgroundColor: theme.palette.scene.background,
borderRadius: '5px',
padding: '12px',
}}
>
Please login to get access to your files on GitHub
</Typography>
}
<Box
sx={{
marginTop: '1em',
fontSize: '.8em',
}}
>
* Local files cannot yet be saved or shared.
</Box>
</Box>
}
/>
Expand All @@ -104,7 +195,7 @@ function OpenModelDialog({isDialogDisplayed, setIsDialogDisplayed, fileOpen}) {
* @property {Function} setIsDialogDisplayed callback
* @return {React.ReactElement}
*/
function ModelFileSelector({setIsDialogDisplayed}) {
function SampleModelFileSelector({setIsDialogDisplayed}) {
const navigate = useNavigate()
const [selected, setSelected] = useState('')
const theme = useTheme()
Expand All @@ -118,6 +209,7 @@ function ModelFileSelector({setIsDialogDisplayed}) {
4: '/share/v/gh/Swiss-Property-AG/Seestrasse-Public/main/SEESTRASSE.ifc#c:119.61,50.37,73.68,16.18,11.25,5.74',
// eslint-disable-next-line max-len
5: '/share/v/gh/sujal23ks/BCF/main/packages/fileimport-service/ifc/ifcs/171210AISC_Sculpture_brep.ifc/120010/120020/120023/4998/2867#c:-163.46,16.12,223.99,12.03,-28.04,-15.28',
6: '/share/v/gh/OlegMoshkovich/Logo/main/IFC_STUDY.ifc',
}
window.removeEventListener('beforeunload', handleBeforeUnload)
navigate({pathname: modelPath[e.target.value]})
Expand Down Expand Up @@ -169,6 +261,7 @@ function ModelFileSelector({setIsDialogDisplayed}) {
<MenuItem value={4}><Typography variant='p'>Seestrasse</Typography></MenuItem>
<MenuItem value={0}><Typography variant='p'>Schependomlaan</Typography></MenuItem>
<MenuItem value={5}><Typography variant='p'>Structural Detail</Typography></MenuItem>
<MenuItem value={6}><Typography variant='p'>Bldrs plaza</Typography></MenuItem>
</TextField>
)
}
30 changes: 30 additions & 0 deletions src/Components/OpenModelControl.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react'
import {render, fireEvent} from '@testing-library/react'
import {
mockedUseAuth0,
mockedUserLoggedIn,
mockedUserLoggedOut} from '../__mocks__/authentication'
import OpenModelControl from './OpenModelControl'
import ShareMock from '../ShareMock'


describe('Open Model Dialog', () => {
it('Renders a login message if the user is not logged in', () => {
mockedUseAuth0.mockReturnValue(mockedUserLoggedOut)
const {getByTitle, getByText} = render(<ShareMock><OpenModelControl/></ShareMock>)
const button = getByTitle('Open IFC')
fireEvent.click(button)
const loginText = getByText('Please login to get access to your files on GitHub')
expect(loginText).toBeInTheDocument()
})
it('Renders file selector if the user is logged in', async () => {
mockedUseAuth0.mockReturnValue(mockedUserLoggedIn)
const {getByTitle, getByTestId} = render(<ShareMock><OpenModelControl/></ShareMock>)
const button = getByTitle('Open IFC')
fireEvent.click(button)
const File = getByTestId('File')
const Repository = await getByTestId('Repository')
expect(File).toBeInTheDocument()
expect(Repository).toBeInTheDocument()
})
})
75 changes: 75 additions & 0 deletions src/Components/Selector.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React from 'react'
import MenuItem from '@mui/material/MenuItem'
import Typography from '@mui/material/Typography'
import TextField from '@mui/material/TextField'
import useTheme from '@mui/styles/useTheme'
import {handleBeforeUnload} from '../utils/event'


/**
* @property {Function} setIsDialogDisplayed callback
pablo-mayrgundter marked this conversation as resolved.
Show resolved Hide resolved
* @return {React.ReactElement}
*/
export default function Selector({setIsDialogDisplayed, label, selected, setSelected, list, testId = 'Selector'}) {
const theme = useTheme()
const handleSelect = (e) => {
window.removeEventListener('beforeunload', handleBeforeUnload)
setSelected(e.target.value)
}


return (
<TextField
sx={selectorStyles(theme)}
value={selected}
onChange={(e) => handleSelect(e)}
variant='outlined'
label={label}
select
size='small'
data-testid={testId}
>
{list.map((listMember, i) => {
return (
<MenuItem key={i} value={i}><Typography variant='p'>{listMember}</Typography></MenuItem>
)
})}
</TextField>
)
}

const selectorStyles = (theme) => {
return (
{
'width': '260px',
'padding': '0px 0px 12px 0px',
'& .MuiOutlinedInput-input': {
color: theme.palette.secondary.main,
},
'& .MuiInputLabel-root': {
color: theme.palette.secondary.main,
},
'& .MuiOutlinedInput-root .MuiOutlinedInput-notchedOutline': {
borderColor: theme.palette.secondary.main,
},
'&:hover .MuiOutlinedInput-input': {
color: theme.palette.secondary.main,
},
'&:hover .MuiInputLabel-root': {
color: theme.palette.secondary.main,
},
'&:hover .MuiOutlinedInput-root .MuiOutlinedInput-notchedOutline': {
borderColor: theme.palette.secondary.main,
},
'& .MuiOutlinedInput-root.Mui-focused .MuiOutlinedInput-input': {
color: theme.palette.secondary.main,
},
'& .MuiInputLabel-root.Mui-focused': {
color: theme.palette.secondary.main,
},
'& .MuiOutlinedInput-root.Mui-focused .MuiOutlinedInput-notchedOutline': {
borderColor: theme.palette.secondary.main,
},
}
)
}
36 changes: 34 additions & 2 deletions src/__mocks__/api-handlers.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import {rest} from 'msw'
import {MOCK_COMMENTS, MOCK_ISSUES} from '../utils/GitHub'
import {
MOCK_COMMENTS,
MOCK_ISSUES,
MOCK_ORGANIZATION,
MOCK_REPOSITORY,
MOCK_FILES,
} from '../utils/GitHub'


const httpOk = 200
Expand Down Expand Up @@ -124,7 +130,6 @@ export const handlers = [
rest.patch('https://api.github.com/repos/:org/:repo/issues/:issueNumber', (req, res, ctx) => {
const {org, repo} = req.params
if (org !== 'pablo-mayrgundter' || repo !== 'Share' ) {
console.log('in the if')
return res(
ctx.status(httpNotFound),
ctx.json({
Expand All @@ -136,4 +141,31 @@ export const handlers = [
ctx.status(httpOk),
)
}),

OlegMoshkovich marked this conversation as resolved.
Show resolved Hide resolved
rest.get('https://api.github.com/user/orgs', (req, res, ctx) => {
return res(
ctx.status(httpOk),
ctx.json({
data: [MOCK_ORGANIZATION],
}),
)
}),

rest.get('https://api.github.com/orgs/bldrs-ai/repos', (req, res, ctx) => {
return res(
ctx.status(httpOk),
ctx.json({
data: [MOCK_REPOSITORY],
}),
)
}),

rest.get('https://api.github.com/repos/:owner/:repo/contents', (req, res, ctx) => {
return res(
ctx.status(httpOk),
ctx.json({
data: [MOCK_FILES],
}),
)
}),
]