diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/boot/after_store.js | 2 | ||||
| -rw-r--r-- | src/components/registration/registration.js | 30 | ||||
| -rw-r--r-- | src/components/registration/registration.vue | 34 | ||||
| -rw-r--r-- | src/components/settings_modal/tabs/profile_tab.js | 6 | ||||
| -rw-r--r-- | src/components/settings_modal/tabs/profile_tab.scss | 5 | ||||
| -rw-r--r-- | src/components/settings_modal/tabs/profile_tab.vue | 12 | ||||
| -rw-r--r-- | src/components/user_profile/user_profile.js | 11 | ||||
| -rw-r--r-- | src/components/user_profile/user_profile.vue | 14 | ||||
| -rw-r--r-- | src/i18n/en.json | 13 | ||||
| -rw-r--r-- | src/modules/instance.js | 2 | ||||
| -rw-r--r-- | src/services/entity_normalizer/entity_normalizer.service.js | 3 |
11 files changed, 126 insertions, 6 deletions
diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 1fa9dd2a..d2e7f488 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -60,6 +60,8 @@ const getInstanceConfig = async ({ store }) => { store.dispatch('setInstanceOption', { name: 'textlimit', value: textlimit }) store.dispatch('setInstanceOption', { name: 'accountApprovalRequired', value: data.approval_required }) + store.dispatch('setInstanceOption', { name: 'birthdayRequired', value: !!data.pleroma.metadata.birthday_required }) + store.dispatch('setInstanceOption', { name: 'birthdayMinAge', value: data.pleroma.metadata.birthday_min_age || 0 }) if (vapidPublicKey) { store.dispatch('setInstanceOption', { name: 'vapidPublicKey', value: vapidPublicKey }) diff --git a/src/components/registration/registration.js b/src/components/registration/registration.js index 6eb316d0..22ca6ad6 100644 --- a/src/components/registration/registration.js +++ b/src/components/registration/registration.js @@ -3,6 +3,7 @@ import { required, requiredIf, sameAs } from '@vuelidate/validators' import { mapActions, mapState } from 'vuex' import InterfaceLanguageSwitcher from '../interface_language_switcher/interface_language_switcher.vue' import localeService from '../../services/locale/locale.service.js' +import { DAY } from 'src/services/date_utils/date_utils.js' const registration = { setup () { return { v$: useVuelidate() } }, @@ -13,6 +14,7 @@ const registration = { username: '', password: '', confirm: '', + birthday: '', reason: '', language: '' }, @@ -32,6 +34,12 @@ const registration = { required, sameAs: sameAs(this.user.password) }, + birthday: { + required: requiredIf(() => this.birthdayRequired), + maxValue: value => { + return !this.birthdayRequired || new Date(value).getTime() <= this.birthdayMin.getTime() + } + }, reason: { required: requiredIf(() => this.accountApprovalRequired) }, language: {} } @@ -52,6 +60,24 @@ const registration = { reasonPlaceholder () { return this.replaceNewlines(this.$t('registration.reason_placeholder')) }, + birthdayMin () { + const minAge = this.birthdayMinAge + const today = new Date() + today.setUTCMilliseconds(0) + today.setUTCSeconds(0) + today.setUTCMinutes(0) + today.setUTCHours(0) + const minDate = new Date() + minDate.setTime(today.getTime() - minAge * DAY) + return minDate + }, + birthdayMinAttr () { + return this.birthdayMin.toJSON().replace(/T.+$/, '') + }, + birthdayMinFormatted () { + const browserLocale = localeService.internalToBrowserLocale(this.$i18n.locale) + return this.user.birthday && new Date(Date.parse(this.birthdayMin)).toLocaleDateString(browserLocale, { timeZone: 'UTC', day: 'numeric', month: 'long', year: 'numeric' }) + }, ...mapState({ registrationOpen: (state) => state.instance.registrationOpen, signedIn: (state) => !!state.users.currentUser, @@ -59,7 +85,9 @@ const registration = { serverValidationErrors: (state) => state.users.signUpErrors, termsOfService: (state) => state.instance.tos, accountActivationRequired: (state) => state.instance.accountActivationRequired, - accountApprovalRequired: (state) => state.instance.accountApprovalRequired + accountApprovalRequired: (state) => state.instance.accountApprovalRequired, + birthdayRequired: (state) => state.instance.birthdayRequired, + birthdayMinAge: (state) => state.instance.birthdayMinAge }) }, methods: { diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue index a26162f0..5701b05e 100644 --- a/src/components/registration/registration.vue +++ b/src/components/registration/registration.vue @@ -169,6 +169,40 @@ <div class="form-group" + :class="{ 'form-group--error': v$.user.birthday.$error }" + > + <label + class="form--label" + for="sign-up-birthday" + > + {{ birthdayRequired ? $t('registration.birthday') : $t('registration.birthday_optional') }} + </label> + <input + id="sign-up-birthday" + v-model="user.birthday" + :disabled="isPending" + class="form-control" + type="date" + :max="birthdayRequired ? birthdayMinAttr : undefined" + :aria-required="birthdayRequired" + > + </div> + <div + v-if="v$.user.birthday.$dirty" + class="form-error" + > + <ul> + <li v-if="v$.user.birthday.required.$invalid"> + <span>{{ $t('registration.validations.birthday_required') }}</span> + </li> + <li v-if="v$.user.birthday.maxValue.$invalid"> + <span>{{ $tc('registration.validations.birthday_min_age', { date: birthdayMinFormatted }) }}</span> + </li> + </ul> + </div> + + <div + class="form-group" :class="{ 'form-group--error': v$.user.language.$error }" > <interface-language-switcher diff --git a/src/components/settings_modal/tabs/profile_tab.js b/src/components/settings_modal/tabs/profile_tab.js index b2d8fed2..72d27083 100644 --- a/src/components/settings_modal/tabs/profile_tab.js +++ b/src/components/settings_modal/tabs/profile_tab.js @@ -32,6 +32,8 @@ const ProfileTab = { newName: this.$store.state.users.currentUser.name_unescaped, newBio: unescape(this.$store.state.users.currentUser.description), newLocked: this.$store.state.users.currentUser.locked, + newBirthday: this.$store.state.users.currentUser.birthday, + showBirthday: this.$store.state.users.currentUser.show_birthday, newFields: this.$store.state.users.currentUser.fields.map(field => ({ name: field.name, value: field.value })), showRole: this.$store.state.users.currentUser.show_role, role: this.$store.state.users.currentUser.role, @@ -125,7 +127,9 @@ const ProfileTab = { display_name: this.newName, fields_attributes: this.newFields.filter(el => el != null), bot: this.bot, - show_role: this.showRole + show_role: this.showRole, + birthday: this.newBirthday || '', + show_birthday: this.showBirthday /* eslint-enable camelcase */ } diff --git a/src/components/settings_modal/tabs/profile_tab.scss b/src/components/settings_modal/tabs/profile_tab.scss index 73879192..ee253ffe 100644 --- a/src/components/settings_modal/tabs/profile_tab.scss +++ b/src/components/settings_modal/tabs/profile_tab.scss @@ -129,4 +129,9 @@ padding: 0 0.5em; } } + + .birthday-input { + display: block; + margin-bottom: 1em; + } } diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue index 642d54ca..faa05bcc 100644 --- a/src/components/settings_modal/tabs/profile_tab.vue +++ b/src/components/settings_modal/tabs/profile_tab.vue @@ -35,6 +35,18 @@ </template> </Checkbox> </p> + <div> + <p>{{ $t('settings.birthday.label') }}</p> + <input + id="birthday" + v-model="newBirthday" + type="date" + class="birthday-input" + > + <Checkbox v-model="showBirthday"> + {{ $t('settings.birthday.show_birthday') }} + </Checkbox> + </div> <div v-if="maxFields > 0"> <p>{{ $t('settings.profile_fields.label') }}</p> <div diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index 08adaeab..acb612ed 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -7,13 +7,16 @@ import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' import RichContent from 'src/components/rich_content/rich_content.jsx' import List from '../list/list.vue' import withLoadMore from '../../hocs/with_load_more/with_load_more' +import localeService from 'src/services/locale/locale.service.js' import { library } from '@fortawesome/fontawesome-svg-core' import { - faCircleNotch + faCircleNotch, + faBirthdayCake } from '@fortawesome/free-solid-svg-icons' library.add( - faCircleNotch + faCircleNotch, + faBirthdayCake ) const FollowerList = withLoadMore({ @@ -76,6 +79,10 @@ const UserProfile = { }, followersTabVisible () { return this.isUs || !this.user.hide_followers + }, + formattedBirthday () { + const browserLocale = localeService.internalToBrowserLocale(this.$i18n.locale) + return this.user.birthday && new Date(Date.parse(this.user.birthday)).toLocaleDateString(browserLocale, { timeZone: 'UTC', day: 'numeric', month: 'long', year: 'numeric' }) } }, methods: { diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index 2720f052..c63a303c 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -12,6 +12,16 @@ rounded="top" :has-note-editor="true" /> + <span + v-if="!!user.birthday" + class="user-birthday" + > + <FAIcon + class="fa-old-padding" + icon="birthday-cake" + /> + {{ $t('user_card.birthday', { birthday: formattedBirthday }) }} + </span> <div v-if="user.fields_html && user.fields_html.length > 0" class="user-profile-fields" @@ -149,6 +159,10 @@ // No sticky header on user profile --currentPanelStack: 1; + .user-birthday { + margin: 0 0.75em 0.5em; + } + .user-profile-fields { margin: 0 0.5em; diff --git a/src/i18n/en.json b/src/i18n/en.json index c8de6851..0bdf4051 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -316,9 +316,13 @@ "email_required": "cannot be left blank", "password_required": "cannot be left blank", "password_confirmation_required": "cannot be left blank", - "password_confirmation_match": "should be the same as password" + "password_confirmation_match": "should be the same as password", + "birthday_required": "cannot be left blank", + "birthday_min_age": "must be on or before {date}" }, - "email_language": "In which language do you want to receive emails from the server?" + "email_language": "In which language do you want to receive emails from the server?", + "birthday": "Birthday:", + "birthday_optional": "Birthday (optional):" }, "remote_user_resolver": { "remote_user_resolver": "Remote user resolver", @@ -528,6 +532,10 @@ "name": "Label", "value": "Content" }, + "birthday": { + "label": "Birthday", + "show_birthday": "Show my birthday" + }, "account_privacy": "Privacy", "use_contain_fit": "Don't crop the attachment in thumbnails", "name": "Name", @@ -986,6 +994,7 @@ "hide_repeats": "Hide repeats", "show_repeats": "Show repeats", "bot": "Bot", + "birthday": "Born {birthday}", "admin_menu": { "moderation": "Moderation", "grant_admin": "Grant Admin", diff --git a/src/modules/instance.js b/src/modules/instance.js index c7579162..16f72583 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -116,6 +116,8 @@ const defaultState = { restrictedNicknames: [], safeDM: true, knownDomains: [], + birthdayRequired: false, + birthdayMinAge: 0, // Feature-set, apparently, not everything here is reported... shoutAvailable: false, diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index ea138177..53c3108c 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -125,6 +125,8 @@ export const parseUser = (data) => { output.role = 'member' } + output.birthday = data.pleroma.birthday + if (data.pleroma.privileges) { output.privileges = data.pleroma.privileges } else if (data.pleroma.is_admin) { @@ -162,6 +164,7 @@ export const parseUser = (data) => { output.no_rich_text = data.source.pleroma.no_rich_text output.show_role = data.source.pleroma.show_role output.discoverable = data.source.pleroma.discoverable + output.show_birthday = data.pleroma.show_birthday } } |
