aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/App.js4
-rw-r--r--src/App.scss13
-rw-r--r--src/App.vue2
-rw-r--r--src/boot/after_store.js10
-rw-r--r--src/components/login_form/login_form.js2
-rw-r--r--src/components/mfa_form/recovery_form.js11
-rw-r--r--src/components/mfa_form/totp_form.js11
-rw-r--r--src/components/mobile_nav/mobile_nav.js1
-rw-r--r--src/components/mobile_nav/mobile_nav.vue1
-rw-r--r--src/components/nav_panel/nav_panel.js20
-rw-r--r--src/components/nav_panel/nav_panel.vue22
-rw-r--r--src/components/post_status_form/post_status_form.js4
-rw-r--r--src/components/registration/registration.vue2
-rw-r--r--src/components/side_drawer/side_drawer.js9
-rw-r--r--src/components/side_drawer/side_drawer.vue49
-rw-r--r--src/components/timeline/timeline.vue13
-rw-r--r--src/hocs/with_load_more/with_load_more.js4
-rw-r--r--src/hocs/with_subscription/with_subscription.js4
-rw-r--r--src/modules/auth_flow.js8
-rw-r--r--src/modules/instance.js1
-rw-r--r--src/services/api/api.service.js2
-rw-r--r--src/services/new_api/mfa.js12
22 files changed, 124 insertions, 81 deletions
diff --git a/src/App.js b/src/App.js
index 04a40e30..61b5eec1 100644
--- a/src/App.js
+++ b/src/App.js
@@ -90,6 +90,7 @@ export default {
},
sitename () { return this.$store.state.instance.name },
chat () { return this.$store.state.chat.channel.state === 'joined' },
+ hideSitename () { return this.$store.state.instance.hideSitename },
suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
showInstanceSpecificPanel () {
return this.$store.state.instance.showInstanceSpecificPanel &&
@@ -97,7 +98,8 @@ export default {
this.$store.state.instance.instanceSpecificPanelContent
},
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
- isMobileLayout () { return this.$store.state.interface.mobileLayout }
+ isMobileLayout () { return this.$store.state.interface.mobileLayout },
+ privateMode () { return this.$store.state.instance.private }
},
methods: {
scrollToTop () {
diff --git a/src/App.scss b/src/App.scss
index 925913f2..754ca62e 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -870,3 +870,16 @@ nav {
transform: rotate(359deg);
}
}
+
+.new-status-notification {
+ position:relative;
+ margin-top: -1px;
+ font-size: 1.1em;
+ border-width: 1px 0 0 0;
+ border-style: solid;
+ border-color: var(--border, $fallback--border);
+ padding: 10px;
+ z-index: 1;
+ background-color: $fallback--fg;
+ background-color: var(--panel, $fallback--fg);
+}
diff --git a/src/App.vue b/src/App.vue
index dbe842ec..1b1c2648 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -31,6 +31,7 @@
</div>
<div class="item">
<router-link
+ v-if="!hideSitename"
class="site-name"
:to="{ name: 'root' }"
active-class="home"
@@ -40,6 +41,7 @@
</div>
<div class="item right">
<search-bar
+ v-if="currentUser || !privateMode"
class="nav-icon mobile-hidden"
@toggled="onSearchBarToggled"
@click.stop.native
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
index 226b67d8..228a0497 100644
--- a/src/boot/after_store.js
+++ b/src/boot/after_store.js
@@ -108,6 +108,7 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => {
copyInstanceOption('alwaysShowSubjectInput')
copyInstanceOption('noAttachmentLinks')
copyInstanceOption('showFeaturesPanel')
+ copyInstanceOption('hideSitename')
return store.dispatch('setTheme', config['theme'])
}
@@ -218,12 +219,21 @@ const getNodeInfo = async ({ store }) => {
store.dispatch('setInstanceOption', { name: 'backendVersion', value: software.version })
store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: software.name === 'pleroma' })
+ const priv = metadata.private
+ store.dispatch('setInstanceOption', { name: 'private', value: priv })
+
const frontendVersion = window.___pleromafe_commit_hash
store.dispatch('setInstanceOption', { name: 'frontendVersion', value: frontendVersion })
store.dispatch('setInstanceOption', { name: 'tagPolicyAvailable', value: metadata.federation.mrf_policies.includes('TagPolicy') })
const federation = metadata.federation
store.dispatch('setInstanceOption', { name: 'federationPolicy', value: federation })
+ store.dispatch('setInstanceOption', {
+ name: 'federating',
+ value: typeof federation.enabled === 'undefined'
+ ? true
+ : federation.enabled
+ })
const accounts = metadata.staffAccounts
await resolveStaffAccounts({ store, accounts })
diff --git a/src/components/login_form/login_form.js b/src/components/login_form/login_form.js
index 0b574a04..0d8f1da6 100644
--- a/src/components/login_form/login_form.js
+++ b/src/components/login_form/login_form.js
@@ -58,7 +58,7 @@ const LoginForm = {
).then((result) => {
if (result.error) {
if (result.error === 'mfa_required') {
- this.requireMFA({ app: app, settings: result })
+ this.requireMFA({ settings: result })
} else if (result.identifier === 'password_reset_required') {
this.$router.push({ name: 'password-reset', params: { passwordResetRequested: true } })
} else {
diff --git a/src/components/mfa_form/recovery_form.js b/src/components/mfa_form/recovery_form.js
index 7a3cc22d..b25c65dd 100644
--- a/src/components/mfa_form/recovery_form.js
+++ b/src/components/mfa_form/recovery_form.js
@@ -8,18 +8,23 @@ export default {
}),
computed: {
...mapGetters({
- authApp: 'authFlow/app',
authSettings: 'authFlow/settings'
}),
- ...mapState({ instance: 'instance' })
+ ...mapState({
+ instance: 'instance',
+ oauth: 'oauth'
+ })
},
methods: {
...mapMutations('authFlow', ['requireTOTP', 'abortMFA']),
...mapActions({ login: 'authFlow/login' }),
clearError () { this.error = false },
submit () {
+ const { clientId, clientSecret } = this.oauth
+
const data = {
- app: this.authApp,
+ clientId,
+ clientSecret,
instance: this.instance.server,
mfaToken: this.authSettings.mfa_token,
code: this.code
diff --git a/src/components/mfa_form/totp_form.js b/src/components/mfa_form/totp_form.js
index 778bf8dc..b774f2d0 100644
--- a/src/components/mfa_form/totp_form.js
+++ b/src/components/mfa_form/totp_form.js
@@ -7,18 +7,23 @@ export default {
}),
computed: {
...mapGetters({
- authApp: 'authFlow/app',
authSettings: 'authFlow/settings'
}),
- ...mapState({ instance: 'instance' })
+ ...mapState({
+ instance: 'instance',
+ oauth: 'oauth'
+ })
},
methods: {
...mapMutations('authFlow', ['requireRecovery', 'abortMFA']),
...mapActions({ login: 'authFlow/login' }),
clearError () { this.error = false },
submit () {
+ const { clientId, clientSecret } = this.oauth
+
const data = {
- app: this.authApp,
+ clientId,
+ clientSecret,
instance: this.instance.server,
mfaToken: this.authSettings.mfa_token,
code: this.code
diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js
index 5a90c31f..c1166a0c 100644
--- a/src/components/mobile_nav/mobile_nav.js
+++ b/src/components/mobile_nav/mobile_nav.js
@@ -29,6 +29,7 @@ const MobileNav = {
unseenNotificationsCount () {
return this.unseenNotifications.length
},
+ hideSitename () { return this.$store.state.instance.hideSitename },
sitename () { return this.$store.state.instance.name }
},
methods: {
diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue
index d1c24e56..51f1d636 100644
--- a/src/components/mobile_nav/mobile_nav.vue
+++ b/src/components/mobile_nav/mobile_nav.vue
@@ -17,6 +17,7 @@
<i class="button-icon icon-menu" />
</a>
<router-link
+ v-if="!hideSitename"
class="site-name"
:to="{ name: 'root' }"
active-class="home"
diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js
index 7f783acb..d9268585 100644
--- a/src/components/nav_panel/nav_panel.js
+++ b/src/components/nav_panel/nav_panel.js
@@ -1,20 +1,18 @@
+import { mapState } from 'vuex'
+
const NavPanel = {
created () {
if (this.currentUser && this.currentUser.locked) {
this.$store.dispatch('startFetchingFollowRequest')
}
},
- computed: {
- currentUser () {
- return this.$store.state.users.currentUser
- },
- chat () {
- return this.$store.state.chat.channel
- },
- followRequestCount () {
- return this.$store.state.api.followRequests.length
- }
- }
+ computed: mapState({
+ currentUser: state => state.users.currentUser,
+ chat: state => state.chat.channel,
+ followRequestCount: state => state.api.followRequests.length,
+ privateMode: state => state.instance.private,
+ federating: state => state.instance.federating
+ })
}
export default NavPanel
diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index 28589bb1..034259d9 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -4,22 +4,22 @@
<ul>
<li v-if="currentUser">
<router-link :to="{ name: 'friends' }">
- {{ $t("nav.timeline") }}
+ <i class="button-icon icon-home-2" /> {{ $t("nav.timeline") }}
</router-link>
</li>
<li v-if="currentUser">
<router-link :to="{ name: 'interactions', params: { username: currentUser.screen_name } }">
- {{ $t("nav.interactions") }}
+ <i class="button-icon icon-bell-alt" /> {{ $t("nav.interactions") }}
</router-link>
</li>
<li v-if="currentUser">
<router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }">
- {{ $t("nav.dms") }}
+ <i class="button-icon icon-mail-alt" /> {{ $t("nav.dms") }}
</router-link>
</li>
<li v-if="currentUser && currentUser.locked">
<router-link :to="{ name: 'friend-requests' }">
- {{ $t("nav.friend_requests") }}
+ <i class="button-icon icon-user-plus" /> {{ $t("nav.friend_requests") }}
<span
v-if="followRequestCount > 0"
class="badge follow-request-count"
@@ -28,19 +28,19 @@
</span>
</router-link>
</li>
- <li>
+ <li v-if="currentUser || !privateMode">
<router-link :to="{ name: 'public-timeline' }">
- {{ $t("nav.public_tl") }}
+ <i class="button-icon icon-users" /> {{ $t("nav.public_tl") }}
</router-link>
</li>
- <li>
+ <li v-if="federating && !privateMode">
<router-link :to="{ name: 'public-external-timeline' }">
- {{ $t("nav.twkn") }}
+ <i class="button-icon icon-globe" /> {{ $t("nav.twkn") }}
</router-link>
</li>
<li>
<router-link :to="{ name: 'about' }">
- {{ $t("nav.about") }}
+ <i class="button-icon icon-info-circled" /> {{ $t("nav.about") }}
</router-link>
</li>
</ul>
@@ -113,4 +113,8 @@
}
}
}
+
+.nav-panel .button-icon:before {
+ width: 1.1em;
+}
</style>
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index af6299e4..74067fef 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -169,9 +169,7 @@ const PostStatusForm = {
if (this.submitDisabled) { return }
if (this.newStatus.status === '') {
- if (this.newStatus.files.length > 0) {
- this.newStatus.status = '\u200b' // hack
- } else {
+ if (this.newStatus.files.length === 0) {
this.error = 'Cannot post an empty status with no files'
return
}
diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue
index 5bb06a4f..222b67a8 100644
--- a/src/components/registration/registration.vue
+++ b/src/components/registration/registration.vue
@@ -172,7 +172,7 @@
for="captcha-label"
>{{ $t('captcha') }}</label>
- <template v-if="captcha.type == 'kocaptcha'">
+ <template v-if="['kocaptcha', 'native'].includes(captcha.type)">
<img
:src="captcha.url"
@click="setCaptcha"
diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js
index 0188cf3e..2534eb8f 100644
--- a/src/components/side_drawer/side_drawer.js
+++ b/src/components/side_drawer/side_drawer.js
@@ -33,11 +33,20 @@ const SideDrawer = {
logo () {
return this.$store.state.instance.logo
},
+ hideSitename () {
+ return this.$store.state.instance.hideSitename
+ },
sitename () {
return this.$store.state.instance.name
},
followRequestCount () {
return this.$store.state.api.followRequests.length
+ },
+ privateMode () {
+ return this.$store.state.instance.private
+ },
+ federating () {
+ return this.$store.state.instance.federating
}
},
methods: {
diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue
index 214b8e0c..3fba9058 100644
--- a/src/components/side_drawer/side_drawer.vue
+++ b/src/components/side_drawer/side_drawer.vue
@@ -27,7 +27,7 @@
class="side-drawer-logo-wrapper"
>
<img :src="logo">
- <span>{{ sitename }}</span>
+ <span v-if="!hideSitename">{{ sitename }}</span>
</div>
</div>
<ul>
@@ -36,7 +36,7 @@
@click="toggleDrawer"
>
<router-link :to="{ name: 'login' }">
- {{ $t("login.login") }}
+ <i class="button-icon icon-login" /> {{ $t("login.login") }}
</router-link>
</li>
<li
@@ -44,7 +44,7 @@
@click="toggleDrawer"
>
<router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }">
- {{ $t("nav.dms") }}
+ <i class="button-icon icon-mail-alt" /> {{ $t("nav.dms") }}
</router-link>
</li>
<li
@@ -52,7 +52,7 @@
@click="toggleDrawer"
>
<router-link :to="{ name: 'interactions', params: { username: currentUser.screen_name } }">
- {{ $t("nav.interactions") }}
+ <i class="button-icon icon-bell-alt" /> {{ $t("nav.interactions") }}
</router-link>
</li>
</ul>
@@ -62,7 +62,7 @@
@click="toggleDrawer"
>
<router-link :to="{ name: 'friends' }">
- {{ $t("nav.timeline") }}
+ <i class="button-icon icon-home-2" /> {{ $t("nav.timeline") }}
</router-link>
</li>
<li
@@ -70,7 +70,7 @@
@click="toggleDrawer"
>
<router-link to="/friend-requests">
- {{ $t("nav.friend_requests") }}
+ <i class="button-icon icon-user-plus" /> {{ $t("nav.friend_requests") }}
<span
v-if="followRequestCount > 0"
class="badge follow-request-count"
@@ -79,14 +79,20 @@
</span>
</router-link>
</li>
- <li @click="toggleDrawer">
+ <li
+ v-if="currentUser || !privateMode"
+ @click="toggleDrawer"
+ >
<router-link to="/main/public">
- {{ $t("nav.public_tl") }}
+ <i class="button-icon icon-users" /> {{ $t("nav.public_tl") }}
</router-link>
</li>
- <li @click="toggleDrawer">
+ <li
+ v-if="federating && !privateMode"
+ @click="toggleDrawer"
+ >
<router-link to="/main/all">
- {{ $t("nav.twkn") }}
+ <i class="button-icon icon-globe" /> {{ $t("nav.twkn") }}
</router-link>
</li>
<li
@@ -94,14 +100,17 @@
@click="toggleDrawer"
>
<router-link :to="{ name: 'chat' }">
- {{ $t("nav.chat") }}
+ <i class="button-icon icon-chat" /> {{ $t("nav.chat") }}
</router-link>
</li>
</ul>
<ul>
- <li @click="toggleDrawer">
+ <li
+ v-if="currentUser || !privateMode"
+ @click="toggleDrawer"
+ >
<router-link :to="{ name: 'search' }">
- {{ $t("nav.search") }}
+ <i class="button-icon icon-search" /> {{ $t("nav.search") }}
</router-link>
</li>
<li
@@ -109,17 +118,17 @@
@click="toggleDrawer"
>
<router-link :to="{ name: 'who-to-follow' }">
- {{ $t("nav.who_to_follow") }}
+ <i class="button-icon icon-user-plus" /> {{ $t("nav.who_to_follow") }}
</router-link>
</li>
<li @click="toggleDrawer">
<router-link :to="{ name: 'settings' }">
- {{ $t("settings.settings") }}
+ <i class="button-icon icon-cog" /> {{ $t("settings.settings") }}
</router-link>
</li>
<li @click="toggleDrawer">
<router-link :to="{ name: 'about'}">
- {{ $t("nav.about") }}
+ <i class="button-icon icon-info-circled" /> {{ $t("nav.about") }}
</router-link>
</li>
<li
@@ -130,7 +139,7 @@
href="/pleroma/admin/#/login-pleroma"
target="_blank"
>
- {{ $t("nav.administration") }}
+ <i class="button-icon icon-gauge" /> {{ $t("nav.administration") }}
</a>
</li>
<li
@@ -141,7 +150,7 @@
href="#"
@click="doLogout"
>
- {{ $t("login.logout") }}
+ <i class="button-icon icon-logout" /> {{ $t("login.logout") }}
</a>
</li>
</ul>
@@ -215,6 +224,10 @@
box-shadow: var(--panelShadow);
background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg);
+
+ .button-icon:before {
+ width: 1.1em;
+ }
}
.side-drawer-logo-wrapper {
diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue
index bb4ab379..9777bd0c 100644
--- a/src/components/timeline/timeline.vue
+++ b/src/components/timeline/timeline.vue
@@ -106,17 +106,4 @@
opacity: 1;
}
}
-
-.new-status-notification {
- position:relative;
- margin-top: -1px;
- font-size: 1.1em;
- border-width: 1px 0 0 0;
- border-style: solid;
- border-color: var(--border, $fallback--border);
- padding: 10px;
- z-index: 1;
- background-color: $fallback--fg;
- background-color: var(--panel, $fallback--fg);
-}
</style>
diff --git a/src/hocs/with_load_more/with_load_more.js b/src/hocs/with_load_more/with_load_more.js
index 1e1b2a74..6142f513 100644
--- a/src/hocs/with_load_more/with_load_more.js
+++ b/src/hocs/with_load_more/with_load_more.js
@@ -65,7 +65,7 @@ const withLoadMore = ({
}
}
},
- render (createElement) {
+ render (h) {
const props = {
props: {
...this.$props,
@@ -74,7 +74,7 @@ const withLoadMore = ({
on: this.$listeners,
scopedSlots: this.$scopedSlots
}
- const children = Object.entries(this.$slots).map(([key, value]) => createElement('template', { slot: key }, value))
+ const children = Object.entries(this.$slots).map(([key, value]) => h('template', { slot: key }, value))
return (
<div class="with-load-more">
<WrappedComponent {...props}>
diff --git a/src/hocs/with_subscription/with_subscription.js b/src/hocs/with_subscription/with_subscription.js
index 91fc4cca..1775adcb 100644
--- a/src/hocs/with_subscription/with_subscription.js
+++ b/src/hocs/with_subscription/with_subscription.js
@@ -49,7 +49,7 @@ const withSubscription = ({
}
}
},
- render (createElement) {
+ render (h) {
if (!this.error && !this.loading) {
const props = {
props: {
@@ -59,7 +59,7 @@ const withSubscription = ({
on: this.$listeners,
scopedSlots: this.$scopedSlots
}
- const children = Object.entries(this.$slots).map(([key, value]) => createElement('template', { slot: key }, value))
+ const children = Object.entries(this.$slots).map(([key, value]) => h('template', { slot: key }, value))
return (
<div class="with-subscription">
<WrappedComponent {...props}>
diff --git a/src/modules/auth_flow.js b/src/modules/auth_flow.js
index d0a90feb..956d40e8 100644
--- a/src/modules/auth_flow.js
+++ b/src/modules/auth_flow.js
@@ -7,7 +7,6 @@ const RECOVERY_STRATEGY = 'recovery'
// initial state
const state = {
- app: null,
settings: {},
strategy: PASSWORD_STRATEGY,
initStrategy: PASSWORD_STRATEGY // default strategy from config
@@ -16,14 +15,10 @@ const state = {
const resetState = (state) => {
state.strategy = state.initStrategy
state.settings = {}
- state.app = null
}
// getters
const getters = {
- app: (state, getters) => {
- return state.app
- },
settings: (state, getters) => {
return state.settings
},
@@ -55,9 +50,8 @@ const mutations = {
requireToken (state) {
state.strategy = TOKEN_STRATEGY
},
- requireMFA (state, { app, settings }) {
+ requireMFA (state, { settings }) {
state.settings = settings
- state.app = app
state.strategy = TOTP_STRATEGY // default strategy of MFA
},
requireRecovery (state) {
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 96f14ed5..625323b9 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -27,6 +27,7 @@ const defaultState = {
scopeCopy: true,
subjectLineBehavior: 'email',
postContentType: 'text/plain',
+ hideSitename: false,
nsfwCensorImage: undefined,
vapidPublicKey: undefined,
noAttachmentLinks: false,
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index 63e72e30..7eb0547e 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -22,7 +22,7 @@ const MFA_BACKUP_CODES_URL = '/api/pleroma/accounts/mfa/backup_codes'
const MFA_SETUP_OTP_URL = '/api/pleroma/accounts/mfa/setup/totp'
const MFA_CONFIRM_OTP_URL = '/api/pleroma/accounts/mfa/confirm/totp'
-const MFA_DISABLE_OTP_URL = '/api/pleroma/account/mfa/totp'
+const MFA_DISABLE_OTP_URL = '/api/pleroma/accounts/mfa/totp'
const MASTODON_LOGIN_URL = '/api/v1/accounts/verify_credentials'
const MASTODON_REGISTRATION_URL = '/api/v1/accounts'
diff --git a/src/services/new_api/mfa.js b/src/services/new_api/mfa.js
index cbba06d5..c944667c 100644
--- a/src/services/new_api/mfa.js
+++ b/src/services/new_api/mfa.js
@@ -1,9 +1,9 @@
-const verifyOTPCode = ({ app, instance, mfaToken, code }) => {
+const verifyOTPCode = ({ clientId, clientSecret, instance, mfaToken, code }) => {
const url = `${instance}/oauth/mfa/challenge`
const form = new window.FormData()
- form.append('client_id', app.client_id)
- form.append('client_secret', app.client_secret)
+ form.append('client_id', clientId)
+ form.append('client_secret', clientSecret)
form.append('mfa_token', mfaToken)
form.append('code', code)
form.append('challenge_type', 'totp')
@@ -14,12 +14,12 @@ const verifyOTPCode = ({ app, instance, mfaToken, code }) => {
}).then((data) => data.json())
}
-const verifyRecoveryCode = ({ app, instance, mfaToken, code }) => {
+const verifyRecoveryCode = ({ clientId, clientSecret, instance, mfaToken, code }) => {
const url = `${instance}/oauth/mfa/challenge`
const form = new window.FormData()
- form.append('client_id', app.client_id)
- form.append('client_secret', app.client_secret)
+ form.append('client_id', clientId)
+ form.append('client_secret', clientSecret)
form.append('mfa_token', mfaToken)
form.append('code', code)
form.append('challenge_type', 'recovery')