Add absolute time setting

This commit is contained in:
noellabo 2022-12-29 14:01:26 +09:00
parent 498cef91c6
commit d73a197f61
14 changed files with 95 additions and 9 deletions

View file

@ -112,6 +112,7 @@ class Settings::PreferencesController < Settings::BaseController
:setting_disable_clear_all_notifications,
:setting_disable_account_delete,
:setting_prohibited_words,
:setting_disable_relative_time,
setting_prohibited_visibilities: [],
notification_emails: %i(follow follow_request reblog favourite emoji_reaction status_reference mention digest report pending_account trending_tag),
interactions: %i(must_be_follower must_be_following must_be_following_dm must_be_dm_to_send_email must_be_following_reference)

View file

@ -0,0 +1,68 @@
import React from 'react';
import { injectIntl } from 'react-intl';
import PropTypes from 'prop-types';
const dateFormatOptions = {
hour12: false,
year: 'numeric',
month: 'short',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
};
const shortDateFormatOptions = {
month: 'short',
day: 'numeric',
};
const shortTimeFormatOptions = {
hour: '2-digit',
minute: '2-digit',
};
const DAY = 1000 * 60 * 60 * 24;
export const timeString = (intl, date, year) => {
const delta = intl.now() - date.getTime();
let absoluteTime;
if (Math.abs(delta) < DAY) {
absoluteTime = intl.formatDate(date, shortTimeFormatOptions);
} else if (date.getFullYear() === year) {
absoluteTime = intl.formatDate(date, shortDateFormatOptions);
} else {
absoluteTime = intl.formatDate(date, { ...shortDateFormatOptions, year: 'numeric' });
}
return absoluteTime;
};
export default @injectIntl
class AbsoluteTimestamp extends React.Component {
static propTypes = {
intl: PropTypes.object.isRequired,
timestamp: PropTypes.string.isRequired,
year: PropTypes.number.isRequired,
};
static defaultProps = {
year: (new Date()).getFullYear(),
};
render () {
const { timestamp, intl, year } = this.props;
const date = new Date(timestamp);
const absoluteTime = timeString(intl, date, year);
return (
<time dateTime={timestamp} title={intl.formatDate(date, dateFormatOptions)}>
{absoluteTime}
</time>
);
}
}

View file

