Add default expires date setting

This commit is contained in:
noellabo 2022-09-28 11:33:51 +09:00
parent 695f9a7d08
commit dc138ccb32
11 changed files with 77 additions and 23 deletions

View file

@ -20,6 +20,7 @@ class Api::V1::StatusesController < Api::BaseController
# conversations as quasi-unlimited, it would be too much work to render more
# than this anyway
CONTEXT_LIMIT = 4_096
DURATION_RE = /^(?:(?<year>\d+)y)?(?:(?<month>\d+)m(?=[\do])o?)?(?:(?<day>\d+)d)?(?:(?<hour>\d+)h)?(?:(?<minute>\d+)m)?$/
def index
@statuses = cache_collection(@statuses, Status)
@ -128,7 +129,18 @@ class Api::V1::StatusesController < Api::BaseController
end
def set_expire
@expires_at = status_params[:expires_at] || (status_params[:expires_in].blank? ? nil : (@scheduled_at || Time.now.utc) + status_params[:expires_in].to_i.seconds)
expires_in =
if status_params.has_key?(:expires_in)
status_params[:expires_in].blank? ? nil : status_params[:expires_in].to_i.seconds
elsif (match = current_user.setting_default_expires_in.match(DURATION_RE))
year, month, day, hour, minute = match.to_a.values_at(1,2,3,4,5).map(&:to_i)
seconds = (year.years + month.months + day.days + hour.hours + minute.minutes).to_i.seconds
seconds < 1.minutes ? nil : seconds
else
nil
end
@expires_at = status_params[:expires_at] || (expires_in.nil? ? nil : (@scheduled_at || Time.now.utc) + expires_in)
end
def status_ids

View file

@ -97,6 +97,8 @@ class Settings::PreferencesController < Settings::BaseController
:setting_show_reload_button,
:setting_default_column_width,
:setting_confirm_domain_block,
:setting_default_expires_in,
:setting_default_expires_action,
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

@ -213,7 +213,7 @@
"datetime_button.add_datetime": "日時を追加",
"datetime_button.remove_datetime": "日時を削除",
"datetime.expires": "終了日時",
"datetime.expires_action.mark": "保持",
"datetime.expires_action.mark": "期限切れマーク",
"datetime.expires_action.delete": "削除",
"datetime.open_calendar": "カレンダーを開く",
"datetime.placeholder": "日付か期間を入力",

View file

