Skip to content

Commit

Permalink
Add kakao share button using SDK
Browse files Browse the repository at this point in the history
    - Kakao SDK Behavior:
      - SDK initialization (window.Kakao.init) is mandatory, making the createShareButton structure unsuitable.
      - SDK is dynamically loaded via a <script> tag, requiring direct DOM access.
    - Constraints:
      - SDK Initialization: Requires a JavaScript Key issued by the Kakao Developer Console.
      - URI Encoding: Kakao SDK performs internal encoding, so additional encoding is unnecessary.
      - Domain Registration: Only URLs from domains registered in My Application > App Settings > Platform are allowed. Unregistered URLs result in a blank page.
      - Image URL: Must use an external URL accessible by Kakao servers.
    - Implementation:
      - Utilized the sendDefault API to compose messages with objectType: 'feed':
        - webUrl and mobileWebUrl: Links must belong to registered domains.
        - buttonTitle: Sets the button text displayed within the KakaoTalk message.
        - imageUrl: Requires an externally accessible URL for images.
      - Sharing counts are not provided via API but can be viewed in the developer console's statistics.
      - SDK load or initialization errors are logged using console.error without disrupting app execution.
    - Additional Setup For Demo:
      - Add vite-env.d.ts in the demo folder to define the VITE_KAKAO_JS_KEY type.
      - Add .env file with VITE_KAKAO_JS_KEY to provide the required JavaScript Key.
Signed-off-by: zmrdltl <meenseek5929@naver.com>
  • Loading branch information
zmrdltl committed Jan 1, 2025
1 parent d8cc1ca commit 664c071
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ npm install react-share
- Hatena
- Gab
- email
- KakaoTalk
- share counts for
- Facebook
- Pinterest
Expand Down Expand Up @@ -96,6 +97,7 @@ import {
VKShareButton,
WhatsappShareButton,
WorkplaceShareButton,
KakaoShareButton,
} from "react-share";
```

Expand Down Expand Up @@ -125,6 +127,7 @@ import {
| WeiboShareButton | - | **`title`** (string): Title of the shared page<br/>**`image`** (string): An absolute link to the image that will be shared |
| WhatsappShareButton | - | **`title`** (string): Title of the shared page<br/>**`separator`** (string, default=`" "`): Separates title from the url |
| WorkplaceShareButton | - | **`quote`** (string): A quote to be shared along with the link.<br/>**`hashtag`** (string): A hashtag specified by the developer to be added to the shared content. People will still have the opportunity to remove this hashtag in the dialog. The hashtag should include the hash symbol. |
| KakaoShareButton | **`kakaoJsKey`** (string): Kakao JavaScript Key<br/>**`webUrl`** (string): URL of the shared page (must be a domain registered in the [Kakao Developer Console](https://developers.kakao.com/))<br/>**`title`** (string): Title of the shared page<br/> | **`mobileWebUrl`** (string): Mobile-specific URL (must also be a registered domain in the Kakao Developer Console)<br/>**`description`** (string): Description of the shared page<br/>**`imageUrl`** (string): External URL of the image to display<br/>**`buttonTitle`** (string): Text for the button inside KakaoTalk.<br/> |

### Share counts

Expand Down Expand Up @@ -186,6 +189,7 @@ import {
WhatsappIcon,
WorkplaceIcon,
XIcon,
KakaoIcon,
} from "react-share";
```

Expand Down
19 changes: 19 additions & 0 deletions demo/Demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ import {
WorkplaceIcon,
WorkplaceShareButton,
XIcon,
KakaoIcon,
KakaoShareButton,
} from '../src';

