Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]
### Added
- `Custom Share` button
- Add `ServiceName` enum
### Changed
- Update `ShareModal` display settings to handle `Custom Share` button action
- Update naming conventions
- GitHub action versions updated
## [0.5] - 2025-01-15
Expand Down
1 change: 1 addition & 0 deletions src/Assets/icons/services/custom-share.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: 5 additions & 0 deletions src/Data/constants.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ export enum ServiceName {
Pinterest = 'Pinterest',
HackerNews = 'Hacker News',
}

export enum SharingMode {
Direct = 'direct',
Indirect = 'indirect',
}
11 changes: 10 additions & 1 deletion src/Data/services.data.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import X from '@assets/icons/services/x.svg';
import { ServiceName } from './constants.data';
import { CONFIG } from '@src/App/Config/constants';
import Email from '@assets/icons/services/email.svg';
import Gmail from '@assets/icons/services/gmail.svg';
import Yahoo from '@assets/icons/services/yahoo.svg';
Expand All @@ -11,10 +13,11 @@ import Facebook from '@assets/icons/services/facebook.svg';
import Linkedin from '@assets/icons/services/linkedin.svg';
import Pinterest from '@assets/icons/services/pinterest.svg';
import HackerNews from '@assets/icons/services/hacker-news.svg';
import { ServiceName } from './constants.data';
import CustomShare from '@assets/icons/services/custom-share.svg';