@ -160,10 +160,10 @@ const clearAll = state => {
map.update('media_attachments', list => list.clear());
map.set('poll', null);
map.set('idempotencyKey', uuid());
map.set('datetime_form', null);
map.set('datetime_form', state.get('default_expires_in') ? true : null);
map.set('scheduled', null);
map.set('expires', null);
map.set('expires_action', 'mark');
map.set('expires', state.get('default_expires_in', null));
map.set('expires_action', state.get('default_expires_action', 'mark'));
map.update('references', set => set.clear());
map.update('context_references', set => set.clear());
});
@ -410,10 +410,10 @@ export default function compose(state = initialState, action) {
map.set('caretPosition', null);
map.set('preselectDate', new Date());
map.set('idempotencyKey', uuid());
map.set('datetime_form', null);
map.set('datetime_form', state.get('default_expires_in') ? true : null);
map.set('scheduled', null);
map.set('expires', null);
map.set('expires_action', 'mark');
map.set('expires', state.get('default_expires_in', null));
map.set('expires_action', state.get('default_expires_action', 'mark'));
map.update('context_references', set => set.clear().concat(action.context_references));
if (action.status.get('spoiler_text').length > 0) {
@ -438,10 +438,10 @@ export default function compose(state = initialState, action) {
map.set('focusDate', new Date());
map.set('preselectDate', new Date());
map.set('idempotencyKey', uuid());
map.set('datetime_form', null);
map.set('datetime_form', state.get('default_expires_in') ? true : null);
map.set('scheduled', null);
map.set('expires', null);
map.set('expires_action', 'mark');
map.set('expires', state.get('default_expires_in', null));
map.set('expires_action', state.get('default_expires_action', 'mark'));
map.update('context_references', set => set.clear().add(action.status.get('id')));
if (action.status.get('spoiler_text').length > 0) {
@ -468,10 +468,10 @@ export default function compose(state = initialState, action) {
map.set('circle_id', null);
map.set('poll', null);
map.set('idempotencyKey', uuid());
map.set('datetime_form', null);
map.set('datetime_form', state.get('default_expires_in') ? true : null);
map.set('scheduled', null);
map.set('expires', null);
map.set('expires_action', 'mark');
map.set('expires', state.get('default_expires_in', null));
map.set('expires_action', state.get('default_expires_action', 'mark'));
map.update('context_references', set => set.clear());
if (action.type == COMPOSE_RESET) {
map.update('references', set => set.clear());
@ -575,6 +575,8 @@ export default function compose(state = initialState, action) {
}));
case REDRAFT:
return state.withMutations(map => {
const default_expires_in_exist = state.get('default_expires_in') ? true : null;
map.set('text', action.raw_text || unescapeHTML(rejectQuoteAltText(expandMentions(action.status))));
map.set('in_reply_to', action.status.get('in_reply_to_id'));
map.set('quote_from', action.status.getIn(['quote', 'id']));
@ -588,10 +590,10 @@ export default function compose(state = initialState, action) {
map.set('caretPosition', null);
map.set('idempotencyKey', uuid());
map.set('sensitive', action.status.get('sensitive'));
map.set('datetime_form', !!action.status.get('scheduled_at') || !!action.status.get('expires_at') ? true : null);
map.set('datetime_form', !!action.status.get('scheduled_at') || !!action.status.get('expires_at') ? true : default_expires_in_exist);
map.set('scheduled', action.status.get('scheduled_at'));
map.set('expires', action.status.get('expires_at') ? format(parseISO(action.status.get('expires_at')), 'yyyy-MM-dd HH:mm') : null);
map.set('expires_action', action.status.get('expires_action') ?? 'mark');
map.set('expires', action.status.get('expires_at') ? format(parseISO(action.status.get('expires_at')), 'yyyy-MM-dd HH:mm') : state.get('default_expires_in', null));
map.set('expires_action', action.status.get('expires_action') ?? state.get('default_expires_action', 'mark'));
map.update('references', set => set.clear().concat(action.status.get('status_reference_ids')));
map.update('context_references', set => set.clear().concat(action.context_references));

View file

@ -91,7 +91,9 @@ class UserSettingsDecorator
user.settings['default_search_searchability'] = default_search_searchability_preference if change?('setting_default_search_searchability')
user.settings['show_reload_button'] = show_reload_button_preference if change?('setting_show_reload_button')
user.settings['default_column_width'] = default_column_width_preference if change?('setting_default_column_width')
end
user.settings['default_expires_in'] = default_expires_in_preference if change?('setting_default_expires_in')
user.settings['default_expires_action'] = default_expires_action_preference if change?('setting_default_expires_action')
end
def merged_notification_emails
user.settings['notification_emails'].merge coerced_settings('notification_emails').to_h
@ -341,6 +343,14 @@ class UserSettingsDecorator
settings['setting_default_column_width']
end
def default_expires_in_preference
settings['setting_default_expires_in']
end
def default_expires_action_preference
settings['setting_default_expires_action']
end
def boolean_cast_setting(key)
ActiveModel::Type::Boolean.new.cast(settings[key])
end

View file

@ -141,7 +141,7 @@ class User < ApplicationRecord
:multi_column_customize, :multi_column_content_font_size, :multi_column_info_font_size, :multi_column_content_emoji_reaction_size,
:mobile_customize, :mobile_content_font_size, :mobile_info_font_size, :mobile_content_emoji_reaction_size,
:hide_bot_on_public_timeline, :confirm_follow_from_bot,
:default_search_searchability,
:default_search_searchability, :default_expires_in, :default_expires_action,
:show_reload_button, :default_column_width,
to: :settings, prefix: :setting, allow_nil: false

View file

@ -89,10 +89,12 @@ class InitialStateSerializer < ActiveModel::Serializer
store = {}
if object.current_account
store[:me] = object.current_account.id.to_s
store[:default_privacy] = object.visibility || object.current_account.user.setting_default_privacy
store[:default_searchability] = object.current_account.searchability
store[:default_sensitive] = object.current_account.user.setting_default_sensitive
store[:me] = object.current_account.id.to_s
store[:default_privacy] = object.visibility || object.current_account.user.setting_default_privacy
store[:default_searchability] = object.current_account.searchability
store[:default_sensitive] = object.current_account.user.setting_default_sensitive
store[:default_expires_in] = object.current_account.user.setting_default_expires_in
store[:default_expires_action] = object.current_account.user.setting_default_expires_action
end
store[:text] = object.text if object.text

View file

@ -40,6 +40,14 @@
.fields-group
= f.input :setting_show_application, as: :boolean, wrapper: :with_label, recommended: true
.fields-row
.fields-group.fields-row__column.fields-row__column-6
= f.input :setting_default_expires_in, wrapper: :with_label, input_html: { maxlength: 30, pattern: '^(?:(\d+)y)?(?:(\d+)m(?=[\do])o?)?(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?$' }, fedibird_features: true
.fields-group.fields-row__column.fields-row__column-6
%span.fedibird_features.float Fedibird
= f.input :setting_default_expires_action, collection: ['mark', 'delete'], label_method: lambda { |item| t("simple_form.labels.defaults.setting_default_expires_action_#{item}") }, hint: t("simple_form.hints.defaults.setting_default_expires_action"), as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label
%h4= t 'preferences.searching_defaults'
.fields-group

View file

@ -62,6 +62,12 @@ en:
setting_default_column_width_x100: 100% - Fix to Mastodon standard column width
setting_default_column_width_x125: 125% - Fix to 125% of standard width
setting_default_column_width_x150: 150% - Fix to 150% of standard width
setting_default_expires_action: |
Submissions whose publication period has ended will be marked or deleted.
Marked posts will no longer be visible, but only the poster and those who reacted (favourite, emoji reaction, bookmark) to the post will be able to see it.
setting_default_expires_in: |
Specify the end date and time as the starting point of the posting date and time.
The format is 1y2mo3d4h5m (1 year, 2 months, 3 days, 4 hours, 5 minutes later).
setting_default_search_searchability: For clients that do not support advanced range settings, switch the settings here. Mastodon's standard behavior is "Reacted-users-only". Targeting "Public" makes it easier to discover unknown information, but if the results are noisy, narrowing the search range is effective.
setting_default_sensitive: Sensitive media is hidden by default and can be revealed with a click
setting_disable_joke_appearance: Disable April Fools' Day and other joke functions
@ -226,6 +232,10 @@ en:
setting_content_font_size: Content font size
setting_crop_images: Crop images in non-expanded posts to 16x9
setting_default_column_width: Default column width
setting_default_expires_action: Default expiry action
setting_default_expires_action_mark: Mark as expired
setting_default_expires_action_delete: Delete
setting_default_expires_in: Default expiry duration
setting_default_language: Posting language
setting_default_privacy: Posting privacy
setting_default_search_searchability: Search range

View file

@ -62,6 +62,8 @@ ja:
setting_default_column_width_x100: 100% - Mastodon標準のカラム幅に固定します
setting_default_column_width_x125: 125% - 標準の125%の幅に固定します
setting_default_column_width_x150: 150% - 標準の150%の幅に固定します
setting_default_expires_action: 公開期間が終了した投稿には、マークが付けられるか、削除されます。マークされた投稿は見えなくなり、投稿者とその投稿に反応(お気に入り・絵文字リアクション・ブックマーク)した人だけが見ることができるようになります。
setting_default_expires_in: 投稿日時を起点とする終了日時を指定します。書式は1y2mo3d4h5m1年2ヶ月3日4時間5分後です。
setting_default_search_searchability: 範囲の詳細設定に対応していないクライアントでは、ここで設定を切り替えてください。Mastodonの標準動作は『リアクション限定』です。『公開』を対象にすると未知の情報を発見しやすくなりますが、結果にイズが多い場合は検索範囲を狭めると効果的です。
setting_default_sensitive: 閲覧注意状態のメディアはデフォルトでは内容が伏せられ、クリックして初めて閲覧できるようになります
setting_disable_joke_appearance: エイプリルフール等のジョーク機能を無効にします
@ -226,6 +228,10 @@ ja:
setting_content_font_size: 投稿のフォントサイズ
setting_crop_images: 投稿の詳細以外では画像を16:9に切り抜く
setting_default_column_width: デフォルトのカラム幅
setting_default_expires_action: デフォルトの終了時アクション
setting_default_expires_action_mark: 期限切れマーク
setting_default_expires_action_delete: 削除
setting_default_expires_in: デフォルトの終了日時
setting_default_language: 投稿する言語
setting_default_privacy: 投稿の公開範囲
setting_default_search_searchability: 検索の対象とする範囲

View file

@ -112,6 +112,8 @@ defaults: &defaults
default_search_searchability: 'private'
show_reload_button: true
default_column_width: 'x100'
default_expires_in: ''
default_expires_action: 'mark'
development:
<<: *defaults