Skip to content

Commit 21510f4

Browse files
author
Connell, Joseph
committed
Merge branch 'feature-otel' of https://github.com/placidic/lowcoder into feature-otel
2 parents 7e0e5ca + 154d214 commit 21510f4

File tree

9 files changed

+440
-44
lines changed

9 files changed

+440
-44
lines changed

client/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
"flag-icons": "^7.2.1",
8484
"number-precision": "^1.6.0",
8585
"react-countup": "^6.5.3",
86+
"react-github-btn": "^1.4.0",
8687
"react-player": "^2.11.0",
8788
"resize-observer-polyfill": "^1.5.1",
8889
"rollup": "^4.22.5",

client/packages/lowcoder-design/src/icons/index.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,6 @@ export { ReactComponent as BorderWidthIcon } from "./remix/space.svg";
221221
export { ReactComponent as BorderStyleIcon } from "./remix/separator.svg";
222222
export { ReactComponent as RotationIcon } from "./remix/clockwise-line.svg";
223223
export { ReactComponent as BorderRadiusIcon } from "./remix/rounded-corner.svg";
224-
225-
// Falk: TODO
226224
export { ReactComponent as ShadowIcon } from "./remix/shadow-line.svg";
227225
export { ReactComponent as OpacityIcon } from "./remix/contrast-drop-2-line.svg";
228226
export { ReactComponent as AnimationIcon } from "./remix/loader-line.svg";
@@ -257,6 +255,13 @@ export { ReactComponent as EnterpriseIcon } from "./remix/earth-line.svg";
257255
export { ReactComponent as VerticalIcon } from "./remix/vertical.svg";
258256
export { ReactComponent as HorizontalIcon } from "./remix/horizontal.svg";
259257

258+
// Social Sharing
259+
export { ReactComponent as TwitterIcon } from "./remix/twitter-x-line.svg";
260+
export { ReactComponent as LinkedInIcon } from "./remix/linkedin-box-fill.svg";
261+
export { ReactComponent as FacebookIcon } from "./remix/facebook-circle-fill.svg";
262+
export { ReactComponent as MediumIcon } from "./remix/medium-fill.svg";
263+
export { ReactComponent as RedditIcon } from "./remix/reddit-line.svg";
264+
260265