import './Demo.css';
Expand All @@ -59,6 +61,8 @@ import exampleImage from './react-share-pin-example.png';
export function Demo() {
const shareUrl = 'http://github.com';
const title = 'GitHub';
const exampleImageUrl = `https://gist.github.com/user-attachments/assets/a3ffebb3-f2f1-4e44-a786-e1994d39c8c9`;
const KAKAO_JS_KEY = import.meta.env.VITE_KAKAO_JS_KEY;

return (
<div className="Demo__container">
Expand Down Expand Up @@ -305,6 +309,21 @@ export function Demo() {
<HatenaShareCount url={shareUrl} className="Demo__some-network__share-count" />
</div>
</div>

<div className="Demo__some-network" aria-label="Kakao">
<KakaoShareButton
kakaoJsKey={KAKAO_JS_KEY}
webUrl={shareUrl}
mobileWebUrl={shareUrl}
title={title}
description={'description'}
buttonTitle={'buttonTitle'}
imageUrl={exampleImageUrl}
className="Demo__some-network__share-button"
>
<KakaoIcon size={32} round />
</KakaoShareButton>
</div>
</div>
);
}
Expand Down
9 changes: 9 additions & 0 deletions src/KakaoIcon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import createIcon from './hocs/createIcon';

const KakaoIcon = createIcon({
color: '#f8dd08',
networkName: 'kakao',
path: `m 18.772236,35.39075 c -0.761229,0 -1.380289,-0.594688 -1.380289,-1.325901 v -8.248134 h -2.153709 c -0.746967,0 -1.354524,-0.609957 -1.354524,-1.359449 0,-0.749501 0.607787,-1.359456 1.354524,-1.359456 h 7.067995 c 0.746967,0 1.354525,0.609955 1.354525,1.359456 0,0.749492 -0.607787,1.359449 -1.354525,1.359449 h -2.15371 v 8.248134 c 0,0.731213 -0.619058,1.325901 -1.380287,1.325901 z m 12.103289,-0.01807 c -0.575582,0 -1.015892,-0.235097 -1.148628,-0.613202 l -0.683475,-1.799804 -4.208959,-2.29e-4 -0.683933,1.800962 c -0.132278,0.377405 -0.572361,0.612273 -1.147942,0.612273 a 2.1060902,2.1184354 0 0 1 -0.877401,-0.191595 c -0.380501,-0.176561 -0.746276,-0.66203 -0.327128,-1.971499 l 3.301649,-8.741217 c 0.232579,-0.664802 0.939058,-1.349734 1.838082,-1.37033 0.901561,0.02036 1.608039,0.705528 1.841077,1.371717 l 3.300268,8.737305 c 0.420068,1.312478 0.0543,1.798178 -0.326207,1.974273 a 2.1109212,2.1232948 0 0 1 -0.877403,0.191364 c -2.3e-4,0 0,0 0,0 z m -2.557903,-4.872281 -1.378681,-3.939524 -1.378676,3.939524 z m 5.982859,4.687863 c -0.729484,0 -1.322777,-0.571086 -1.322777,-1.272681 v -9.429412 c 0,-0.765692 0.632175,-1.388374 1.409046,-1.388374 0.776869,0 1.409045,0.622682 1.409045,1.388374 v 8.15673 h 2.933112 c 0.729481,0 1.322778,0.571086 1.322778,1.27268 0,0.701596 -0.593297,1.27268 -1.322778,1.27268 z m 7.668424,0.184423 c -0.76123,0 -1.380291,-0.62269 -1.380291,-1.388379 v -9.498137 c 0,-0.765692 0.619061,-1.388374 1.380291,-1.388374 0.761227,0 1.380287,0.622682 1.380287,1.388374 v 2.984091 l 3.851006,-3.873574 c 0.198068,-0.199234 0.470217,-0.308915 0.765596,-0.308915 0.344614,0 0.690608,0.149482 0.94987,0.410034 0.241782,0.242968 0.386022,0.555583 0.405803,0.880231 0.02002,0.327424 -0.08833,0.627549 -0.30458,0.845289 l -3.145449,3.163422 3.397578,4.527507 a 1.3690161,1.3770409 0 0 1 0.265017,1.028561 1.3701665,1.3781977 0 0 1 -0.535552,0.915631 1.3664856,1.3744954 0 0 1 -0.830243,0.280227 1.3694761,1.3775036 0 0 1 -1.102617,-0.551884 l -3.237009,-4.314157 -0.478959,0.481763 v 3.029216 a 1.3825889,1.3906933 0 0 1 -1.380748,1.38907 z M 32,9.7925047 c -13.213501,0 -23.925,8.4952633 -23.925,18.9745183 0,6.775062 4.478116,12.719866 11.214383,16.076741 -0.366466,1.271292 -2.355001,8.178479 -2.434137,8.7211 0,0 -0.04762,0.407723 0.214865,0.563222 0.262484,0.155499 0.571209,0.0347 0.571209,0.0347 0.752717,-0.105745 8.728712,-5.741178 10.109232,-6.719752 1.379136,0.196457 2.799225,0.298499 4.249448,0.298499 13.213499,0 23.925,-8.49503 23.925,-18.974516 C 55.925,18.287762 45.213499,9.7924987 32,9.7924987 Z`,
});

export default KakaoIcon;
137 changes: 137 additions & 0 deletions src/KakaoShareButton.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import React, { useEffect, useState, useCallback } from 'react';
import cx from 'classnames';

declare global {
interface Window {
Kakao: any;
}
}

type KakaoShareButtonProps = {
kakaoJsKey: string; // kakao app javascript key
webUrl: string;
mobileWebUrl?: string;
title: string;
description?: string;
imageUrl?: string;
className?: string;
buttonTitle?: string;
style?: React.CSSProperties;
disabled?: boolean;
children: React.ReactNode;
};

export const KakaoShareButton: React.FC<KakaoShareButtonProps> = ({
kakaoJsKey,
webUrl,
mobileWebUrl = webUrl,
title,
description = 'description',
imageUrl = '',
className = '',
buttonTitle = 'Shared On Kakao',
style = {},
disabled = false,
children,
}) => {
const [isKakaoLoaded, setIsKakaoLoaded] = useState(false);

const loadKakaoSDK = useCallback(() => {
if (typeof window !== 'undefined' && !window.Kakao) {
const script = document.createElement('script');
script.src = 'https://developers.kakao.com/sdk/js/kakao.js';
script.async = true;

script.onload = () => {
if (window.Kakao && !window.Kakao.isInitialized()) {
try {
window.Kakao.init(kakaoJsKey);
setIsKakaoLoaded(true);
} catch (error) {
console.error('Init Kakao SDK failed : ', error);
}
}
};
script.onerror = () => {
console.error('Load Kakao SDK failed');
};
document.body.appendChild(script);
} else if (window.Kakao && window.Kakao.isInitialized()) {
setIsKakaoLoaded(true);
}
}, []);

useEffect(() => {
loadKakaoSDK();
}, [loadKakaoSDK]);

const handleClick = () => {
if (disabled) return;
if (typeof window !== 'undefined' && !window.Kakao) {
const script = document.createElement('script');
script.src = 'https://developers.kakao.com/sdk/js/kakao.js';
script.async = true;
script.onload = () => {
if (window.Kakao && !window.Kakao.isInitialized()) {
window.Kakao.init(kakaoJsKey);
}
};
document.body.appendChild(script);
}

if (isKakaoLoaded && window.Kakao.isInitialized()) {
window.Kakao.Link.sendDefault({
objectType: 'feed',
content: {
title,
description,
imageUrl,
link: {
mobileWebUrl,
webUrl,
},
},
buttons: [
{
title: buttonTitle,
link: {
mobileWebUrl,
webUrl,
},
},
],
});
} else {
console.error('Kakao SDK is not loaded yet.');
}
};

const buttonClassName = cx(
'react-share__ShareButton',
{ 'react-share__ShareButton--disabled': disabled },
className,
);

const buttonStyle: React.CSSProperties = {
backgroundColor: 'transparent',
border: 'none',
padding: 0,
font: 'inherit',
color: 'inherit',
cursor: disabled ? 'not-allowed' : 'pointer',
...style,
};

return React.createElement(
'button',
{
className: buttonClassName,
style: buttonStyle,
onClick: handleClick,
disabled: isKakaoLoaded ? disabled : true,
},
children,
);
};

export default KakaoShareButton;
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,5 @@ export { default as WhatsappShareButton } from './WhatsappShareButton';
export { default as WorkplaceIcon } from './WorkplaceIcon';
export { default as WorkplaceShareButton } from './WorkplaceShareButton';
export { default as XIcon } from './XIcon';
export { default as KakaoIcon } from './KakaoIcon';
export { default as KakaoShareButton } from './KakaoShareButton';

0 comments on commit 664c071

Please sign in to comment.