export const getServiceURL = (url: string, subject?: string): { [key: string]: string } => {
return {
[ServiceName.Custom]: `${CONFIG.FRONT_DOMAIN}/?path=custom_share&link=${url}&subject=${subject}`,
[ServiceName.Email]: `mailto:?subject=${subject}&body=${url}`,
[ServiceName.Gmail]: `https://mail.google.com/mail/u/0/?ui=2&fs=1&tf=cm&su=${subject}&body=${url}`,
[ServiceName.Telegram]: `https://telegram.me/share/url?url=${url}&text=${subject}`,
Expand All @@ -32,6 +35,12 @@ export const getServiceURL = (url: string, subject?: string): { [key: string]: s
};

export const SERVICES = [
{
title: ServiceName.Custom,
icon: CustomShare,
iconUrl: 'https://github.com/openscilab/mybutton/raw/main/src/Assets/icons/services/custom-share.svg',
bg: '#4b2b79',
},
{
title: ServiceName.Email,
icon: Email,
Expand Down
10 changes: 5 additions & 5 deletions src/Tools/Store/slices/LocalCacheSlice.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { createSlice } from '@reduxjs/toolkit';
import useStore from '../useStore';

type InitStateType = { openShareModal: boolean; activePage: string };
type InitStateType = { shareModal: { open: boolean; url?: string; subject?: string }; activePage: string };

const initialState: InitStateType = { openShareModal: false, activePage: 'home' };
const initialState: InitStateType = { shareModal: { open: false }, activePage: 'home' };

const localCacheSlice = createSlice({
name: 'localCache',
initialState,
reducers: {
setOpenShareModal(state, { payload }) {
state.openShareModal = payload;
setShareModal(state, { payload }) {
state.shareModal = payload;
},
setActivePage(state, { payload }) {
state.activePage = payload;
Expand All @@ -23,6 +23,6 @@ export const useLocalCache = () => {
return selector(s => s?.localStorage);
};

export const { setOpenShareModal, setActivePage } = localCacheSlice.actions;
export const { setShareModal, setActivePage } = localCacheSlice.actions;

export default localCacheSlice;
4 changes: 2 additions & 2 deletions src/Views/Layout/Navbar/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import useStore from '@src/Tools/Store/useStore';
import { classes } from '@src/Tools/Utils/React';
import { useSearchParams } from 'react-router-dom';
import { ReactComponent as Logo } from '@assets/Images/logo-text.svg';
import { setOpenShareModal, useLocalCache } from '@src/Tools/Store/slices/LocalCacheSlice';
import { setShareModal, useLocalCache } from '@src/Tools/Store/slices/LocalCacheSlice';

const NavBar = () => {
const { activePage } = useLocalCache();
Expand All @@ -27,7 +27,7 @@ const NavBar = () => {
</Navbar.Brand>

<Nav className='main-nav' pullRight>
<div className='nav-item' onClick={() => dispatch(setOpenShareModal(true))}>
<div className='nav-item' onClick={() => dispatch(setShareModal({ open: true }))}>
Share
</div>
<div
Expand Down
20 changes: 9 additions & 11 deletions src/Views/Layout/ShareModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,23 @@ import { CONFIG } from '@src/App/Config/constants';
import { encode } from '@src/Tools/Utils/URLEncoding';
import { SERVICES, getServiceURL } from '@src/Data/services.data';
import EditableInput from '@src/Components/EditableInput/EditableInput';
import { setOpenShareModal, useLocalCache } from '@src/Tools/Store/slices/LocalCacheSlice';
import { useLocalCache, setShareModal } from '@src/Tools/Store/slices/LocalCacheSlice';
import { Checkbox, CheckboxGroup, Col, Modal, Radio, RadioGroup, Row, Tooltip, Whisper } from 'rsuite';
import { useSearchParams } from 'react-router-dom';
import { useData } from '@src/Tools/Hooks/useData';
import { ServiceName } from '@src/Data/constants.data';

const ShareModal = () => {
const { dispatch } = useStore();
const urlParams = useSearchParams()[0];
const path = urlParams.get('path');
const { openShareModal } = useLocalCache();
const { shareModal } = useLocalCache();
const { set, temp, discard } = useData(
{
url: path === 'custom_share' ? urlParams.get('link') || '' : '',
subject: path === 'custom_share' ? urlParams.get('subject') || '' : '',
url: shareModal.url || '',
subject: shareModal.subject || '',
isValid: true,
encodingValue: [],
shareMode: 'direct',
},
[urlParams]
[shareModal]
);

// ? ------------------------- Functions -----------------------
Expand Down Expand Up @@ -56,10 +54,10 @@ const ShareModal = () => {
// --------------------------------------------------------------
return (
<Modal
open={openShareModal}
open={shareModal.open}
size='sm'
onClose={() => {
dispatch(setOpenShareModal(false));
dispatch(setShareModal({ open: false }));
discard();
}}
backdrop
Expand Down Expand Up @@ -122,7 +120,7 @@ const ShareModal = () => {
</div>
<div className='services-list'>
<Row>
{SERVICES.map((service, i) => {
{SERVICES.filter(s => s.title !== ServiceName.Custom).map((service, i) => {
const validated_url = urlValidation(temp.url);
const href =
temp.shareMode === 'direct'
Expand Down
12 changes: 6 additions & 6 deletions src/Views/Layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ import ShareModal from './ShareModal';
import PagesRouter from '../Pages/PagesRouter';
import useStore from '@src/Tools/Store/useStore';
import { useSearchParams } from 'react-router-dom';
import { ReactComponent as Bg } from '@assets/Images/bg.svg';
import { setActivePage, setOpenShareModal } from '@src/Tools/Store/slices/LocalCacheSlice';
import { decode } from '@src/Tools/Utils/URLEncoding';
import { ReactComponent as Bg } from '@assets/Images/bg.svg';
import { setActivePage, setShareModal } from '@src/Tools/Store/slices/LocalCacheSlice';

const Layout = () => {
const urlParams = useSearchParams()[0];
const encoded = urlParams.get('encoded');
const path = urlParams.get('path') || new URLSearchParams(decode(encoded || '')).get('path');
const paramsString = encoded ? new URLSearchParams(decode(encoded || '')) : urlParams;
const path = paramsString.get('path');
const { dispatch } = useStore();

useEffect(() => {
dispatch(setActivePage(path));
if (path === 'custom_share') {
dispatch(setOpenShareModal(true));
}
dispatch(setShareModal({ open: true, url: paramsString.get('link'), subject: paramsString.get('subject') }));
} else dispatch(setActivePage(path));
}, [path]);

return (
Expand Down
7 changes: 5 additions & 2 deletions src/Views/Pages/GetButton/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@
@apply overflow-y-auto;

.rs-col {
@apply sm:mb-2;
@apply sm:mb-2 p-0;
}

.service-radio {
Expand Down Expand Up @@ -196,7 +196,10 @@
}

h2 {
@apply ml-1.5 capitalize transition-colors;
@apply ml-1.5 transition-colors;
@include down(md) {
font-size: 0.75rem;
}
}
}
}
Expand Down
37 changes: 29 additions & 8 deletions src/Views/Pages/GetButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,26 @@ import './index.scss';
import { useEffect, useState } from 'react';
import Service from '@src/Components/Service';
import { ValueType } from 'rsuite/esm/Checkbox';
import useStore from '@src/Tools/Store/useStore';
import useWindow from '@src/Tools/Hooks/useWindow';
import { useData } from '@src/Tools/Hooks/useData';
import { CONFIG } from '@src/App/Config/constants';
import { classes } from '../../../Tools/Utils/React';
import { encode } from '@src/Tools/Utils/URLEncoding';
import { ServiceName, SharingMode } from '@src/Data/constants.data';
import SyntaxHighlighter from 'react-syntax-highlighter';
import { copyToClipboard } from '@src/Tools/Utils/React';
import { SERVICES, getServiceURL } from '@src/Data/services.data';
import EditableInput from '@src/Components/EditableInput/EditableInput';
import { setShareModal } from '@src/Tools/Store/slices/LocalCacheSlice';
import { lightfair } from 'react-syntax-highlighter/dist/esm/styles/hljs';
import { ReactComponent as Clone } from '@assets/icons/clone-regular.svg';
import { Button, Checkbox, CheckboxGroup, Col, Modal, Radio, RadioGroup, Row, Tooltip, Whisper } from 'rsuite';

const GetButton = () => {
const { isMobile } = useWindow();
const [buttons, setButtons] = useState<JSX.Element>();
const { dispatch } = useStore();
const { set, temp } = useData({
url: '',
code: '',
Expand All @@ -26,12 +30,13 @@ const GetButton = () => {
showCode: false,
openModal: false,
openTooltip: false,
shareMode: 'direct',
shareMode: SharingMode.Direct,
whisperOpen: false,
dropdownOpen: false,
encodingValue: [],
});
const [selectedServices, setSelectedServices] = useState<string[]>(['Email']);
const [selectedServices, setSelectedServices] = useState<string[]>([ServiceName.Email]);
const services = temp.shareMode === SharingMode.Indirect ? SERVICES : SERVICES.filter(s => s.title !== ServiceName.Custom);

// ? -------------------------- Functions ------------------------------
const onAddService = (title: string) => {
Expand Down Expand Up @@ -71,7 +76,18 @@ const GetButton = () => {
{selected.map((service, i) => {
const href = temp.shareMode === 'direct' ? urls[service.title] : getShareLink(service.title, validated_url);
return (
<a href={href} target='_blank' rel='noreferrer' key={i}>
<a
href={href}
{...(service.title === ServiceName.Custom
? {
onClick: e => {
openShareModal(e, temp.url);
},
}
: {})}
target='_blank'
rel='noreferrer'
key={i}>
<img
src={service.icon}
width={32}
Expand Down Expand Up @@ -114,10 +130,15 @@ const GetButton = () => {
else set.ou.temp('encodingValue', []);
};

const openShareModal = (e: any, url: string) => {
e.preventDefault();
dispatch(setShareModal({ open: true, url, subject: temp.subject }));
};

// ? ------------------------------ useEffect -------------------------------
useEffect(() => {
if (temp.showCode) getCode();
}, [selectedServices, temp.shareMode, temp.encodingValue]);
}, [selectedServices, temp.shareMode, temp.encodingValue.length]);
// --------------------------------------------------------------------------
return (
<div className='get-button-layout'>
Expand Down Expand Up @@ -168,14 +189,14 @@ const GetButton = () => {
defaultValue={temp.shareMode}
onChange={value => set.ou.temp('shareMode', value)}>
<label className='box-label'>Sharing Mode: </label>
<Radio value='direct'>Direct</Radio>
<Radio value='indirect'>Indirect</Radio>
<Radio value={SharingMode.Direct}>Direct</Radio>
<Radio value={SharingMode.Indirect}>Indirect</Radio>
</RadioGroup>
</div>
</Whisper>
<div
{...classes('encoding-mode-checkbox ', {
'is-visible': temp.shareMode === 'indirect',
'is-visible': temp.shareMode === SharingMode.Indirect,
})}>
<CheckboxGroup inline name='checkbox-group' value={temp.encodingValue}>
<Checkbox value='base64' onChange={onCheckboxChanged}>
Expand Down Expand Up @@ -237,7 +258,7 @@ const GetButton = () => {
<Modal.Body>
<div className='services-list'>
<Row>
{SERVICES.map((service, i) => {
{services.map((service, i) => {
const checked = selectedServices.includes(service.title);
return (
<Col xs={12} sm={8} key={i}>
Expand Down