Add cake day display in user page & posts/comments #682
This commit is contained in:
parent
8fda7d00d5
commit
68e9755e59
10 changed files with 90 additions and 1 deletions
|
@ -106,6 +106,7 @@ select
|
||||||
u.actor_id as creator_actor_id,
|
u.actor_id as creator_actor_id,
|
||||||
u."local" as creator_local,
|
u."local" as creator_local,
|
||||||
u."name" as creator_name,
|
u."name" as creator_name,
|
||||||
|
u.published as creator_published,
|
||||||
u.avatar as creator_avatar,
|
u.avatar as creator_avatar,
|
||||||
u.banned as banned,
|
u.banned as banned,
|
||||||
cb.id::bool as banned_from_community,
|
cb.id::bool as banned_from_community,
|
||||||
|
@ -490,6 +491,7 @@ select
|
||||||
u.actor_id as creator_actor_id,
|
u.actor_id as creator_actor_id,
|
||||||
u.local as creator_local,
|
u.local as creator_local,
|
||||||
u.name as creator_name,
|
u.name as creator_name,
|
||||||
|
u.published as creator_published,
|
||||||
u.avatar as creator_avatar,
|
u.avatar as creator_avatar,
|
||||||
-- score details
|
-- score details
|
||||||
coalesce(cl.total, 0) as score,
|
coalesce(cl.total, 0) as score,
|
||||||
|
|
|
@ -28,6 +28,7 @@ table! {
|
||||||
creator_local -> Bool,
|
creator_local -> Bool,
|
||||||
creator_name -> Varchar,
|
creator_name -> Varchar,
|
||||||
creator_avatar -> Nullable<Text>,
|
creator_avatar -> Nullable<Text>,
|
||||||
|
creator_published -> Timestamp,
|
||||||
score -> BigInt,
|
score -> BigInt,
|
||||||
upvotes -> BigInt,
|
upvotes -> BigInt,
|
||||||
downvotes -> BigInt,
|
downvotes -> BigInt,
|
||||||
|
@ -63,6 +64,7 @@ table! {
|
||||||
creator_local -> Bool,
|
creator_local -> Bool,
|
||||||
creator_name -> Varchar,
|
creator_name -> Varchar,
|
||||||
creator_avatar -> Nullable<Text>,
|
creator_avatar -> Nullable<Text>,
|
||||||
|
creator_published -> Timestamp,
|
||||||
score -> BigInt,
|
score -> BigInt,
|
||||||
upvotes -> BigInt,
|
upvotes -> BigInt,
|
||||||
downvotes -> BigInt,
|
downvotes -> BigInt,
|
||||||
|
@ -101,6 +103,7 @@ pub struct CommentView {
|
||||||
pub creator_local: bool,
|
pub creator_local: bool,
|
||||||
pub creator_name: String,
|
pub creator_name: String,
|
||||||
pub creator_avatar: Option<String>,
|
pub creator_avatar: Option<String>,
|
||||||
|
pub creator_published: chrono::NaiveDateTime,
|
||||||
pub score: i64,
|
pub score: i64,
|
||||||
pub upvotes: i64,
|
pub upvotes: i64,
|
||||||
pub downvotes: i64,
|
pub downvotes: i64,
|
||||||
|
@ -314,6 +317,7 @@ table! {
|
||||||
creator_local -> Bool,
|
creator_local -> Bool,
|
||||||
creator_name -> Varchar,
|
creator_name -> Varchar,
|
||||||
creator_avatar -> Nullable<Text>,
|
creator_avatar -> Nullable<Text>,
|
||||||
|
creator_published -> Timestamp,
|
||||||
score -> BigInt,
|
score -> BigInt,
|
||||||
upvotes -> BigInt,
|
upvotes -> BigInt,
|
||||||
downvotes -> BigInt,
|
downvotes -> BigInt,
|
||||||
|
@ -353,6 +357,7 @@ pub struct ReplyView {
|
||||||
pub creator_local: bool,
|
pub creator_local: bool,
|
||||||
pub creator_name: String,
|
pub creator_name: String,
|
||||||
pub creator_avatar: Option<String>,
|
pub creator_avatar: Option<String>,
|
||||||
|
pub creator_published: chrono::NaiveDateTime,
|
||||||
pub score: i64,
|
pub score: i64,
|
||||||
pub upvotes: i64,
|
pub upvotes: i64,
|
||||||
pub downvotes: i64,
|
pub downvotes: i64,
|
||||||
|
@ -576,6 +581,7 @@ mod tests {
|
||||||
published: inserted_comment.published,
|
published: inserted_comment.published,
|
||||||
updated: None,
|
updated: None,
|
||||||
creator_name: inserted_user.name.to_owned(),
|
creator_name: inserted_user.name.to_owned(),
|
||||||
|
creator_published: inserted_user.published,
|
||||||
creator_avatar: None,
|
creator_avatar: None,
|
||||||
score: 1,
|
score: 1,
|
||||||
downvotes: 0,
|
downvotes: 0,
|
||||||
|
@ -609,6 +615,7 @@ mod tests {
|
||||||
published: inserted_comment.published,
|
published: inserted_comment.published,
|
||||||
updated: None,
|
updated: None,
|
||||||
creator_name: inserted_user.name.to_owned(),
|
creator_name: inserted_user.name.to_owned(),
|
||||||
|
creator_published: inserted_user.published,
|
||||||
creator_avatar: None,
|
creator_avatar: None,
|
||||||
score: 1,
|
score: 1,
|
||||||
downvotes: 0,
|
downvotes: 0,
|
||||||
|
|
|
@ -28,6 +28,7 @@ table! {
|
||||||
creator_actor_id -> Text,
|
creator_actor_id -> Text,
|
||||||
creator_local -> Bool,
|
creator_local -> Bool,
|
||||||
creator_name -> Varchar,
|
creator_name -> Varchar,
|
||||||
|
creator_published -> Timestamp,
|
||||||
creator_avatar -> Nullable<Text>,
|
creator_avatar -> Nullable<Text>,
|
||||||
banned -> Bool,
|
banned -> Bool,
|
||||||
banned_from_community -> Bool,
|
banned_from_community -> Bool,
|
||||||
|
@ -75,6 +76,7 @@ table! {
|
||||||
creator_actor_id -> Text,
|
creator_actor_id -> Text,
|
||||||
creator_local -> Bool,
|
creator_local -> Bool,
|
||||||
creator_name -> Varchar,
|
creator_name -> Varchar,
|
||||||
|
creator_published -> Timestamp,
|
||||||
creator_avatar -> Nullable<Text>,
|
creator_avatar -> Nullable<Text>,
|
||||||
banned -> Bool,
|
banned -> Bool,
|
||||||
banned_from_community -> Bool,
|
banned_from_community -> Bool,
|
||||||
|
@ -125,6 +127,7 @@ pub struct PostView {
|
||||||
pub creator_actor_id: String,
|
pub creator_actor_id: String,
|
||||||
pub creator_local: bool,
|
pub creator_local: bool,
|
||||||
pub creator_name: String,
|
pub creator_name: String,
|
||||||
|
pub creator_published: chrono::NaiveDateTime,
|
||||||
pub creator_avatar: Option<String>,
|
pub creator_avatar: Option<String>,
|
||||||
pub banned: bool,
|
pub banned: bool,
|
||||||
pub banned_from_community: bool,
|
pub banned_from_community: bool,
|
||||||
|
@ -499,6 +502,7 @@ mod tests {
|
||||||
body: None,
|
body: None,
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
creator_name: user_name.to_owned(),
|
creator_name: user_name.to_owned(),
|
||||||
|
creator_published: inserted_user.published,
|
||||||
creator_avatar: None,
|
creator_avatar: None,
|
||||||
banned: false,
|
banned: false,
|
||||||
banned_from_community: false,
|
banned_from_community: false,
|
||||||
|
@ -548,6 +552,7 @@ mod tests {
|
||||||
stickied: false,
|
stickied: false,
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
creator_name: user_name,
|
creator_name: user_name,
|
||||||
|
creator_published: inserted_user.published,
|
||||||
creator_avatar: None,
|
creator_avatar: None,
|
||||||
banned: false,
|
banned: false,
|
||||||
banned_from_community: false,
|
banned_from_community: false,
|
||||||
|
|
41
ui/src/components/cake-day.tsx
vendored
Normal file
41
ui/src/components/cake-day.tsx
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { Component } from 'inferno';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { i18n } from '../i18next';
|
||||||
|
|
||||||
|
interface CakeDayProps {
|
||||||
|
creator_name: string;
|
||||||
|
creator_published: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CakeDay extends Component<CakeDayProps, any> {
|
||||||
|
render() {
|
||||||
|
const { creator_name, creator_published } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
this.isCakeDay(creator_published) && (
|
||||||
|
<div
|
||||||
|
className="mr-lg-2 d-inline-block unselectable pointer mx-2"
|
||||||
|
data-tippy-content={this.cakeDayTippy(creator_name)}
|
||||||
|
>
|
||||||
|
<svg class="icon icon-inline">
|
||||||
|
<use xlinkHref="#icon-cake"></use>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
isCakeDay(input: string): boolean {
|
||||||
|
const userCreationDate = moment.utc(input).local();
|
||||||
|
const currentDate = moment(new Date());
|
||||||
|
|
||||||
|
return (
|
||||||
|
userCreationDate.date() === currentDate.date() &&
|
||||||
|
userCreationDate.month() === currentDate.month()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
cakeDayTippy(creator_name: string): string {
|
||||||
|
return i18n.t('cake_day_info', { creator_name });
|
||||||
|
}
|
||||||
|
}
|
8
ui/src/components/comment-node.tsx
vendored
8
ui/src/components/comment-node.tsx
vendored
|
@ -33,6 +33,7 @@ import { CommentForm } from './comment-form';
|
||||||
import { CommentNodes } from './comment-nodes';
|
import { CommentNodes } from './comment-nodes';
|
||||||
import { UserListing } from './user-listing';
|
import { UserListing } from './user-listing';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
|
import { CakeDay } from './cake-day';
|
||||||
|
|
||||||
interface CommentNodeState {
|
interface CommentNodeState {
|
||||||
showReply: boolean;
|
showReply: boolean;
|
||||||
|
@ -124,6 +125,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let node = this.props.node;
|
let node = this.props.node;
|
||||||
|
const { creator_name, creator_published } = node.comment;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`comment ${
|
className={`comment ${
|
||||||
|
@ -160,6 +162,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<CakeDay
|
||||||
|
creator_name={creator_name}
|
||||||
|
creator_published={creator_published}
|
||||||
|
/>
|
||||||
|
|
||||||
{this.isMod && (
|
{this.isMod && (
|
||||||
<div className="badge badge-light d-none d-sm-inline mr-2">
|
<div className="badge badge-light d-none d-sm-inline mr-2">
|
||||||
{i18n.t('mod')}
|
{i18n.t('mod')}
|
||||||
|
|
9
ui/src/components/post-listing.tsx
vendored
9
ui/src/components/post-listing.tsx
vendored
|
@ -35,6 +35,7 @@ import {
|
||||||
previewLines,
|
previewLines,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
|
import { CakeDay } from './cake-day';
|
||||||
|
|
||||||
interface PostListingState {
|
interface PostListingState {
|
||||||
showEdit: boolean;
|
showEdit: boolean;
|
||||||
|
@ -253,6 +254,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
|
|
||||||
listing() {
|
listing() {
|
||||||
let post = this.props.post;
|
let post = this.props.post;
|
||||||
|
const { creator_name, creator_published } = post;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div className={`vote-bar col-1 pr-0 small text-center`}>
|
<div className={`vote-bar col-1 pr-0 small text-center`}>
|
||||||
|
@ -432,6 +435,12 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
actor_id: post.creator_actor_id,
|
actor_id: post.creator_actor_id,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<CakeDay
|
||||||
|
creator_name={creator_name}
|
||||||
|
creator_published={creator_published}
|
||||||
|
/>
|
||||||
|
|
||||||
{this.isMod && (
|
{this.isMod && (
|
||||||
<span className="mx-1 badge badge-light">
|
<span className="mx-1 badge badge-light">
|
||||||
{i18n.t('mod')}
|
{i18n.t('mod')}
|
||||||
|
|
3
ui/src/components/symbols.tsx
vendored
3
ui/src/components/symbols.tsx
vendored
File diff suppressed because one or more lines are too long
10
ui/src/components/user.tsx
vendored
10
ui/src/components/user.tsx
vendored
|
@ -46,6 +46,7 @@ import { ListingTypeSelect } from './listing-type-select';
|
||||||
import { CommentNodes } from './comment-nodes';
|
import { CommentNodes } from './comment-nodes';
|
||||||
import { MomentTime } from './moment-time';
|
import { MomentTime } from './moment-time';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
enum View {
|
enum View {
|
||||||
Overview,
|
Overview,
|
||||||
|
@ -412,6 +413,15 @@ export class User extends Component<any, UserState> {
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</h5>
|
</h5>
|
||||||
|
<div className="d-flex align-items-center mb-2">
|
||||||
|
<svg class="icon">
|
||||||
|
<use xlinkHref="#icon-cake"></use>
|
||||||
|
</svg>
|
||||||
|
<span className="ml-2">
|
||||||
|
{i18n.t('cake_day_title')}{' '}
|
||||||
|
{moment.utc(user.published).local().format('MMM DD, YYYY')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{i18n.t('joined')} <MomentTime data={user} showAgo />
|
{i18n.t('joined')} <MomentTime data={user} showAgo />
|
||||||
</div>
|
</div>
|
||||||
|
|
2
ui/src/interfaces.ts
vendored
2
ui/src/interfaces.ts
vendored
|
@ -183,6 +183,7 @@ export interface Post {
|
||||||
creator_actor_id: string;
|
creator_actor_id: string;
|
||||||
creator_local: boolean;
|
creator_local: boolean;
|
||||||
creator_name: string;
|
creator_name: string;
|
||||||
|
creator_published: string;
|
||||||
creator_avatar?: string;
|
creator_avatar?: string;
|
||||||
community_actor_id: string;
|
community_actor_id: string;
|
||||||
community_local: boolean;
|
community_local: boolean;
|
||||||
|
@ -227,6 +228,7 @@ export interface Comment {
|
||||||
creator_local: boolean;
|
creator_local: boolean;
|
||||||
creator_name: string;
|
creator_name: string;
|
||||||
creator_avatar?: string;
|
creator_avatar?: string;
|
||||||
|
creator_published: string;
|
||||||
score: number;
|
score: number;
|
||||||
upvotes: number;
|
upvotes: number;
|
||||||
downvotes: number;
|
downvotes: number;
|
||||||
|
|
4
ui/translations/en.json
vendored
4
ui/translations/en.json
vendored
|
@ -265,5 +265,7 @@
|
||||||
"action": "Action",
|
"action": "Action",
|
||||||
"emoji_picker": "Emoji Picker",
|
"emoji_picker": "Emoji Picker",
|
||||||
"block_leaving": "Are you sure you want to leave?",
|
"block_leaving": "Are you sure you want to leave?",
|
||||||
"what_is": "What is"
|
"what_is": "What is",
|
||||||
|
"cake_day_title": "Cake day:",
|
||||||
|
"cake_day_info": "It's {{ creator_name }}'s cake day today!"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue