Skip to content

Commit

Permalink
Merge branch '220-countdown-in-post' into 'develop'
Browse files Browse the repository at this point in the history
Resolve "Add support to countdown in posts"

Closes #220

See merge request hive/condenser!405
  • Loading branch information
dnotestein committed May 12, 2024
2 parents 427458b + 39319ae commit 70b0eb8
Show file tree
Hide file tree
Showing 15 changed files with 523 additions and 11 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ lib
*.bk
docs
jest
._*
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ FROM node:18.14.0 as development
WORKDIR /var/app

COPY package.json yarn.lock ./
COPY patches ./patches

RUN yarn install --non-interactive --frozen-lockfile --ignore-optional

Expand Down
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"storybook": "start-storybook --no-manager-cache -p 6006",
"storybook-build": "build-storybook -c .storybook -o docs",
"build:analyze": "webpack-bundle-analyzer --port 4200 dist/stats.json",
"build:storybook": "build-storybook"
"build:storybook": "build-storybook",
"postinstall": "patch-package"
},
"author": "HiveBlog",
"license": "MIT",
Expand Down Expand Up @@ -99,12 +100,14 @@
"libsodium-wrappers": "0.4.8",
"lodash": "^4.17.21",
"lodash.debounce": "4.0.8",
"luxon": "^3.4.4",
"mem-stat": "1.0.5",
"mini-css-extract-plugin": "^1.2.1",
"mixpanel": "0.5.0",
"node-cache": "4.2.0",
"node-fetch": "^2.6.7",
"os": "0.1.1",
"patch-package": "^8.0.0",
"path": "^0.12.7",
"pluralizers": "^0.1.7",
"postcss": "^8.1.6",
Expand All @@ -114,6 +117,8 @@
"react": "^18.1.0",
"react-addons-pure-render-mixin": "^15.6.3",
"react-autocomplete": "^1.8.1",
"react-calendar": "^4.8.0",
"react-clock": "^4.6.0",
"react-copy-to-clipboard": "^5.1.0",
"react-dispatch": "^1.2.0",
"react-dom": "^18.1.0",
Expand Down Expand Up @@ -146,6 +151,7 @@
"secure-random": "1.1.1",
"selection-position": "1.0.0",
"simple-react-lightbox": "^3.6.9-0",
"simplepicker": "^2.0.4",
"slate": "~0.43",
"slate-drop-or-paste-images": "0.9.1",
"slate-insert-block-on-enter": "0.4.0",
Expand Down
16 changes: 16 additions & 0 deletions patches/simplepicker+2.0.4.patch

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions src/app/components/cards/CountdownSelector.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from "react";
import tt from 'counterpart';

import DateTimePicker from "../elements/DateTimePicker";

const CountdownSelector = (props) => {
const { value, onChange } = props;

return (
<>
<div className="row">
<div className="column">
<h4>{tt('post_advanced_settings_jsx.countdown')}</h4>
<p>{tt('post_advanced_settings_jsx.countdown_description')}</p>
</div>
</div>
<div className="row">
<div className="small-12 medium-6 large-12 columns">
<DateTimePicker
onChange={onChange}
value={value}
/>
</div>
</div>
</>
);
};

export default CountdownSelector;
7 changes: 6 additions & 1 deletion src/app/components/cards/PostFull.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { Role } from 'app/utils/Community';
import UserNames from 'app/components/elements/UserNames';
import ContentEditedWrapper from '../elements/ContentEditedWrapper';
import { isUrlWhitelisted } from "../../utils/Phishing";
import PostCountDown from "../elements/PostCountDown";