261266
// components
262267

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import Api from "api/api";
2+
import axios, { AxiosInstance, AxiosRequestConfig, CancelToken } from "axios";
3+
import { calculateFlowCode } from "./apiUtils";
4+
5+
export type ResponseType = {
6+
response: any;
7+
};
8+
9+
// Axios Configuration
10+
const lcHeaders = {
11+
"Lowcoder-Token": calculateFlowCode(),
12+
"Content-Type": "application/json"
13+
};
14+
15+
let axiosIns: AxiosInstance | null = null;
16+
17+
const getAxiosInstance = (clientSecret?: string) => {
18+
if (axiosIns && !clientSecret) {
19+
return axiosIns;
20+
}
21+
22+
const headers: Record<string, string> = {
23+
"Content-Type": "application/json",
24+
};
25+
26+
const apiRequestConfig: AxiosRequestConfig = {
27+
baseURL: "https://api-service.lowcoder.cloud/api/flow",
28+
headers,
29+
};
30+
31+
axiosIns = axios.create(apiRequestConfig);
32+
return axiosIns;
33+
};
34+
35+
class NewsApi extends Api {
36+
37+
static async secureRequest(body: any, timeout: number = 6000): Promise<any> {
38+
let response;
39+
const axiosInstance = getAxiosInstance();
40+
41+
// Create a cancel token and set timeout for cancellation
42+
const source = axios.CancelToken.source();
43+
const timeoutId = setTimeout(() => {
44+
source.cancel("Request timed out.");
45+
}, timeout);
46+
47+
// Request configuration with cancel token
48+
const requestConfig: AxiosRequestConfig = {
49+
method: "POST",
50+
withCredentials: true,
51+
data: body,
52+
cancelToken: source.token, // Add cancel token
53+
};
54+
55+
try {
56+
response = await axiosInstance.request(requestConfig);
57+
} catch (error) {
58+
if (axios.isCancel(error)) {
59+
// Retry once after timeout cancellation
60+
try {
61+
// Reset the cancel token and retry
62+
const retrySource = axios.CancelToken.source();
63+
const retryTimeoutId = setTimeout(() => {
64+
retrySource.cancel("Retry request timed out.");
65+
}, 10000);
66+
67+
response = await axiosInstance.request({
68+
...requestConfig,
69+
cancelToken: retrySource.token,
70+
});
71+
72+
clearTimeout(retryTimeoutId);
73+
} catch (retryError) {
74+
console.warn("Error at Secure Flow Request. Retry failed:", retryError);
75+
throw retryError;
76+
}
77+
} else {
78+
console.warn("Error at Secure Flow Request:", error);
79+
throw error;
80+
}
81+
} finally {
82+
clearTimeout(timeoutId); // Clear the initial timeout
83+
}
84+
85+
return response;
86+
}
87+
}
88+
89+
// API Functions
90+
91+
// secure/get-youtube-videos
92+
// secure/get-github-releases
93+
94+
export const getReleases = async () => {
95+
const apiBody = {
96+
path: "webhook/secure/get-github-releases",
97+
data: {},
98+
method: "post",
99+
headers: lcHeaders
100+
};
101+
try {
102+
const result = await NewsApi.secureRequest(apiBody);
103+
return result?.data[0]?.github?.length > 0 ? result.data[0].github as any[] : [];
104+
} catch (error) {
105+
console.error("Error getting news:", error);
106+
throw error;
107+
}
108+
};
109+
110+
export const getYoutubeVideos = async () => {
111+
const apiBody = {
112+
path: "webhook/secure/get-youtube-videos",
113+
data: {},
114+
method: "post",
115+
headers: lcHeaders
116+
};
117+
try {
118+
const result = await NewsApi.secureRequest(apiBody);
119+
return result?.data[0]?.youtube?.length > 0 ? result.data[0].youtube as any[] : [];
120+
} catch (error) {
121+
console.error("Error getting news:", error);
122+
throw error;
123+
}
124+
};
125+
126+
export const getHubspotContent = async () => {
127+
const apiBody = {
128+
path: "webhook/secure/get-hubspot-content",
129+
data: {},
130+
method: "post",
131+
headers: lcHeaders
132+
};
133+
try {
134+
const result = await NewsApi.secureRequest(apiBody);
135+
return result?.data[0]?.hubspot?.length > 0 ? result.data[0].hubspot as any[] : [];
136+
} catch (error) {
137+
console.error("Error getting news:", error);
138+
throw error;
139+
}
140+
};
141+
142+
export default NewsApi;

client/packages/lowcoder/src/components/PermissionDialog/AppPermissionDialog.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ import { TacoButton } from "components/button";
2727
import copy from "copy-to-clipboard";
2828
import { StyledLoading } from "./commonComponents";
2929
import { PermissionRole } from "./Permission";
30-
import { SHARE_TITLE } from "../../constants/apiConstants";
3130
import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
3231
import { default as Divider } from "antd/es/divider";
32+
import { SocialShareButtons } from "components/SocialShareButtons";
3333

