Skip to content

Commit

Permalink
fix: add exclusive timeline feature (#1959)
Browse files Browse the repository at this point in the history
* fix: add exclusive timeline

* fix: add subscription button for exclusive timeline

* format fix

* Update ExperienceCard.tsx
  • Loading branch information
RiXelanya authored Oct 23, 2024
1 parent c813b68 commit 46103d4
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 52 deletions.
18 changes: 17 additions & 1 deletion src/components/ExperienceEditor/BasicExperienceEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ import React, { useState, useRef } from 'react';
import {
Button,
FormControl,
FormControlLabel,
FormGroup,
FormHelperText,
IconButton,
InputLabel,
OutlinedInput,
SvgIcon,
Switch,
TextField,
Typography,
} from '@material-ui/core';
Expand Down Expand Up @@ -96,11 +99,19 @@ export const BasicExperienceEditor: React.FC<BasicExperienceEditorProps> =
selectedUserId: false,
});

const onSwitch = (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.checked;
onExperience(prevExperience => ({
...prevExperience,
exclusive: value,
}));
setDetailChanged(experience['exclusive'] !== value);
};

const handleChange =
(field: keyof ExperienceProps) =>
(event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value.trimStart();

onExperience(prevExperience => ({
...prevExperience,
[field]: value,
Expand Down Expand Up @@ -242,6 +253,11 @@ export const BasicExperienceEditor: React.FC<BasicExperienceEditorProps> =
</div>
</ShowIf>
</FormControl>
<FormGroup>
<FormControlLabel
control={<Switch onChange={onSwitch} />}
label="Exclusive"></FormControlLabel>
</FormGroup>
</div>
<div className={styles.row2}>
<FormControl fullWidth variant="outlined" error={errors.name}>
Expand Down
63 changes: 61 additions & 2 deletions src/components/Timeline/Render/ExperienceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { DotsVerticalIcon, DuplicateIcon } from '@heroicons/react/outline';

import React, { useState } from 'react';
import CopyToClipboard from 'react-copy-to-clipboard';
import { useSelector } from 'react-redux';

import getConfig from 'next/config';
import dynamic from 'next/dynamic';
Expand All @@ -21,6 +22,8 @@ import {
import Menu from '@material-ui/core/Menu';
import BaseMenuItem from '@material-ui/core/MenuItem';

import { BN, BN_TEN } from '@polkadot/util';

import { useStyles } from './experience-card.style';

import { WithAuthorizeAction } from 'components/common/Authorization/WithAuthorizeAction';
Expand All @@ -29,9 +32,14 @@ import ShowIf from 'components/common/show-if.component';
import { Modal } from 'src/components/atoms/Modal';
import useConfirm from 'src/components/common/Confirm/use-confirm.hook';
import { useExperienceHook } from 'src/hooks/use-experience-hook';
import { useWallet } from 'src/hooks/use-wallet-hook';
import { Experience, WrappedExperience } from 'src/interfaces/experience';
import { ReferenceType } from 'src/interfaces/interaction';
import { User } from 'src/interfaces/user';
import * as UserAPI from 'src/lib/api/user';
import i18n from 'src/locale';
import { RootState } from 'src/reducers';
import { BalanceState } from 'src/reducers/balance/reducer';

const MenuItem = WithAuthorizeAction(BaseMenuItem);

Expand Down Expand Up @@ -59,19 +67,26 @@ export const ExperienceCard: React.FC<ExperienceCardProps> = props => {
const confirm = useConfirm();
const enqueueSnackbar = useEnqueueSnackbar();
const [promptSignin, setPromptSignin] = useState(false);
const [isSubscribing, setSubscribing] = useState(false);
const [menuAnchorElement, setMenuAnchorElement] =
useState<null | HTMLElement>(null);
const [shareAnchorElement, setShareAnchorElement] =
useState<null | HTMLElement>(null);
const { userExperiencesMeta, removeExperience, loadExperience } =
useExperienceHook();
const { sendTip } = useWallet();
const link = publicRuntimeConfig.appAuthURL + `?type=all&id=${experience.id}`;
const isOwnExperience = experience?.createdBy === user?.id;
const isHidden = () => {
if (experience.private && !experience.friend) return true;
if (experience.private && experience.friend) return false;
return false;
};
const isExclusive = experience.exclusive ? experience.exclusive : false;

const { balanceDetails: balances } = useSelector<RootState, BalanceState>(
state => state.balanceState,
);

const isSubscribed = () => {
return (
Expand Down Expand Up @@ -175,6 +190,29 @@ export const ExperienceCard: React.FC<ExperienceCardProps> = props => {
});
};

const handleSubscription = () => {
setSubscribing(true);
};

const closeSubscribing = () => {
setSubscribing(false);
};

const handlePaySubscription = async () => {
const receiver = await UserAPI.getWalletAddress(experience.createdBy);
const defaultCurrency = balances[0];
const amount = BN_TEN.pow(new BN(defaultCurrency.decimal - 2));
await sendTip(
receiver,
amount,
balances[0],
ReferenceType.EXCLUSIVE_TIMELINE,
experience.id,
);
handleSubscribeExperience();
closeSubscribing();
};

const confirmDeleteExperience = () => {
handleCloseSettings();

Expand Down Expand Up @@ -258,12 +296,19 @@ export const ExperienceCard: React.FC<ExperienceCardProps> = props => {
variant="contained"
color="primary"
size="small"
disabled={isExclusive && isSubscribed()}
onClick={
isSubscribed()
isExclusive
? handleSubscription
: isSubscribed()
? openUnsubscribeConfirmation
: handleSubscribeExperience
}>
{isSubscribed()
{isExclusive
? isSubscribed()
? 'subscribed'
: i18n.t('Experience.Preview.Button.Subscription')
: isSubscribed()
? i18n.t('Experience.Preview.Button.Unsubscribe')
: i18n.t('Experience.Preview.Button.Subscribe')}
</Button>
Expand Down Expand Up @@ -382,6 +427,20 @@ export const ExperienceCard: React.FC<ExperienceCardProps> = props => {
/>
</div>
</Modal>
<Modal
title="subscription"
subtitle="subscription"
className={style.modal}
open={isSubscribing}
onClose={closeSubscribing}>
<Button
variant="contained"
color="primary"
size="small"
onClick={handlePaySubscription}>
{i18n.t('Experience.Preview.Button.Subscription')}
</Button>
</Modal>
<ExperienceSignInDialog
open={promptSignin}
onClose={() => setPromptSignin(false)}
Expand Down
1 change: 1 addition & 0 deletions src/interfaces/experience.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export interface ExperienceProps extends Searchable {
visibility: string;
selectedUserIds: SelectedUserIds[];
editorsId?: string[];
exclusive?: boolean;
}

export interface Experience extends ExperienceProps, BaseModel {
Expand Down
1 change: 1 addition & 0 deletions src/interfaces/interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export enum ReferenceType {
USER = 'user',
PEOPLE = 'people',
EXCLUSIVE_CONTENT = 'unlockable_content',
EXCLUSIVE_TIMELINE = 'unlockable_timeline',
}

export enum SectionType {
Expand Down
21 changes: 11 additions & 10 deletions src/lib/api/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,17 @@ export const getPost = async (
params.owner = userId;
}

if (params.experienceId) {
const { data } = await MyriadAPI().request<PostList>({
url: `/experience/${params.experienceId}/posts`,
method: 'GET',
params: {
filter: filterParams,
},
});
return data;
}

const { data } = await MyriadAPI().request<PostList>({
url: '/user/posts',
method: 'GET',
Expand All @@ -156,16 +167,6 @@ export const getPost = async (
return post.createdBy === fields.owner;
});
}
if (data.data.length === 0 && params.experienceId) {
const { data } = await MyriadAPI().request<PostList>({
url: `/experience/${params.experienceId}/posts`,
method: 'GET',
params: {
filter: filterParams,
},
});
return data;
}

return data;
};
Expand Down
43 changes: 4 additions & 39 deletions src/lib/services/polkadot-js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,62 +260,27 @@ export class PolkadotJs implements IProvider {

callback && callback({ signerOpened: true });

// here we use the api to create a balance transfer to some account of a value of 12345678
const { referenceId: accountId, referenceType } = walletDetail;
const isWalletAddress =
referenceType === WalletReferenceType.WALLET_ADDRESS;
const assetId = parseInt(referenceId);
const transferExtrinsic = isWalletAddress
? !referenceId
? api.tx.balances.transfer(accountId, amount)
? api.tx.balances.transferKeepAlive(accountId, amount)
: api.tx.octopusAssets.transfer(assetId, accountId, amount)
: api.tx.tipping.sendTip(walletDetail, amount);

// passing the injected account address as the first argument of signAndSend
// will allow the api to retrieve the signer and the user will see the extension
// popup asking to sign the balance transfer transaction
const txInfo = await transferExtrinsic.signAsync(signer.address, {

const txInfo = await transferExtrinsic.signAndSend(signer.address, {
signer: injector.signer,
// make sure nonce does not stuck
nonce: -1,
});

const txHash: string = await new Promise((resolve, reject) => {
txInfo
.send(({ status, isError, dispatchError }) => {
if (status.isInBlock) {
console.log(`\tBlock hash : ${status.asInBlock.toHex()}`);
} else if (status.isFinalized) {
console.log(`\tFinalized : ${status.asFinalized.toHex()}`);
resolve(status.asFinalized.toHex());
} else if (isError) {
console.log(`\tFinalized : null`);
reject('FailedToSendTip');
}

if (dispatchError) {
if (dispatchError.isModule) {
const { name } = api.registry.findMetaError(
dispatchError.asModule,
);

reject(new Error(name));
} else {
const dispatchErrorType = dispatchError.toString();
const parseDispatch = JSON.parse(dispatchErrorType);

const values: string[] = Object.values(parseDispatch);

reject(new Error(values[0] ?? 'ExtrinsicFailed'));
}
}
})
.catch(err => {
reject(err);
});
});

return txHash;
return txInfo.toHex();
} catch (error) {
console.log(error);
throw error;
Expand Down
1 change: 1 addition & 0 deletions src/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"Edit": "Edit timeline",
"Subscribe": "Follow",
"Unsubscribe": "Unfollow",
"Subscription": "Subscribe",
"Clone": "Clone"
},
"Subheader": {
Expand Down
1 change: 1 addition & 0 deletions src/locale/fra.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"Edit": "Edit timeline",
"Subscribe": "Follow",
"Unsubscribe": "Unfollow",
"Subscription": "Subscribe",
"Clone": "Cloner"
},
"Subheader": {
Expand Down
1 change: 1 addition & 0 deletions src/locale/id.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"Edit": "Ubah Timeline",
"Subscribe": "Mengikuti",
"Unsubscribe": "Berhenti mengikuti",
"Subscription": "Subscribe",
"Clone": "Klon"
},
"Subheader": {
Expand Down
1 change: 1 addition & 0 deletions src/locale/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"Edit": "Редактировать опыт",
"Subscribe": "Следовать",
"Unsubscribe": "Отписаться",
"Subscription": "Subscribe",
"Clone": "Копия"
},
"Subheader": {
Expand Down

0 comments on commit 46103d4

Please sign in to comment.