function TimeAuthorCategory({ post }) {
return (
Expand Down Expand Up @@ -508,7 +509,11 @@ class PostFull extends React.Component {
{post_header}
<TimeAuthorCategoryLarge post={post} />
</div>
<div className="PostFull__body entry-content">{contentBody}</div>
<PostCountDown post={post} />
<div className="PostFull__body entry-content">
{contentBody}
</div>
{content_body.length >= 1500 && <PostCountDown post={post} />}
</span>
)}

Expand Down
16 changes: 16 additions & 0 deletions src/app/components/cards/PostFull.scss
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,19 @@ input.share-box {
.table-responsive {
overflow-x: auto;
}

.PostFull__countdown {
width: 100%;
background-color: darkred;
color: white;
text-align: center;

&.terminated {
background-color: lightgray;
color: black;
}
}

.PostFull__countdown {
font-size: 0.85rem;
}
2 changes: 2 additions & 0 deletions src/app/components/cards/PostSummary.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { proxifyImageUrl } from 'app/utils/ProxifyUrl';
import Userpic, { SIZE_SMALL } from 'app/components/elements/Userpic';
import { SIGNUP_URL } from 'shared/constants';
import { hasNsfwTag } from 'app/utils/StateFunctions';
import PostCountDown from "../elements/PostCountDown";

const CURATOR_VESTS_THRESHOLD = 1.0 * 1000.0 * 1000.0;

Expand Down Expand Up @@ -345,6 +346,7 @@ class PostSummary extends React.Component {
<div className="articles__content-block articles__content-block--text">
{content_title}
{content_body}
<PostCountDown post={post} />
{this.props.blogmode ? null : <div className="articles__footer">{summary_footer}</div>}
</div>
{this.props.blogmode ? summary_footer : null}
Expand Down
59 changes: 59 additions & 0 deletions src/app/components/elements/DateTimePicker.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React, { useEffect, useCallback, useState } from 'react';
import SimplePicker from 'simplepicker';
import { DateTime } from 'luxon';
import tt from 'counterpart';

import 'simplepicker/dist/simplepicker.css';

const DateTimePicker = (props) => {
const { onChange, value } = props;
const [picker, setPicker] = useState(undefined);
const [error, setError] = useState('');

const handleChange = useCallback((event) => {
const coundDownDate = DateTime.fromISO(event);
const delta = coundDownDate.diffNow().as('seconds');

if (delta < 3600) {
setError(tt('post_advanced_settings_jsx.countdown_date_error'));
} else if (onChange) {
setError('');
onChange(coundDownDate);
}
}, []);

const openPicker = useCallback(() => {
if (value) {
picker.reset(value.toJSDate());
}
picker.open();
}, [picker, value]);

useEffect(() => {
const _picker = new SimplePicker('.datetimepicker');
_picker.on('submit', (date) => {
handleChange(date.toISOString());
});
setPicker(_picker);
}, [onChange]);

return (
<div>
{picker && (
<input
className="datetimepicker_value"
type="text"
name="countdown"
placeholder={tt('post_advanced_settings_jsx.countdown_placeholder')}
value={value ? value.toLocaleString(DateTime.DATETIME_SHORT) : ''}
onClick={openPicker}
readOnly
/>
)}
<div className="datetimepicker" />
<div className="error">{error}</div>
</div>
);
};

export default DateTimePicker;
56 changes: 56 additions & 0 deletions src/app/components/elements/PostCountDown.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, { useEffect, useState } from 'react';
import { DateTime } from 'luxon';
import tt from "counterpart";
import HumanizeDuration from "humanize-duration";
import classnames from 'classnames';

const PostCountDown = (props) => {
const { post } = props;
const jsonMetadata = post.get('json_metadata');
const countDownEndDate = jsonMetadata.get('countdown');
const [remainingTime, setRemainingTime] = useState();
const [intervalHandler, setIntervalHandler] = useState();

useEffect(() => {
const endDate = DateTime.fromISO(countDownEndDate);
let interval = 1000;

if (endDate.diff(DateTime.now()).as('days') >= 1) {
interval = 24 * 3600;
}

const updateRemainingTime = () => {
const remainingSeconds = Math.round(endDate.diff(DateTime.now()).as('seconds'));
setRemainingTime(remainingSeconds);
};

if (countDownEndDate) {
updateRemainingTime();
setIntervalHandler(setInterval(updateRemainingTime, interval));
}

return () => {
clearInterval(intervalHandler);
};
}, [countDownEndDate]);

useEffect(() => {
if (remainingTime && remainingTime <= 0) {
clearInterval(intervalHandler);
}
}, [remainingTime]);

if (!countDownEndDate) {
return null;
}

return (
<div className={classnames('PostFull__countdown', { terminated: remainingTime <= 0 })}>
{remainingTime > 0
? tt('postfull_jsx.post_countdown', { remainingTime: HumanizeDuration(remainingTime * 1000, { largest: 2 })})
: tt('postfull_jsx.post_countdown_terminated', { date: DateTime.fromISO(countDownEndDate).toLocaleString(DateTime.DATETIME_MED) })}
</div>
);
};

export default PostCountDown;
Loading

0 comments on commit 70b0eb8

Please sign in to comment.