3434
export const AppPermissionDialog = React.memo((props: {
3535
applicationId: string;
@@ -83,7 +83,7 @@ export const AppPermissionDialog = React.memo((props: {
8383
return (
8484
<PermissionDialog
8585
{...props}
86-
title={SHARE_TITLE}
86+
title={trans("home.appSharingDialogueTitle")}
8787
ownerLabel={trans("home.allPermissions")}
8888
viewBodyRender={(list) => {
8989
if (!appPermissionInfo) {
@@ -96,6 +96,7 @@ export const AppPermissionDialog = React.memo((props: {
9696
applicationId={applicationId}
9797
permissionInfo={appPermissionInfo!}
9898
/>
99+
<Divider/>
99100
{list}
100101
</>
101102
);
@@ -206,6 +207,8 @@ function AppShareView(props: {
206207
useEffect(() => {
207208
setPublicToMarketplace(permissionInfo.publicToMarketplace);
208209
}, [permissionInfo.publicToMarketplace]);
210+
const inviteLink = window.location.origin + APPLICATION_VIEW_URL(props.applicationId, "view");
211+
209212
return (
210213
<div style={{ marginBottom: "22px" }}>
211214

@@ -247,7 +250,19 @@ function AppShareView(props: {
247250
{trans("home.marketplaceGoodPublishing")}
248251
</div><Divider/></>}
249252

250-
{isPublic && <AppInviteView appId={applicationId} />}
253+
{isPublic && <AppInviteView appId={applicationId} />}
254+
255+
{isPublic &&
256+
<>
257+
<Divider />
258+
<SocialShareButtons
259+
url={inviteLink}
260+
text={trans("home.appSocialSharingMessage")}
261+
/>
262+
</>
263+
}
264+
265+
251266
</div>
252267
);
253268
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import React from "react";
2+
import styled from "styled-components";
3+
import { trans } from "../i18n";
4+
import {
5+
TwitterIcon,
6+
LinkedInIcon,
7+
FacebookIcon,
8+
MediumIcon,
9+
RedditIcon,
10+
} from "lowcoder-design";
11+
12+
const ShareWrapper = styled.div`
13+
margin-top: 0px;
14+
padding: 0px;
15+
`;
16+
17+
const ButtonGroup = styled.div`
18+
display: flex;
19+
gap: 12px;
20+
margin-top: 8px;
21+
22+
a {
23+
display: inline-flex;
24+
align-items: center;
25+
justify-content: center;
26+
width: 44px;
27+
height: 44px;
28+
border-radius: 4px;
29+
background-color: #f5f5f5;
30+
text-decoration: none;
31+
color: #333;
32+
33+
&:hover {
34+
background-color: #e6e6e6;
35+
}
36+
37+
svg {
38+
width: 20px;
39+
height: 20px;
40+
}
41+
}
42+
`;
43+
44+
export const SocialShareButtons: React.FC<{ url: string; text: string }> = ({
45+
url,
46+
text,
47+
}) => {
48+
const encodedUrl = encodeURIComponent(url);
49+
const encodedText = encodeURIComponent(text);
50+
51+
return (
52+
<ShareWrapper>
53+
<div style={{ fontWeight: 500, marginBottom: 4 }}>
54+
{trans("home.appSocialSharing")}
55+
</div>
56+
<ButtonGroup>
57+
{/* Twitter supports inline text and URL */}
58+
<a
59+
href={`https://twitter.com/intent/tweet?text=${encodedText}&url=${encodedUrl}`}
60+
target="_blank"
61+
title={trans("home.socialShare") + " Twitter"}
62+
rel="noopener noreferrer"
63+
>
64+
<TwitterIcon />
65+
</a>
66+
67+
{/* Facebook ONLY accepts the URL and reads OG metadata from it */}
68+
<a
69+
href={`https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`}
70+
target="_blank"
71+
title={trans("home.socialShare") + " Facebook"}
72+
rel="noopener noreferrer"
73+
>
74+
<FacebookIcon />
75+
</a>
76+
77+
{/* LinkedIn also only uses the URL; title/summary are ignored unless OG tags exist */}
78+
<a
79+
href={`https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`}
80+
target="_blank"
81+
title={trans("home.socialShare") + " LinkedIn"}
82+
rel="noopener noreferrer"
83+
>
84+
<LinkedInIcon />
85+
</a>
86+
87+
{/* Reddit sharing */}
88+
<a
89+
href={`https://www.reddit.com/submit?url=${encodedUrl}&title=${encodedText}`}
90+
target="_blank"
91+
title={trans("home.socialShare") + " Reddit"}
92+
rel="noopener noreferrer"
93+
>
94+
<RedditIcon />
95+
</a>
96+
97+
{/* Medium sharing - sharing the Medium article URL directly */}
98+
<a
99+
href={"https://medium.com/new-story"}
100+
target="_blank"
101+
title={trans("home.socialShare") + " Medium"}
102+
rel="noopener noreferrer"
103+
>
104+
<MediumIcon />
105+
</a>
106+
</ButtonGroup>
107+
</ShareWrapper>
108+
);
109+
};

client/packages/lowcoder/src/i18n/locales/en.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3383,6 +3383,10 @@ export const en = {
33833383
"fileFormatError": "File format error",
33843384
"groupWithSquareBrackets": "[Group] ",
33853385
"allPermissions": "Owner",
3386+
"appSharingDialogueTitle" : "App Sharing and Permissions",
3387+
"appSocialSharing" : "Share Your App and Experience on:",
3388+
"appSocialSharingMessage" : "I made this App with Lowcoder, check it out!",
3389+
"socialShare" : "Share on",
33863390
"shareLink": "Share link: ",
33873391
"copyLink": "Copy link",
33883392
"appPublicMessage": "Make the app public. Anyone can view.",

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy