From 98b4924694f8345a77b4297b3b1963c4de9a811c Mon Sep 17 00:00:00 2001 From: Jared Trouth Date: Mon, 4 Mar 2024 16:25:28 -0600 Subject: [PATCH 1/7] set default deployment enviornment to dev --- .github/workflows/build-and-push.yaml | 52 +++++++++++++-------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build-and-push.yaml b/.github/workflows/build-and-push.yaml index b5fe8082..a43e3bde 100644 --- a/.github/workflows/build-and-push.yaml +++ b/.github/workflows/build-and-push.yaml @@ -31,44 +31,20 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - - name: Docker meta - id: meta - uses: docker/metadata-action@v4 - with: - # list of Docker images to use as base name for tags - images: | - gcr.io/skyviewer/rubinobs-client,enable=${{ github.ref != 'master' && github.ref_type != 'tag' && github.base_ref == 'develop' }} - gcr.io/edc-int-6c5e/rubinobs-client,enable=${{ github.ref == 'master' || github.base_ref == 'master'}} - gcr.io/edc-prod-eef0/rubinobs-client,enable=${{ github.ref_type == 'tag'}} - flavor: | - latest=${{ github.event_name == 'push'}} - # generate Docker tags based on the following events/attributes - tags: | - type=schedule - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - type=sha - name: Parse deployment info id: deploy-info run: | # Determine environment to deploy to - if ${{ github.ref != 'master' && github.ref_type != 'tag' && github.base_ref == 'develop' }}; then - environment_name="dev" - credentials_json='${{ secrets.DEV_SA_KEY }}' - elif ${{ github.ref == 'master' || github.base_ref == 'master'}}; then + if ${{ github.ref == 'master' || github.base_ref == 'master'}}; then environment_name="int" credentials_json='${{ secrets.SKYVIEWER_INT_SERVICE_ACCOUNT }}' elif ${{ github.ref_type == 'tag'}}; then environment_name="prod" credentials_json='${{ secrets.PIPELINE_EPO_PROD_PROJECT }}' else - environment_name="" - credentials_json="" + environment_name="dev" + credentials_json='${{ secrets.DEV_SA_KEY }}' fi echo environment_name=$environment_name >> "$GITHUB_OUTPUT" echo credentials_json=$credentials_json >> "$GITHUB_OUTPUT" @@ -79,7 +55,27 @@ jobs: echo image_tag=$(echo "$full_tag" | cut -f2 -d:) >> "$GITHUB_OUTPUT" echo image_name=$(echo "$full_tag" | cut -f1 -d:) >> "$GITHUB_OUTPUT" echo full_tag=$full_tag >> "$GITHUB_OUTPUT" - + - + name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + # list of Docker images to use as base name for tags + images: | + gcr.io/skyviewer/rubinobs-client,enable=${{ steps.deploy-info.outputs.environment_name == 'dev' }} + gcr.io/edc-int-6c5e/rubinobs-client,enable=${{ steps.deploy-info.outputs.environment_name == 'int' }} + gcr.io/edc-prod-eef0/rubinobs-client,enable=${{ steps.deploy-info.outputs.environment_name == 'prod' }} + flavor: | + latest=${{ github.event_name == 'push'}} + # generate Docker tags based on the following events/attributes + tags: | + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha - name: Set up QEMU uses: docker/setup-qemu-action@v2 From 406c12349de98bf48add8e7868f33a92cb9c8a0c Mon Sep 17 00:00:00 2001 From: Jared Trouth Date: Mon, 4 Mar 2024 16:34:52 -0600 Subject: [PATCH 2/7] add build arg to skip local client build --- .github/workflows/build-and-push.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build-and-push.yaml b/.github/workflows/build-and-push.yaml index a43e3bde..5dc797fc 100644 --- a/.github/workflows/build-and-push.yaml +++ b/.github/workflows/build-and-push.yaml @@ -76,6 +76,8 @@ jobs: type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} type=sha + build-args: | + RUN_BUILD=false - name: Set up QEMU uses: docker/setup-qemu-action@v2 From 69638951041ff7629266f16b990f56813f19338f Mon Sep 17 00:00:00 2001 From: Blake Mason Date: Tue, 27 Feb 2024 15:05:10 -0800 Subject: [PATCH 3/7] [F] Jobs are open by default --- components/dynamic/JobList/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/dynamic/JobList/index.js b/components/dynamic/JobList/index.js index 1bda97d7..def7363a 100644 --- a/components/dynamic/JobList/index.js +++ b/components/dynamic/JobList/index.js @@ -59,7 +59,8 @@ const JobList = ({ ); // logic for open/closed - const checkOpen = checkIfBetweenDates(openDate, closeDate) + let checkOpen = "open"; + checkOpen = checkIfBetweenDates(openDate, closeDate) ? "open" : "closed"; const lock = checkOpen === "open" ? "LockOpen" : "LockClosed"; From 8c9ba24953a50c385c1fc4ea79919ff5428fb36c Mon Sep 17 00:00:00 2001 From: Blake Mason Date: Mon, 12 Feb 2024 12:00:55 -0800 Subject: [PATCH 4/7] [F] Event, News, & Related list items have optional featuredImage - Uses `featuredImage` first, `hero` if no `featuredImage`, placeholder image as fallback --- components/dynamic/EventList/index.js | 3 ++- components/dynamic/NewsList/index.js | 7 ++++++- components/dynamic/RelatedList/index.js | 24 +++++++++++++----------- lib/api/fragments/content-blocks.js | 16 +++++++++++++--- lib/api/fragments/event.js | 11 ++++++++--- lib/api/fragments/news-post.js | 11 ++++++++--- 6 files changed, 50 insertions(+), 22 deletions(-) diff --git a/components/dynamic/EventList/index.js b/components/dynamic/EventList/index.js index 6230ac49..62176cd7 100644 --- a/components/dynamic/EventList/index.js +++ b/components/dynamic/EventList/index.js @@ -42,6 +42,7 @@ const EventList = ({ endDate, description, id, + hero, image, eventType, registrationCloseDate, @@ -82,7 +83,7 @@ const EventList = ({ } : null } - image={image?.[0]} + image={image?.[0] || hero?.[0]} link={uri} pretitle={ gridType === "events" && eventType?.[0]?.title diff --git a/components/dynamic/NewsList/index.js b/components/dynamic/NewsList/index.js index e349968d..3d324c3c 100644 --- a/components/dynamic/NewsList/index.js +++ b/components/dynamic/NewsList/index.js @@ -51,6 +51,7 @@ const NewsList = ({ description, subtitle, id, + hero, image, images: releaseImages, newsAssets, @@ -70,7 +71,11 @@ const NewsList = ({ } : null } - image={image?.[0] || makeReleaseFeature(releaseImages)?.[0]} + image={ + image?.[0] || + makeReleaseFeature(releaseImages)?.[0] || + hero?.[0] + } isFeature={canShowFeatured && page === 1 && i === 0} link={uri} pretitle={ diff --git a/components/dynamic/RelatedList/index.js b/components/dynamic/RelatedList/index.js index 4de8c283..b76f95a3 100644 --- a/components/dynamic/RelatedList/index.js +++ b/components/dynamic/RelatedList/index.js @@ -30,17 +30,19 @@ const RelatedList = ({ <> {entries?.length > 0 && ( - {entries.map(({ id, description, image, title, uri }, i) => ( - - ))} + {entries.map( + ({ id, description, hero, image, title, uri }, i) => ( + + ) + )} )} diff --git a/lib/api/fragments/content-blocks.js b/lib/api/fragments/content-blocks.js index d3fa7216..2fe28d0a 100644 --- a/lib/api/fragments/content-blocks.js +++ b/lib/api/fragments/content-blocks.js @@ -342,6 +342,11 @@ export const relatedContentFragment = ` uri ... on pages_pages_Entry { description + hero { + ...on heroes_Asset { + ${getImageFields("crop", 900, 550)} + } + } image: featuredImage { ...on contentImages_Asset { ${getImageFields("crop", 900, 550)} @@ -350,11 +355,16 @@ export const relatedContentFragment = ` } ...on news_post_Entry { description: teaser - image: hero { + hero { ...on heroes_Asset { ${getImageFields("crop", 900, 550)} } } + image: featuredImage { + ...on contentImages_Asset { + ${getImageFields("crop", 900, 550)} + } + } } ...on investigations_investigation_Entry { uri @@ -711,8 +721,8 @@ export const relatedContentNewsFragment = ` ...on news_post_Entry { date description: teaser - image: hero { - ...on heroes_Asset { + image: featuredImage { + ...on contentImages_Asset { ${getImageFields("crop", 900, 550)} } } diff --git a/lib/api/fragments/event.js b/lib/api/fragments/event.js index 09c383ae..0d67b025 100644 --- a/lib/api/fragments/event.js +++ b/lib/api/fragments/event.js @@ -14,11 +14,16 @@ export const eventFragment = ` startDate endDate: date description - image: hero { + hero { ...on heroes_Asset { ${getImageFields("crop", 400, 400)} } } + image: featuredImage { + ...on contentImages_Asset { + ${getImageFields("crop", 400, 400)} + } + } eventType { id title @@ -48,8 +53,8 @@ export const eventFragmentFull = ` ${getImageFields("crop", 1920, 1067)} } } - featuredImage: hero { - ...on heroes_Asset { + featuredImage { + ...on contentImages_Asset { ${getImageFields("crop", 800, 800)} } } diff --git a/lib/api/fragments/news-post.js b/lib/api/fragments/news-post.js index c8039dc7..da14c4e0 100644 --- a/lib/api/fragments/news-post.js +++ b/lib/api/fragments/news-post.js @@ -11,11 +11,16 @@ export const newsPostFragment = ` dateCreated description: teaser pressReleaseId - image: hero { + hero { ...on heroes_Asset { ${getImageFields("crop", 900, 550)} } } + image: featuredImage { + ...on contentImages_Asset { + ${getImageFields("crop", 900, 550)} + } + } postType { id title @@ -58,8 +63,8 @@ export const newsPostFragmentFull = ` } } heroCaption: captionRichText - featuredImage: hero { - ...on heroes_Asset { + featuredImage { + ...on contentImages_Asset { ${getImageFields("crop", 800, 800)} } } From e1f38107f1f77975e760123b06c2f81f32ea33d9 Mon Sep 17 00:00:00 2001 From: Blake Mason Date: Mon, 12 Feb 2024 15:57:41 -0800 Subject: [PATCH 5/7] [F] NewsPost sidebar assets reorg - Hero image is auto-added to mediaAssets - Manual ImageAssets are added to mediaAssets (excluded from manual sections) - Fix issue where non-press-release type news posts were missing any/all assets --- components/templates/NewsPage/MediaAssets.js | 13 ++++--- components/templates/NewsPage/NewsAside.js | 28 +++++++------- components/templates/NewsPage/index.js | 39 +++++++++++++++++--- lib/api/fragments/news-post.js | 1 + 4 files changed, 57 insertions(+), 24 deletions(-) diff --git a/components/templates/NewsPage/MediaAssets.js b/components/templates/NewsPage/MediaAssets.js index 705bc454..aec1289c 100644 --- a/components/templates/NewsPage/MediaAssets.js +++ b/components/templates/NewsPage/MediaAssets.js @@ -1,7 +1,7 @@ import PropTypes from "prop-types"; import Link from "next/link"; import { useTranslation } from "react-i18next"; -import { ResponsiveImage } from "@rubin-epo/epo-react-lib"; +import { ResponsiveImage, Figure } from "@rubin-epo/epo-react-lib"; import ReleaseAssets from "./ReleaseAssets"; import * as Styled from "./styles"; @@ -15,11 +15,12 @@ export default function MediaAssets({ if (!contentBlockAssets && !releaseImages && !releaseVideos) return null; if ( - contentBlockAssets?.length <= 0 && - releaseImages?.length <= 0 && - releaseVideos?.length <= 0 + contentBlockAssets?.length === 0 && + releaseImages?.length === 0 && + releaseVideos?.length === 0 ) return null; + return (