@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
import Avatar from './avatar';
import AvatarOverlay from './avatar_overlay';
import AvatarComposite from './avatar_composite';
import AbsoluteTimestamp from './absolute_timestamp';
import RelativeTimestamp from './relative_timestamp';
import DisplayName from './display_name';
import StatusContent from './status_content';
@ -19,7 +20,7 @@ import classNames from 'classnames';
import Icon from 'mastodon/components/icon';
import EmojiReactionsBar from 'mastodon/components/emoji_reactions_bar';
import PictureInPicturePlaceholder from 'mastodon/components/picture_in_picture_placeholder';
import { displayMedia, enableReaction, compactReaction, show_reply_tree_button, enableStatusReference } from 'mastodon/initial_state';
import { displayMedia, enableReaction, compactReaction, show_reply_tree_button, enableStatusReference, disableRelativeTime } from 'mastodon/initial_state';
import { List as ImmutableList } from 'immutable';
// We use the component (and not the container) since we do not want
@ -755,7 +756,7 @@ class Status extends ImmutablePureComponent {
{status.get('expires_at') && <span className='status__expiration-time'><time dateTime={expires_at} title={intl.formatDate(expires_date, dateFormatOptions)}><i className="fa fa-clock-o" aria-hidden="true"></i></time></span>}
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener noreferrer'>
{threadMark}
<RelativeTimestamp timestamp={status.get('created_at')} />
{disableRelativeTime ? <AbsoluteTimestamp timestamp={status.get('created_at')} /> : <RelativeTimestamp timestamp={status.get('created_at')} /> }
</a>
<span className='status__visibility-icon'>{visibilityLink}</span>

View file

@ -9,9 +9,10 @@ import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
import AvatarComposite from 'mastodon/components/avatar_composite';
import Permalink from 'mastodon/components/permalink';
import IconButton from 'mastodon/components/icon_button';
import AbsoluteTimestamp from 'mastodon/components/absolute_timestamp';
import RelativeTimestamp from 'mastodon/components/relative_timestamp';
import { HotKeys } from 'react-hotkeys';
import { autoPlayGif } from 'mastodon/initial_state';
import { autoPlayGif, disableRelativeTime } from 'mastodon/initial_state';
import classNames from 'classnames';
const messages = defineMessages({
@ -153,7 +154,8 @@ class Conversation extends ImmutablePureComponent {
<div className='conversation__content'>
<div className='conversation__content__info'>
<div className='conversation__content__relative-time'>
{unread && <span className='conversation__unread' />} <RelativeTimestamp timestamp={lastStatus.get('created_at')} />
{unread && <span className='conversation__unread' />}
{disableRelativeTime ? <AbsoluteTimestamp timestamp={lastStatus.get('created_at')} /> : <RelativeTimestamp timestamp={lastStatus.get('created_at')} /> }
</div>
<div className='conversation__content__names' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>

View file

@ -6,6 +6,7 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import Button from '../../../components/button';
import StatusContent from '../../../components/status_content';
import Avatar from '../../../components/avatar';
import AbsoluteTimestamp from '../../../components/absolute_timestamp';
import RelativeTimestamp from '../../../components/relative_timestamp';
import DisplayName from '../../../components/display_name';
import ImmutablePureComponent from 'react-immutable-pure-component';
@ -14,6 +15,7 @@ import AttachmentList from 'mastodon/components/attachment_list';
import PrivacyDropdown from 'mastodon/features/compose/components/privacy_dropdown';
import classNames from 'classnames';
import { changeBoostPrivacy } from 'mastodon/actions/boosts';
import { disableRelativeTime } from 'mastodon/initial_state';
const messages = defineMessages({
cancel_reblog: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
@ -109,7 +111,9 @@ class BoostModal extends ImmutablePureComponent {
<div className={classNames('status', `status-${status.get('visibility')}`, 'light')}>
<div className='boost-modal__status-header'>
<div className='boost-modal__status-time'>
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener noreferrer'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener noreferrer'>
{disableRelativeTime ? <AbsoluteTimestamp timestamp={status.get('created_at')} /> : <RelativeTimestamp timestamp={status.get('created_at')} /> }
</a>
</div>
<span className='status__visibility-icon'><Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></span>

View file

@ -4,6 +4,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import StatusContent from '../../../components/status_content';
import Avatar from '../../../components/avatar';
import AbsoluteTimestamp from '../../../components/absolute_timestamp';
import RelativeTimestamp from '../../../components/relative_timestamp';
import DisplayName from '../../../components/display_name';
@ -12,7 +13,7 @@ import { supportsPassiveEvents } from 'detect-passive-events';
import { EmojiPicker as EmojiPickerAsync } from '../util/async-components';
import { buildCustomEmojis, categoriesFromEmojis } from '../../emoji/emoji';
import { assetHost } from 'mastodon/utils/config';
import { pickerEmojiSize } from 'mastodon/initial_state';
import { pickerEmojiSize, disableRelativeTime } from 'mastodon/initial_state';
const messages = defineMessages({
emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' },
@ -233,7 +234,7 @@ export default class ReactionModal extends ImmutablePureComponent {
<div className='boost-modal__status-header'>
<div className='boost-modal__status-time'>
<a href={this.props.status.get('url')} className='status__relative-time' target='_blank' rel='noopener noreferrer'>
<RelativeTimestamp timestamp={this.props.status.get('created_at')} />
{disableRelativeTime ? <AbsoluteTimestamp timestamp={this.props.status.get('created_at')} /> : <RelativeTimestamp timestamp={this.props.status.get('created_at')} /> }
</a>
</div>

View file

@ -65,6 +65,7 @@ export const disableBlock = getMeta('disable_block');
export const disableDomainBlock = getMeta('disable_domain_block');
export const disableClearAllNotifications = getMeta('disable_clear_all_notifications');
export const disableAccountDelete = getMeta('disable_account_delete');
export const disableRelativeTime = getMeta('disable_relative_time');
export const maxChars = initialState?.max_toot_chars ?? 500;

View file

@ -89,6 +89,7 @@ class UserSettingsDecorator
disable_domain_block
disable_clear_all_notifications
disable_account_delete
disable_relative_time
).freeze
STRING_KEYS = %w(

View file

@ -145,6 +145,7 @@ class User < ApplicationRecord
:show_reload_button, :default_column_width,
:disable_post, :disable_reactions, :disable_follow, :disable_unfollow, :disable_block, :disable_domain_block, :disable_clear_all_notifications, :disable_account_delete,
:prohibited_visibilities, :prohibited_words,
:disable_relative_time,
to: :settings, prefix: :setting, allow_nil: false

View file

@ -86,6 +86,7 @@ class InitialStateSerializer < ActiveModel::Serializer
store[:disable_domain_block] = object.current_account.user.setting_disable_domain_block
store[:disable_clear_all_notifications] = object.current_account.user.setting_disable_clear_all_notifications
store[:disable_account_delete] = object.current_account.user.setting_disable_account_delete
store[:disable_relative_time] = object.current_account.user.setting_disable_relative_time
else
store[:auto_play_gif] = Setting.auto_play_gif
store[:display_media] = Setting.display_media

View file

@ -116,8 +116,8 @@
.fields-group
= f.input :setting_show_reload_button, as: :boolean, wrapper: :with_label, fedibird_features: true
-# .fields-group
-# = f.input :setting_show_target, as: :boolean, wrapper: :with_label
.fields-group
= f.input :setting_disable_relative_time, as: :boolean, wrapper: :with_label, fedibird_features: true
%h4= t 'preferences.public_timelines'

View file

@ -78,6 +78,7 @@ en:
setting_disable_joke_appearance: Disable April Fools' Day and other joke functions
setting_disable_post: Restricts you from accidentally posting
setting_disable_reactions: Restrict reactions for favorites, boosts ,emoji reactions and polls
setting_disable_relative_time:
setting_disable_unfollow: Restricts you from accidentally unfollowing
setting_display_media_default: Hide media marked as sensitive
setting_display_media_hide_all: Always hide media
@ -261,6 +262,7 @@ en:
setting_disable_joke_appearance: Disable joke feature to change appearance
setting_disable_post: Disable post
setting_disable_reactions: Disable reactions
setting_disable_relative_time: Disable relative time on posts
setting_disable_swiping: Disable swiping motions
setting_disable_unfollow: Disable unfollow
setting_display_media: Media display

View file

@ -74,6 +74,7 @@ ja:
setting_disable_joke_appearance: エイプリルフール等のジョーク機能を無効にします
setting_disable_post: 誤って投稿することを防ぎます
setting_disable_reactions: 誤ってお気に入り・ブースト・絵文字リアクション・投票することを防ぎます
setting_disable_relative_time: 投稿日時を相対表示する機能を無効にし、日時をそのまま表示します
setting_disable_unfollow: 誤ってフォロー解除することを防ぎます
setting_display_media_default: 閲覧注意としてマークされたメディアは隠す
setting_display_media_hide_all: メディアを常に隠す
@ -257,6 +258,7 @@ ja:
setting_disable_joke_appearance: ジョーク機能による見た目の変更を無効にする
setting_disable_post: 投稿を無効にする
setting_disable_reactions: リアクションを無効にする
setting_disable_relative_time: 投稿の相対日時を無効にする
setting_disable_swiping: スワイプでの切り替えを無効にする
setting_disable_unfollow: フォロー解除を無効にする
setting_display_media: メディアの表示

View file

@ -128,6 +128,7 @@ defaults: &defaults
disable_account_delete: false
prohibited_visibilities: []
prohibited_words: ''
disable_relative_time: false
development:
<<: *defaults