From 24c0213a132e647818e7b5a225c089f3dc94600f Mon Sep 17 00:00:00 2001 From: Andrey Platov Date: Tue, 15 Oct 2024 08:21:20 +0200 Subject: [PATCH 1/3] config: add hcengineering/platform to slack alternative collection (#1805) Signed-off-by: Andrey Platov --- configs/collections/10061.slack-alternative.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/collections/10061.slack-alternative.yml b/configs/collections/10061.slack-alternative.yml index ce7e056617e..13960bf7339 100644 --- a/configs/collections/10061.slack-alternative.yml +++ b/configs/collections/10061.slack-alternative.yml @@ -12,3 +12,4 @@ items: - matrix-org/synapse - spacebarchat/spacebarchat - Linen-dev/linen.dev + - hcengineering/platform From f1a93c3e0b76b9f1b1500b56b3154f857f172b87 Mon Sep 17 00:00:00 2001 From: Andrey Platov Date: Tue, 15 Oct 2024 08:24:13 +0200 Subject: [PATCH 2/3] config: add project management collection (10096) (#1804) Signed-off-by: Andrey Platov --- configs/collections/10096.project-management.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 configs/collections/10096.project-management.yml diff --git a/configs/collections/10096.project-management.yml b/configs/collections/10096.project-management.yml new file mode 100644 index 00000000000..b553308edbf --- /dev/null +++ b/configs/collections/10096.project-management.yml @@ -0,0 +1,13 @@ +id: 10096 +name: Project Management +items: + - AppFlowy-IO/AppFlowy + - makeplane/plane + - mattermost-community/focalboard + - frappe/erpnext + - hcengineering/platform + - opf/openproject + - kanboard/kanboard + - kuaifan/dootask + - Leantime/leantime + - JordanKnott/taskcafe From 6e7aeaa88541ed6f139d4805b859c1bc72dc19e2 Mon Sep 17 00:00:00 2001 From: Jagger <634750802@qq.com> Date: Wed, 16 Oct 2024 19:09:59 +0800 Subject: [PATCH 3/3] feat: support tidb cloud oauth (#1806) * Update README.md * update * fix * Update web/src/pages/open-source-heroes/tidbcloud/oauth/callback/index.tsx * update * update --------- Co-authored-by: Mini256 --- README.md | 1 + .../_components/ClaimForm.tsx | 114 ++++++------------ .../_components/TiDBCloudButton.tsx | 71 ++++++++++- .../_sections/0-heading.tsx | 19 ++- .../_sections/2-introduction.tsx | 1 + .../tidbcloud/oauth/callback/index.tsx | 82 +++++++++++++ web/src/utils/ga.ts | 11 +- 7 files changed, 213 insertions(+), 86 deletions(-) create mode 100644 web/src/pages/open-source-heroes/tidbcloud/oauth/callback/index.tsx diff --git a/README.md b/README.md index 2c6d3cfd536..3cea56659f0 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ Insights about the **code update frequency & degree of popularity** from reposit **Examples**: + * [React](https://ossinsight.io/analyze/facebook/react) * [TiDB](https://ossinsight.io/analyze/pingcap/tidb) * [web3.js](https://ossinsight.io/analyze/web3/web3.js) diff --git a/web/src/pages/open-source-heroes/_components/ClaimForm.tsx b/web/src/pages/open-source-heroes/_components/ClaimForm.tsx index c4bdea794c4..c060ba9e166 100644 --- a/web/src/pages/open-source-heroes/_components/ClaimForm.tsx +++ b/web/src/pages/open-source-heroes/_components/ClaimForm.tsx @@ -13,6 +13,8 @@ type Check = { githubId: number; isClaimed: boolean; isEligible: boolean; + isLinkTiDBCloud: boolean; + claimedTenantId?: number; }; type Tenant = { @@ -23,6 +25,7 @@ type Tenant = { export function ClaimForm () { const [claimedThisSession, setClaimedThisSession] = useState(false); + const [shouldAutoClaim, setShouldAutoClaim] = useState(false); const { getAccessTokenSilently } = useResponsiveAuth0(); const { gtagEvent } = useGtag(); @@ -37,12 +40,12 @@ export function ClaimForm () { return res; })); - const { data: tenants } = useSWR('/api/v1/serverless-credits-campaign/tenants', async url => await giftClientWithoutCache.get(url, { + const { data: tenants, isValidating: isTenantValidating } = useSWR(check?.isLinkTiDBCloud && '/api/v1/serverless-credits-campaign/tenants', async url => await giftClientWithoutCache.get(url, { withCredentials: true, headers: { Authorization: `Bearer ${await getAccessTokenSilently({ connection: 'github' })}`, }, - })); + }), { errorRetryCount: 0 }); const handleClaim = async (id: string) => { gtagEvent('github_campaign_claim', {}); @@ -61,26 +64,26 @@ export function ClaimForm () { }); }; + useEffect(() => { + if (shouldAutoClaim && check && tenants && tenants.length > 0 && !check.isClaimed) { + void handleClaim(tenants[0].id); + } + }, [check, tenants, shouldAutoClaim]); + let children: ReactNode; - if (check && tenants) { + if (check && (tenants != null || !isTenantValidating)) { if (check.isClaimed) { if (claimedThisSession) { children = ; } else { children = ; } - } else if (check.isEligible) { - if (tenants.length === 0) { - children = ; - } else { - children = ; - } } else { - if (tenants.length === 0) { - children = ; + if (tenants) { + children = ; } else { - children = ; + children = setShouldAutoClaim(true)} />; } } @@ -95,10 +98,6 @@ export function ClaimForm () { //
// //
- // - //
- // - //
// // // ); @@ -141,7 +140,7 @@ function ClaimedThisSession ({ check }: { check: Check }) { - + Start Building with TiDB Cloud! @@ -164,7 +163,7 @@ function Claimed ({ check }: { check: Check }) { - + Start Building with TiDB Cloud! @@ -172,53 +171,7 @@ function Claimed ({ check }: { check: Check }) { ); } -function NotEligible ({ tenants }: { tenants: Tenant[] }) { - const { user } = useResponsiveAuth0(); - - return ( - <> - {badIcon} - - Hi {user?.nickname ?? user?.name} -
- Thanks for being an open-source hero! As a token of our appreciation, you have 25GB of free storage and 250 million reads available on TiDB Serverless. -
- Share this great news with your friends and start building something amazing. -
- - - - Login to TiDB Cloud - - - - ); -} - -function NotEligibleNoTenant () { - const { user } = useResponsiveAuth0(); - - return ( - <> - {badIcon} - - Hi {user?.nickname ?? user?.name} -
- Want to try TiDB Serverless for free? -
- Create a new TiDB Cloud account and enjoy 25GB of free storage to start building your applications. -
- - - - Create TiDB Cloud Account - - - - ); -} - -function EligibleNoTenants ({ check }: { check: Check }) { +function EligibleNoTenants ({ check, onShouldAutoClaim }: { check: Check, onShouldAutoClaim: () => void }) { const { user } = useResponsiveAuth0(); return ( @@ -227,15 +180,20 @@ function EligibleNoTenants ({ check }: { check: Check }) { Hi {user?.nickname ?? user?.name}
- Awesome! You're eligible for {check.credits} in TiDB Serverless credits for your contributions to the open-source community. + Awesome! You're eligible for {check.credits} in TiDB Serverless credits.
Create a new TiDB Cloud account and your credits will be waiting for you.
{ + onShouldAutoClaim(); + }} > - Sign up to TiDB Cloud + Connect to TiDB Cloud ; } @@ -333,16 +291,23 @@ function ShareButton ({ check }: { check?: Check }) { url = 'https://ossinsight.io/open-source-heroes/?utm_source=twitter&utm_medium=social&utm_campaign=plg_OSScontribution_credit_05'; } + const { gtagEvent } = useGtag(); + return ( ); @@ -393,13 +358,6 @@ const starIcon = ; -const badIcon = - - -; - const successIcon = diff --git a/web/src/pages/open-source-heroes/_components/TiDBCloudButton.tsx b/web/src/pages/open-source-heroes/_components/TiDBCloudButton.tsx index 574afa274da..e6321e29362 100644 --- a/web/src/pages/open-source-heroes/_components/TiDBCloudButton.tsx +++ b/web/src/pages/open-source-heroes/_components/TiDBCloudButton.tsx @@ -1,15 +1,62 @@ import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import { Button } from '@mui/material'; import type { ButtonProps } from '@mui/material/Button'; -import React, { type ReactNode } from 'react'; +import { useGtag } from '@site/src/utils/ga'; +import React, { type ReactNode, useState } from 'react'; const utm = '?utm_source=ossinsight&utm_medium=referral&utm_campaign=plg_OSScontribution_credit_05'; -export function TiDBCloudButton ({ children, trial = true, mt, ...props }: { children: ReactNode, mt?: number, trial?: boolean } & Pick, 'sx' | 'color' | 'variant'>) { +interface TiDBCloudButtonProps extends Pick, 'sx' | 'color' | 'variant' | 'onClick'> { + children: ReactNode; + mt?: number; + trial?: boolean; + link?: boolean; + org?: number; + pop?: boolean; +} + +const chars = 'abcdef1234567890'; + +export function TiDBCloudButton ({ + children, + trial = true, + link, + org, + mt, + pop = false, + onClick, + ...props +}: TiDBCloudButtonProps) { + const [state] = useState(() => Array(6).fill(0).map(_ => chars[Math.round(Math.random() * (chars.length - 1))]).join('')); + const { siteConfig: { customFields }, } = useDocusaurusContext(); + const { gtagEvent } = useGtag(); + + let href = `https://${customFields?.tidbcloud_host as string}`; + let tag: undefined | (() => any); + + if (org != null) { + href += `/console/clusters/?orgId=${org}`; + tag = () => gtagEvent('github_campaign_start_building', { trigger_by: 'cta-start' }); + } else if (link) { + href += '/oauth/authorize/'; + tag = () => gtagEvent('github_campaign_connect', { trigger_by: 'cta-connect' }); + } else if (trial) { + href += '/free-trial/'; + tag = () => gtagEvent('github_campaign_try', { trigger_by: 'cta-try' }); + } else { + href += '/'; + } + + href += utm; + + if (link) { + href += `&response_type=code&client_id=OSSInsight&scope=org:owner&state=${state}&redirect_uri=${encodeURIComponent(location.origin + '/open-source-heroes/tidbcloud/oauth/callback/')}`; + } + return ( diff --git a/web/src/pages/open-source-heroes/_sections/0-heading.tsx b/web/src/pages/open-source-heroes/_sections/0-heading.tsx index b1a27fa83ac..ddf263665bd 100644 --- a/web/src/pages/open-source-heroes/_sections/0-heading.tsx +++ b/web/src/pages/open-source-heroes/_sections/0-heading.tsx @@ -15,8 +15,14 @@ declare module 'react' { } } +declare global { + interface Window { + __trigger?: string | undefined; + } +} + export function HeadingSection () { - const { user, isLoading, login } = useResponsiveAuth0(); + const { user, isLoading, login, getIdTokenClaims } = useResponsiveAuth0(); const [claiming, setClaiming] = useState(false); const { gtagEvent } = useGtag(); @@ -26,12 +32,17 @@ export function HeadingSection () { return { - gtagEvent('github_campaign_action', {}); + const trigger = window.__trigger ?? 'cta-claim-top'; + window.__trigger = undefined; + gtagEvent('github_campaign_action', { trigger_by: trigger }); if (isGithubSub(user?.sub)) { setClaiming(true); } else { - void login({ connection: 'github' }).then(() => { - setClaiming(true); + void login({ connection: 'github' }).then(async () => { + const idc = await getIdTokenClaims(); + if (idc != null) { + setClaiming(true); + } }); } }} diff --git a/web/src/pages/open-source-heroes/_sections/2-introduction.tsx b/web/src/pages/open-source-heroes/_sections/2-introduction.tsx index eb26a0681d3..c0e11080390 100644 --- a/web/src/pages/open-source-heroes/_sections/2-introduction.tsx +++ b/web/src/pages/open-source-heroes/_sections/2-introduction.tsx @@ -68,6 +68,7 @@ export function IntroductionsSection () { variant="contained" onClick={() => { window.scrollTo({ top: 0, behavior: 'smooth' }); + window.__trigger = 'cta-claim-middle'; document.getElementById('start-claim-trigger')?.click(); }} > diff --git a/web/src/pages/open-source-heroes/tidbcloud/oauth/callback/index.tsx b/web/src/pages/open-source-heroes/tidbcloud/oauth/callback/index.tsx new file mode 100644 index 00000000000..a5715950f2a --- /dev/null +++ b/web/src/pages/open-source-heroes/tidbcloud/oauth/callback/index.tsx @@ -0,0 +1,82 @@ +import { AuthProvider } from '@site/src/context/user'; +import { useResponsiveAuth0 } from '@site/src/theme/NavbarItem/useResponsiveAuth0'; +import { useGtag } from '@site/src/utils/ga'; +import React, { useEffect, useState } from 'react'; + +export default function Page () { + return ( + + + + ); +} + +enum AuthorizeState { + AUTHORIZING, + AUTHORIZED, + FAILED, +} + +function Inner () { + const { getAccessTokenSilently } = useResponsiveAuth0(); + const [authorizeState, setAuthorizeState] = useState(AuthorizeState.AUTHORIZING); + const [error, setError] = useState<{ error: string, message: string }>(); + const { gtagEvent } = useGtag(); + + useEffect(() => { + const execute = async () => { + const token = await getAccessTokenSilently(); + + const usp = new URLSearchParams(window.location.search); + const code = usp.get('code'); + const state = usp.get('state'); + + const AUTH_URL = `${process.env.GIFT_APP_API_BASE as string}/api/v1/serverless-credits-campaign/oauth/tidbcloud/callback`; + + const response = await fetch(AUTH_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ + code, + state, + redirect_uri: location.origin + '/open-source-heroes/tidbcloud/oauth/callback/', + }), + }); + + if (response.ok) { + gtagEvent('connect_tidb_cloud_succeed', {}); + setAuthorizeState(AuthorizeState.AUTHORIZED); + setTimeout(() => { + window.close(); + }, 1500); + } else { + gtagEvent('connect_tidb_cloud_failed', {}); + setAuthorizeState(AuthorizeState.FAILED); + setError(await response.json()); + } + }; + + void execute(); + }, []); + + switch (authorizeState) { + case AuthorizeState.AUTHORIZING: + return
+

Authorizing

+
Please do not close this window.
+
; + case AuthorizeState.AUTHORIZED: + return
+

Authorized

+
This window will close in seconds. Go back to window.close()} style={{ textDecoration: 'underline' }}>OSSInsight.
+
; + case AuthorizeState.FAILED: + return
+

{error?.error ?? 'Authorization failed'}

+
{error?.message ?? 'Unknown error'}
+
; + } +} diff --git a/web/src/utils/ga.ts b/web/src/utils/ga.ts index 98d490a3865..bcf19606354 100644 --- a/web/src/utils/ga.ts +++ b/web/src/utils/ga.ts @@ -17,6 +17,8 @@ type GAConfiguredDimensions = { errorMessage: string; // trigger-login, login-failed, login-success trigger_login_by: string | undefined; + + trigger_by: string | undefined; }; type GAConfiguredOptions = GAConfiguredDimensions & GAConfiguredMetrics; @@ -56,8 +58,15 @@ type CustomEventMap = { login_success: GaEvent<'trigger_login_by'>; trigger_login: GaEvent<'trigger_login_by'>; - github_campaign_action: GaEvent; + github_campaign_action: GaEvent<'trigger_by'>; github_campaign_claim: GaEvent; + github_campaign_try: GaEvent<'trigger_by'>; + github_campaign_share: GaEvent<'trigger_by'>; + github_campaign_start_building: GaEvent<'trigger_by'>; + github_campaign_connect: GaEvent<'trigger_by'>; + + connect_tidb_cloud_succeed: GaEvent; + connect_tidb_cloud_failed: GaEvent; }; interface GtagEventApi {