{t(`media`)}

@@ -27,7 +28,9 @@ export default function MediaAssets({ if (asset.image?.[0].url) { return ( - +
+ +
); } diff --git a/components/templates/NewsPage/NewsAside.js b/components/templates/NewsPage/NewsAside.js index 096e79de..e325c47a 100644 --- a/components/templates/NewsPage/NewsAside.js +++ b/components/templates/NewsPage/NewsAside.js @@ -1,30 +1,32 @@ import PropTypes from "prop-types"; import Link from "next/link"; -import { ResponsiveImage, IconComposer } from "@rubin-epo/epo-react-lib"; +import { + ResponsiveImage, + IconComposer, + Figure, +} from "@rubin-epo/epo-react-lib"; import Tags from "./Tags"; import MediaAssets from "./MediaAssets"; import * as Styled from "./styles"; export default function NewsAside({ - newsAssets, + manualAssets, contentBlockAssets, releaseImages, releaseVideos, tags, rootHomeLink, }) { - // This sets up the automatic media grabber -- if there are no manual media set - let manualMedia = false; // This adds the document icon from the designs, if there is a text-style link near the start. - let manualDoc = newsAssets.some( + let manualDoc = manualAssets.some( (a, i) => i < 4 && (a.textLink?.length > 0 || a.externalLink?.length > 0) ); return ( - {newsAssets?.length > 0 && ( + {manualAssets?.length > 0 && ( - {newsAssets.map((a, i) => { + {manualAssets.map((a, i) => { if (a.assetHeader) { return (

@@ -53,14 +55,14 @@ export default function NewsAside({ ); } else if (a.image?.length > 0) { - manualMedia = true; return ( - - - + +
+ +
+ ); } else if (a.galleryItem?.length > 0) { - manualMedia = true; if (a.galleryItem[0].uri) { return ( block.typeHandle === "image" + const manualAssets = []; + const heroBlock = + hero?.length > 0 + ? { + id: id + "hero", + typeHandle: "image", + image: hero, + caption: heroCaption, + } + : null; + const mediaContentBlocks = [...contentBlocksNews].filter( + (block) => block.typeHandle === "image" || block.typeHandle === "video" ); + newsAssets.forEach((a, i) => { + if (a.image?.length > 0) { + // If there are manually added news assets combine them with the content block media assets + mediaContentBlocks.push({ + id: id + i, + typeHandle: "image", + image: a.image, + caption: a.caption, + }); + } else { + manualAssets.push(a); + } + }); + // If there is a hero then combine it with the content block media assets + if (heroBlock) mediaContentBlocks.unshift(heroBlock); + + // Only show the aside if there are news assets const showAside = - newsAssets?.length > 0 || - imageContentBlocks?.length > 0 || + manualAssets?.length > 0 || + mediaContentBlocks?.length > 0 || releaseImages?.length > 0 || releaseVideos?.length > 0 || postTags?.length > 0; @@ -77,8 +104,8 @@ export default function NewsPage({ data }) { )} {showAside && ( Date: Tue, 27 Feb 2024 14:39:00 -0800 Subject: [PATCH 6/7] [F] Google OAuth lib parity with investigations-client AS close to same as can be without migrating from /pages to /app --- .../atomic/Button/patterns/GoogleSSOButton.js | 52 +++++++ components/atomic/index.js | 1 + components/auth/AuthorizePage/index.js | 3 +- .../modal/RegisterModal/JoinForm/index.js | 8 +- components/modal/SignInModal/index.js | 8 +- hooks/useAuthentication.js | 22 +-- package.json | 3 +- pages/[[...uriSegments]].js | 10 +- pages/api/authGoogle.js | 26 ++++ yarn.lock | 129 +++++++++++++++--- 10 files changed, 208 insertions(+), 54 deletions(-) create mode 100644 components/atomic/Button/patterns/GoogleSSOButton.js create mode 100644 pages/api/authGoogle.js diff --git a/components/atomic/Button/patterns/GoogleSSOButton.js b/components/atomic/Button/patterns/GoogleSSOButton.js new file mode 100644 index 00000000..2cf2df02 --- /dev/null +++ b/components/atomic/Button/patterns/GoogleSSOButton.js @@ -0,0 +1,52 @@ +import PropTypes from "prop-types"; +import { useGoogleLogin } from "@react-oauth/google"; +import { useRouter } from "next/router"; +import { useAuthenticationContext } from "@/contexts/Authentication"; +import SSOButton from "./SSOButton"; + +export default function GoogleSSOButton({ children, ...buttonProps }) { + const { authenticateWithGoogle } = useAuthenticationContext(); + const { query, push, asPath, pathname } = useRouter(); + const goToGoogleSignIn = useGoogleLogin({ + state: asPath, + onSuccess: (response) => { + push( + { pathname: asPath.split("?")[0], query: { sso: true } }, + undefined, + { + shallow: true, + } + ); + fetch("/api/authGoogle", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ code: response.code }), + }) + .then((res) => res.json()) + .then((data) => { + authenticateWithGoogle(data); + }) + .catch(console.error); + }, + onError: (error) => { + console.error(error); + }, + flow: "auth-code", + }); + + return ( + + {children} + + ); +} + +GoogleSSOButton.propTypes = { + children: PropTypes.node, + service: PropTypes.oneOf(["google", "facebook", "email"]), +}; diff --git a/components/atomic/index.js b/components/atomic/index.js index fc309792..181f6fd7 100644 --- a/components/atomic/index.js +++ b/components/atomic/index.js @@ -1,4 +1,5 @@ export { default as SSOButton } from "./Button/patterns/SSOButton"; +export { default as GoogleSSOButton } from "./Button/patterns/GoogleSSOButton"; export { default as EarlyAccess } from "./Flag/patterns/EarlyAccess"; export { default as Tile } from "./Tile"; export { default as InvestigationTile } from "./Tile/patterns/InvestigationTile"; diff --git a/components/auth/AuthorizePage/index.js b/components/auth/AuthorizePage/index.js index cc0b3358..8c6295ba 100644 --- a/components/auth/AuthorizePage/index.js +++ b/components/auth/AuthorizePage/index.js @@ -15,7 +15,8 @@ const AUTHORIZED_TYPES = { }; function isAuthorized(typeHandle, user, status) { - if (typeHandle === "educatorPages") return user?.group === "educators"; + if (typeHandle === "educatorPages") + return user?.group === "educators" && status === "active"; if (typeHandle === "userProfilePage") return !!user && status === "active"; return false; } diff --git a/components/modal/RegisterModal/JoinForm/index.js b/components/modal/RegisterModal/JoinForm/index.js index 18600b1e..2c49a780 100644 --- a/components/modal/RegisterModal/JoinForm/index.js +++ b/components/modal/RegisterModal/JoinForm/index.js @@ -2,7 +2,7 @@ import PropTypes from "prop-types"; import { useTranslation } from "react-i18next"; import Link from "next/link"; import { Link as BaseLink } from "@rubin-epo/epo-react-lib"; -import { SSOButton } from "@/components/atomic"; +import { GoogleSSOButton, SSOButton } from "@/components/atomic"; import useAuthModal from "@/hooks/useAuthModal"; import { useAuthenticationContext } from "@/contexts/Authentication"; import AuthModal from "../../AuthModal"; @@ -14,7 +14,7 @@ export default function JoinForm({ onEmailSignup }) { const { pendingGroup, setPendingGroup, - goToGoogleSignIn, + // goToGoogleSignIn, goToFacebookSignIn, } = useAuthenticationContext(); @@ -47,9 +47,9 @@ export default function JoinForm({ onEmailSignup }) { */} - + {t("join.continue_with_google")} - + {/* */} - - {t("sign_in.continue_with_google")} - + + {t("join.continue_with_google")} + {/* {t("sign_in.continue_with_facebook")} */} diff --git a/hooks/useAuthentication.js b/hooks/useAuthentication.js index 29f4790c..665e9b29 100644 --- a/hooks/useAuthentication.js +++ b/hooks/useAuthentication.js @@ -1,6 +1,5 @@ import { useState, useEffect } from "react"; import { useRouter } from "next/router"; -import { useGoogleLogin } from "react-google-login"; import jwtDecode from "jwt-decode"; import { authenticate, @@ -17,7 +16,6 @@ import { requestDeletion, } from "@/lib/api/auth"; -const GOOGLE_APP_ID = process.env.NEXT_PUBLIC_GOOGLE_APP_ID; const SESSION_STORAGE_KEYS = [ "jwt", "jwtExpiresAt", @@ -83,24 +81,6 @@ export default function useAuthentication(data) { const [loading, setLoading] = useState(false); const [error, setError] = useState(false); - const { signIn: goToGoogleSignIn } = useGoogleLogin({ - clientId: GOOGLE_APP_ID, - onSuccess: (response) => { - const ssoModalUrl = { pathname: "/", query: { sso: true } }; - push(ssoModalUrl, undefined, { - shallow: true, - }); - // eslint-disable-next-line no-console - console.log("onSuccess", response, "onSuccess"); - authenticateWithGoogle({ idToken: response.tokenId }); - }, - onFailure: (error) => { - // eslint-disable-next-line no-console - console.log("onFailure", error, "onFailure"); - console.error(error); - }, - }); - useEffect(() => { // TODO: cancel promise if component unmounts first (async () => await maybeRefreshToken())(); @@ -442,8 +422,8 @@ export default function useAuthentication(data) { forgotPassword, setPassword, activateUser, - goToGoogleSignIn, goToFacebookSignIn, + authenticateWithGoogle, fetchUserData, requestAccountDeletion, }; diff --git a/package.json b/package.json index 9a686b8b..75f7166a 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@headlessui/react": "^1.6.6", "@influxdata/influxdb-client": "^1.33.2", "@popperjs/core": "^2.11.5", + "@react-oauth/google": "^0.12.1", "@rubin-epo/epo-react-lib": "^1.2.9", "add": "^2.0.6", "classnames": "^2.3.1", @@ -68,6 +69,7 @@ "feed": "^4.2.2", "focus-trap": "^7.0.0", "focus-visible": "^5.1.0", + "google-auth-library": "^9.6.3", "graphql": "^16.5.0", "graphql-request": "^5.0.0", "hoist-non-react-statics": "^3.3.2", @@ -79,7 +81,6 @@ "npm-run-all": "^4.1.5", "react": "18.2.0", "react-dom": "18.2.0", - "react-google-login": "^5.2.2", "react-hook-form": "^7.33.1", "react-i18next": "^12.0.0", "react-is": "^18.2.0", diff --git a/pages/[[...uriSegments]].js b/pages/[[...uriSegments]].js index e49badeb..a23ce0c6 100644 --- a/pages/[[...uriSegments]].js +++ b/pages/[[...uriSegments]].js @@ -1,4 +1,5 @@ import PropTypes from "prop-types"; +import { GoogleOAuthProvider } from "@react-oauth/google"; import { getGlobalData } from "@/api/global"; import { getAllEntries } from "@/api/entries"; import { getEntryDataByUri, getEntrySectionTypeByUri } from "@/api/entry"; @@ -57,6 +58,7 @@ function logNextDir() { }); } +const GOOGLE_APP_ID = process.env.NEXT_PUBLIC_GOOGLE_APP_ID; export default function Page({ section, globalData, ...entryProps }) { globalData.localeInfo.locale === "es" ? updateI18n("es") : updateI18n("en"); @@ -75,9 +77,11 @@ export default function Page({ section, globalData, ...entryProps }) { const Template = sectionMap[section] || PageTemplate; return ( - -