Skip to content

Commit

Permalink
feat(js): add Download and QR Popup buttons to collapsed build blocks (
Browse files Browse the repository at this point in the history
…#404)

add square logo

allow click anywhere outside modal to close

safe lazy modal styling

Signed-off-by: Elizabeth Kelen <erskelen@gmail.com>
  • Loading branch information
ekelen authored Oct 13, 2020
1 parent 237b097 commit 1a5c21b
Show file tree
Hide file tree
Showing 9 changed files with 274 additions and 121 deletions.
12 changes: 12 additions & 0 deletions web/src/assets/svg/yolo-logo-square.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions web/src/ui/components/Build/ArtifactRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ const ArtifactRowKindIcon = ({ color, kind = '' }) => (
/>
)

const QrCode = ({ artifactPlistSignedUrl, closeAction }) => (
export const QrCode = ({ artifactPlistSignedUrl, closeAction }) => (
<QRCodeModal closeAction={closeAction}>
<QRCode
value={`itms-services://?action=download-manifest&url=${process.env.API_SERVER}${artifactPlistSignedUrl}`}
size={256}
// size={256}
level="M"
renderAs="svg"
includeMargin
style={{ width: '100%' }}
/>
</QRCodeModal>
)
Expand Down
3 changes: 1 addition & 2 deletions web/src/ui/components/Build/BuildBlockHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,7 @@ const ChevronIcon = ({ theme, collapsed, toggleCollapsed }) => (
<div
className={styles.headerChevronToggler}
style={{ color: theme.text.blockTitle }}
onClick={(e) => {
e.stopPropagation()
onClick={() => {
toggleCollapsed(!collapsed)
}}
>
Expand Down
246 changes: 179 additions & 67 deletions web/src/ui/components/Build/BuildContainer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { faQrcode } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { get } from 'lodash'
import React, { useContext, useState } from 'react'
import { Download } from 'react-feather'
import { ARTIFACT_KIND_NAMES, BRANCH, BUILD_STATE } from '../../../constants'
import { GlobalContext } from '../../../store/GlobalStore'
import { ThemeContext } from '../../../store/ThemeStore'
Expand All @@ -10,9 +12,13 @@ import {
getStrEquNormalized,
} from '../../../util/getters'
import { getArtifactKindIcon } from '../../styleTools/brandIcons'
import { tagColorStyles } from '../../styleTools/buttonStyler'
import {
primaryButtonColors,
tagColorStyles,
} from '../../styleTools/buttonStyler'
import ShowingOlderBuildsTag from '../ShowingOlderBuildsTag'
import Tag from '../Tag/Tag'
import { QrCode } from './ArtifactRow'
import styles from './Build.module.scss'
import BuildAndMrContainer from './BuildAndMrContainer'
import BuildBlockHeader from './BuildBlockHeader'
Expand Down Expand Up @@ -63,13 +69,114 @@ const AnyRunningBuildTags = ({ hasRunningBuilds, allBuildsForMr, theme }) => has
</Tag>
)

const LatestBuildQrButton = ({ onClick, theme, artifactPlistSignedUrl }) => artifactPlistSignedUrl && (
<div
onClick={(e) => {
onClick()
e.stopPropagation()
}}
className="btn btn-sm"
style={{
...primaryButtonColors(theme),
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
title="Show QR code"
>
<FontAwesomeIcon
icon={faQrcode}
size="lg"
color={theme.text.btnPrimary}
style={{
marginTop: 0,
marginBottom: 0,
}}
/>
</div>
)

const DlButtonSmall = ({ buildHasArtifacts, theme }) => buildHasArtifacts && (
<>
{buildHasArtifacts.map((artifact, i) => {
const {
plist_signed_url: artifactPlistSignedUrl = '',
dl_artifact_signed_url: artifactDlArtifactSignedUrl = '',
kind: artifactKind = '',
} = artifact

const fullPlistSignedUrl = artifactPlistSignedUrl
&& `itms-services://?action=download-manifest&url=${process.env.API_SERVER}${artifactPlistSignedUrl}`

const fullDlArtifactSignedUrl = artifactDlArtifactSignedUrl
&& `${process.env.API_SERVER}${artifactDlArtifactSignedUrl}`

const hasDlUrl = fullPlistSignedUrl || fullDlArtifactSignedUrl
return (
<a
key={`${artifactPlistSignedUrl}=${i}`}
href={hasDlUrl}
className="btn btn-sm"
style={{
...primaryButtonColors(theme),
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
title={hasDlUrl}
>
<Download size="1rem" style={{ marginRight: '0.3rem' }} />
{`.${ARTIFACT_KIND_NAMES[artifactKind]}`}
</a>
)
})}
</>
)

const LatestBuildStateTags = ({
collapsed,
theme,
buildState,
buildId,
buildHasArtifacts,
allBuildsForMr,
hasRunningBuilds,
}) => {
const [showingQrModal, toggleShowQrModal] = useState(false)
const firstArtifactPlistSignedUrl = buildHasArtifacts?.find((a) => a.kind === ARTIFACT_KIND_NAMES.IPA)
?.plist_signed_url || null
return (
collapsed && (
<>
{showingQrModal && firstArtifactPlistSignedUrl && (
<QrCode
artifactPlistSignedUrl={firstArtifactPlistSignedUrl}
closeAction={() => toggleShowQrModal(false)}
/>
)}
<BuildStateTag {...{ theme, buildState, buildId }} />
<LatestBuildArtifactsIcons {...{ theme, buildHasArtifacts }} />
<AnyRunningBuildTags {...{ hasRunningBuilds, allBuildsForMr, theme }} />
<DlButtonSmall {...{ buildHasArtifacts, theme }} />
<LatestBuildQrButton
onClick={() => toggleShowQrModal(true)}
theme={theme}
artifactPlistSignedUrl={firstArtifactPlistSignedUrl}
/>
<ShowingOlderBuildsTag nOlderBuilds={allBuildsForMr.length - 1} />
</>
)
)
}

const BuildContainer = React.memo(
({
build, toCollapse, hasRunningBuilds, isLatestMaster = false,
}) => {
const { state } = useContext(GlobalContext)
const [collapsed, setCollapsed] = useState(toCollapse)
const [showingAllBuilds, toggleShowingAllBuilds] = useState(false)

const { theme } = useContext(ThemeContext)

const toggleCollapsed = () => setCollapsed(!collapsed)
Expand Down Expand Up @@ -105,75 +212,80 @@ const BuildContainer = React.memo(

const isMasterBuildBranch = getStrEquNormalized(buildBranch, BRANCH.MASTER)

const LatestBuildStateTags = () => collapsed && (
<>
<BuildStateTag {...{ theme, buildState, buildId }} />
<LatestBuildArtifactsIcons {...{ theme, buildHasArtifacts }} />
<ShowingOlderBuildsTag nOlderBuilds={allBuildsForMr.length - 1} />
<AnyRunningBuildTags
{...{ hasRunningBuilds, allBuildsForMr, theme }}
/>
</>
)

return (
<div className={styles.buildBlock}>
<div
className="card"
style={{
backgroundColor: theme.bg.block,
boxShadow: theme.shadowStyle.block,
...tablerOverrides.card,
}}
key={buildId}
onClick={() => {
if (collapsed) {
toggleCollapsed()
}
}}
>
<BuildBlockHeader
{...{
buildAuthorName,
buildAuthorAvatarUrl,
buildAuthorId,
buildHasMr,
buildId,
buildShortId,
collapsed,
isMasterBuildBranch,
isLatestMaster,
mrId,
mrTitle,
mrShortId,
mrState,
toggleCollapsed,
projectOwnerId,
projectOwnerAvatarUrl,
...{ childrenLatestBuildTags: <LatestBuildStateTags /> },
<>
<div className={styles.buildBlock}>
<div
className="card"
style={{
backgroundColor: theme.bg.block,
boxShadow: theme.shadowStyle.block,
...tablerOverrides.card,
}}
/>
{!collapsed
&& allBuildsForMr
.filter((bIdx, i) => showingAllBuilds ? Number.isInteger(bIdx) : i === 0)
.map((buildidx, i) => (
<BuildAndMrContainer
build={state.builds[buildidx]}
buildHasMr={buildHasMr}
hasRunningBuilds={hasRunningBuilds}
isLatestBuild={i === 0}
key={i}
nOlderBuilds={
i === 0 && getIsArrayWithN(allBuildsForMr, 2)
? allBuildsForMr.length - 1
: 0
}
showingAllBuilds={showingAllBuilds}
toggleShowingAllBuilds={toggleShowingAllBuilds}
/>
))}
key={buildId}
onClick={() => {
if (collapsed) {
toggleCollapsed()
}
}}
>
<BuildBlockHeader
{...{
buildAuthorName,
buildAuthorAvatarUrl,
buildAuthorId,
buildHasMr,
buildId,
buildShortId,
collapsed,
isMasterBuildBranch,
isLatestMaster,
mrId,
mrTitle,
mrShortId,
mrState,
toggleCollapsed,
projectOwnerId,
projectOwnerAvatarUrl,
...{
childrenLatestBuildTags: (
<LatestBuildStateTags
{...{
collapsed,
theme,
buildState,
buildId,
buildHasArtifacts,
allBuildsForMr,
hasRunningBuilds,
}}
/>
),
},
}}
/>
{!collapsed
&& allBuildsForMr
.filter((bIdx, i) => showingAllBuilds ? Number.isInteger(bIdx) : i === 0)
.map((buildidx, i) => (
<BuildAndMrContainer
build={state.builds[buildidx]}
buildHasMr={buildHasMr}
hasRunningBuilds={hasRunningBuilds}
isLatestBuild={i === 0}
key={i}
nOlderBuilds={
i === 0 && getIsArrayWithN(allBuildsForMr, 2)
? allBuildsForMr.length - 1
: 0
}
showingAllBuilds={showingAllBuilds}
toggleShowingAllBuilds={toggleShowingAllBuilds}
/>
))}
</div>
</div>
</div>
</>
)
},
)
Expand Down
9 changes: 7 additions & 2 deletions web/src/ui/components/BuildList.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,18 @@ const BuildList = ({ builds = [], loaded }) => {

useEffect(() => {
// On show feed, scroll to feed with 'hide feed' button at top of screen
if (refOpen?.current && displayFeed === true && prevDisplay === false) {
if (
refOpen?.current
&& displayFeed === true
&& prevDisplay === false
&& builds.length
) {
refOpen.current.scrollIntoView({
behavior: 'smooth',
block: 'start',
})
}
}, [displayFeed, refOpen, prevDisplay])
}, [displayFeed, refOpen, prevDisplay, builds.length])

return !builds.length > 0 && loaded ? (
<NoBuilds />
Expand Down
29 changes: 22 additions & 7 deletions web/src/ui/components/MessageModal/MessageModal.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
import React from 'react'
import './MessageModal.scss'

const MessageModal = ({ children }) => (
const MessageModal = ({ children, onClose = () => {} }) => (
<>
<div className="faded" />
<div
className="faded"
onClick={(e) => {
e.stopPropagation()
onClose()
}}
/>
<div className="MessageModal">
<div className="modal modal-blur fade show">
<div className="modal-dialog" role="document">
<div className="modal-content">
{children}
</div>
<div
className="modal modal-blur fade show"
onClick={(e) => {
e.stopPropagation()
onClose()
}}
>
<div
className="modal-dialog"
onClick={(e) => {
e.stopPropagation()
}}
>
<div className="modal-content">{children}</div>
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit 1a5c21b

Please sign in to comment.