diff options
Diffstat (limited to 'src/components')
17 files changed, 437 insertions, 70 deletions
diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue index b2f63668..c48fb16b 100644 --- a/src/components/attachment/attachment.vue +++ b/src/components/attachment/attachment.vue @@ -96,6 +96,9 @@ background: rgba(230,230,230,0.6); font-weight: bold; z-index: 4; + line-height: 1; + border-radius: $fallback--tooltipRadius; + border-radius: var(--tooltipRadius, $fallback--tooltipRadius); } .small { diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index 308e5e7d..bfcd3fe7 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -3,7 +3,7 @@ <div class="panel-heading conversation-heading"> {{ $t('timeline.conversation') }} <span v-if="collapsable" style="float:right;"> - <small><a href="#" @click.prevent="$emit('toggleExpanded')">Collapse</a></small> + <small><a href="#" @click.prevent="$emit('toggleExpanded')">{{ $t('timeline.collapse') }}</a></small> </span> </div> <div class="panel-body"> diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue index 6f949afb..2e1a6c7a 100644 --- a/src/components/nav_panel/nav_panel.vue +++ b/src/components/nav_panel/nav_panel.vue @@ -45,8 +45,6 @@ border-bottom: 1px solid; border-color: $fallback--border; border-color: var(--border, $fallback--border); - background-color: $fallback--bg; - background-color: var(--bg, $fallback--bg); padding: 0; &:first-child a { diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 9cbb1226..008530b4 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -98,7 +98,7 @@ .status { padding: 0.25em 0; color: $fallback--faint; - color: var($fallback--faint, --faint); + color: var(--faint, $fallback--faint); } padding: 0; .media-body { diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 6bcf1c66..0597d652 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -48,12 +48,21 @@ const PostStatusForm = { highlighted: 0, newStatus: { status: statusText, - files: [] + files: [], + visibility: 'public' }, caret: 0 } }, computed: { + vis () { + return { + public: { selected: this.newStatus.visibility === 'public' }, + unlisted: { selected: this.newStatus.visibility === 'unlisted' }, + private: { selected: this.newStatus.visibility === 'private' }, + direct: { selected: this.newStatus.visibility === 'direct' } + } + }, candidates () { const firstchar = this.textAtCaret.charAt(0) if (firstchar === '@') { @@ -118,6 +127,9 @@ const PostStatusForm = { }, isOverLengthLimit () { return this.hasStatusLengthLimit && (this.statusLength > this.statusLengthLimit) + }, + scopeOptionsEnabled () { + return this.$store.state.config.scopeOptionsEnabled } }, methods: { @@ -185,6 +197,8 @@ const PostStatusForm = { this.posting = true statusPoster.postStatus({ status: newStatus.status, + spoilerText: newStatus.spoilerText || null, + visibility: newStatus.visibility, media: newStatus.files, store: this.$store, inReplyToStatusId: this.replyTo @@ -192,7 +206,8 @@ const PostStatusForm = { if (!data.error) { this.newStatus = { status: '', - files: [] + files: [], + visibility: newStatus.visibility } this.$emit('posted') let el = this.$el.querySelector('textarea') @@ -239,18 +254,20 @@ const PostStatusForm = { e.dataTransfer.dropEffect = 'copy' }, resize (e) { - const target = e.target || e - target.style.height = 'auto' - const heightPx = target.scrollHeight - 10 - if (heightPx > 54) { - target.style.height = `${target.scrollHeight - 10}px` - } - if (target.value === '') { - target.style.height = '16px' + if (!e.target) { return } + const vertPadding = Number(window.getComputedStyle(e.target)['padding-top'].substr(0, 1)) + + Number(window.getComputedStyle(e.target)['padding-bottom'].substr(0, 1)) + e.target.style.height = 'auto' + e.target.style.height = `${e.target.scrollHeight - vertPadding}px` + if (e.target.value === '') { + e.target.style.height = '16px' } }, clearError () { this.error = null + }, + changeVis (visibility) { + this.newStatus.visibility = visibility } } } diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 88627e3a..802d51ed 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -2,6 +2,12 @@ <div class="post-status-form"> <form @submit.prevent="postStatus(newStatus)"> <div class="form-group" > + <input + v-if="scopeOptionsEnabled" + type="text" + :placeholder="$t('post_status.content_warning')" + v-model="newStatus.spoilerText" + class="form-cw"> <textarea ref="textarea" @click="setCaret" @@ -18,16 +24,17 @@ @input="resize" @paste="paste"> </textarea> + <div v-if="scopeOptionsEnabled" class="visibility-tray"> + <i v-on:click="changeVis('direct')" class="icon-mail-alt" :class="vis.direct"></i> + <i v-on:click="changeVis('private')" class="icon-lock" :class="vis.private"></i> + <i v-on:click="changeVis('unlisted')" class="icon-lock-open-alt" :class="vis.unlisted"></i> + <i v-on:click="changeVis('public')" class="icon-globe" :class="vis.public"></i> + </div> </div> <div style="position:relative;" v-if="candidates"> <div class="autocomplete-panel"> <div v-for="candidate in candidates" @click="replace(candidate.utf || (candidate.screen_name + ' '))"> - <div v-if="candidate.highlighted" class="autocomplete"> - <span v-if="candidate.img"><img :src="candidate.img"></span> - <span v-else>{{candidate.utf}}</span> - <span>{{candidate.screen_name}}<small>{{candidate.name}}</small></span> - </div> - <div v-else class="autocomplete"> + <div class="autocomplete" :class="{ highlighted: candidate.highlighted }"> <span v-if="candidate.img"><img :src="candidate.img"></img></span> <span v-else>{{candidate.utf}}</span> <span>{{candidate.screen_name}}<small>{{candidate.name}}</small></span> @@ -84,6 +91,17 @@ } } +.post-status-form .visibility-tray { + font-size: 1.2em; + padding: 3px; + cursor: pointer; + + .selected { + color: $fallback--lightFg; + color: var(--lightFg, $fallback--lightFg); + } +} + .post-status-form, .login { .form-bottom { display: flex; @@ -135,10 +153,6 @@ cursor: not-allowed; } - .icon-cancel { - cursor: pointer; - } - form { display: flex; flex-direction: column; @@ -152,7 +166,15 @@ line-height:24px; } - form textarea { + form textarea.form-cw { + line-height:16px; + resize: none; + overflow: hidden; + transition: min-height 200ms 100ms; + min-height: 1px; + } + + form textarea.form-control { line-height:16px; resize: none; overflow: hidden; @@ -161,7 +183,7 @@ box-sizing: content-box; } - form textarea:focus { + form textarea.form-control:focus { min-height: 48px; } @@ -186,8 +208,8 @@ z-index: 1; box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5); min-width: 75%; - background: $fallback--btn; - background: var(--btn, $fallback--btn); + background: $fallback--bg; + background: var(--bg, $fallback--bg); color: $fallback--lightFg; color: var(--lightFg, $fallback--lightFg); } @@ -216,6 +238,11 @@ color: $fallback--faint; color: var(--faint, $fallback--faint); } + + &.highlighted { + background-color: $fallback--btn; + background-color: var(--btn, $fallback--btn); + } } } </style> diff --git a/src/components/status/status.js b/src/components/status/status.js index 73f4a7aa..87ef90d8 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -104,6 +104,18 @@ const Status = { StillImage }, methods: { + visibilityIcon (visibility) { + switch (visibility) { + case 'private': + return 'icon-lock' + case 'unlisted': + return 'icon-lock-open-alt' + case 'direct': + return 'icon-mail-alt' + default: + return 'icon-globe' + } + }, linkClicked ({target}) { if (target.tagName === 'SPAN') { target = target.parentNode diff --git a/src/components/status/status.vue b/src/components/status/status.vue index f1163fd9..ace141cd 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -55,6 +55,7 @@ <router-link class="timeago" :to="{ name: 'conversation', params: { id: status.id } }"> <timeago :since="status.created_at" :auto-update="60"></timeago> </router-link> + <span v-if="status.visibility"><i :class="visibilityIcon(status.visibility)"></i> </span> <a :href="status.external_url" target="_blank" v-if="!status.is_local" class="source_url"><i class="icon-link-ext"></i></a> <template v-if="expandable"> <a href="#" @click.prevent="toggleExpanded"><i class="icon-plus-squared"></i></a> @@ -165,8 +166,6 @@ border-left-width: 0px; line-height: 18px; min-width: 0; - background-color: $fallback--bg; - background-color: var(--bg, $fallback--bg); border-color: $fallback--border; border-color: var(--border, $fallback--border); @@ -189,6 +188,10 @@ margin: 0 0 0.25em 0.8em; } + .usercard { + margin-bottom: .7em + } + .media-heading { flex-wrap: nowrap; } diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index 08bc7113..6f4845c4 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -14,6 +14,7 @@ export default { greenColorLocal: '', orangeColorLocal: '', btnRadiusLocal: '', + inputRadiusLocal: '', panelRadiusLocal: '', avatarRadiusLocal: '', avatarAltRadiusLocal: '', @@ -42,6 +43,7 @@ export default { this.orangeColorLocal = rgbstr2hex(this.$store.state.config.colors.cOrange) this.btnRadiusLocal = this.$store.state.config.radii.btnRadius || 4 + this.inputRadiusLocal = this.$store.state.config.radii.inputRadius || 4 this.panelRadiusLocal = this.$store.state.config.radii.panelRadius || 10 this.avatarRadiusLocal = this.$store.state.config.radii.avatarRadius || 5 this.avatarAltRadiusLocal = this.$store.state.config.radii.avatarAltRadius || 50 @@ -85,6 +87,7 @@ export default { cGreen: greenRgb, cOrange: orangeRgb, btnRadius: this.btnRadiusLocal, + inputRadius: this.inputRadiusLocal, panelRadius: this.panelRadiusLocal, avatarRadius: this.avatarRadiusLocal, avatarAltRadius: this.avatarAltRadiusLocal, diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 9c39b245..7acba1dc 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -59,6 +59,11 @@ <input id="btnradius-t" class="theme-radius-in" type="text" v-model="btnRadiusLocal"> </div> <div class="radius-item"> + <label for="inputradius" class="theme-radius-lb">{{$t('settings.inputRadius')}}</label> + <input id="inputradius" class="theme-radius-rn" type="range" v-model="inputRadiusLocal" max="16"> + <input id="inputradius-t" class="theme-radius-in" type="text" v-model="inputRadiusLocal"> + </div> + <div class="radius-item"> <label for="panelradius" class="theme-radius-lb">{{$t('settings.panelRadius')}}</label> <input id="panelradius" class="theme-radius-rn" type="range" v-model="panelRadiusLocal" max="50"> <input id="panelradius-t" class="theme-radius-in" type="text" v-model="panelRadiusLocal"> @@ -86,6 +91,7 @@ </div> <div :style="{ '--btnRadius': btnRadiusLocal + 'px', + '--inputRadius': inputRadiusLocal + 'px', '--panelRadius': panelRadiusLocal + 'px', '--avatarRadius': avatarRadiusLocal + 'px', '--avatarAltRadius': avatarAltRadiusLocal + 'px', diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index 1e8c91de..4d4266cb 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -2,16 +2,24 @@ import StillImage from '../still-image/still-image.vue' import { hex2rgb } from '../../services/color_convert/color_convert.js' export default { - props: [ 'user', 'switcher', 'hideBio' ], + props: [ 'user', 'switcher', 'selected', 'hideBio' ], computed: { headingStyle () { const color = this.$store.state.config.colors.bg if (color) { const rgb = hex2rgb(color) + const tintColor = `rgba(${Math.floor(rgb.r)}, ${Math.floor(rgb.g)}, ${Math.floor(rgb.b)}, .5)` console.log(rgb) + console.log([ + `url(${this.user.cover_photo})`, + `linear-gradient(to bottom, ${tintColor}, ${tintColor})` + ].join(', ')) return { - backgroundColor: `rgb(${Math.floor(rgb[0] * 0.53)}, ${Math.floor(rgb[1] * 0.56)}, ${Math.floor(rgb[2] * 0.59)})`, - backgroundImage: `url(${this.user.cover_photo})` + backgroundColor: `rgb(${Math.floor(rgb.r * 0.53)}, ${Math.floor(rgb.g * 0.56)}, ${Math.floor(rgb.b * 0.59)})`, + backgroundImage: [ + `linear-gradient(to bottom, ${tintColor}, ${tintColor})`, + `url(${this.user.cover_photo})` + ].join(', ') } } }, @@ -61,8 +69,10 @@ export default { store.state.api.backendInteractor.setUserMute(this.user) }, setProfileView (v) { - const store = this.$store - store.commit('setProfileView', { v }) + if (this.switcher) { + const store = this.$store + store.commit('setProfileView', { v }) + } } } } diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue index ca8428ca..c120df9a 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card_content/user_card_content.vue @@ -14,8 +14,9 @@ </router-link> <div class="name-and-screen-name"> <div :title="user.name" class='user-name'>{{user.name}}</div> - <router-link :to="{ name: 'user-profile', params: { id: user.id } }"> - <div class='user-screen-name'>@{{user.screen_name}}</div> + <router-link class='user-screen-name':to="{ name: 'user-profile', params: { id: user.id } }"> + <span>@{{user.screen_name}}</span> + <span class="dailyAvg">{{dailyAvg}} {{ $t('user_card.per_day') }}</span> </router-link> </div> </div> @@ -73,20 +74,17 @@ </div> </div> <div class="panel-body profile-panel-body"> - <div class="user-counts"> - <div class="user-count"> - <a href="#" v-on:click.prevent="setProfileView('statuses')" v-if="switcher"><h5>{{ $t('user_card.statuses') }}</h5></a> - <h5 v-else>{{ $t('user_card.statuses') }}</h5> - <span>{{user.statuses_count}} <br><span class="dailyAvg">{{dailyAvg}} {{ $t('user_card.per_day') }}</span></span> + <div class="user-counts" :class="{clickable: switcher}"> + <div class="user-count" v-on:click.prevent="setProfileView('statuses')" :class="{selected: selected === 'statuses'}"> + <h5>{{ $t('user_card.statuses') }}</h5> + <span>{{user.statuses_count}} <br></span> </div> - <div class="user-count"> - <a href="#" v-on:click.prevent="setProfileView('friends')" v-if="switcher"><h5>{{ $t('user_card.followees') }}</h5></a> - <h5 v-else>{{ $t('user_card.followees') }}</h5> + <div class="user-count" v-on:click.prevent="setProfileView('friends')" :class="{selected: selected === 'friends'}"> + <h5>{{ $t('user_card.followees') }}</h5> <span>{{user.friends_count}}</span> </div> - <div class="user-count"> - <a href="#" v-on:click.prevent="setProfileView('followers')" v-if="switcher"><h5>{{ $t('user_card.followers') }}</h5></a> - <h5 v-else>{{ $t('user_card.followers') }}</h5> + <div class="user-count" v-on:click.prevent="setProfileView('followers')" :class="{selected: selected === 'followers'}"> + <h5>{{ $t('user_card.followers') }}</h5> <span>{{user.followers_count}}</span> </div> </div> @@ -112,20 +110,18 @@ } .profile-panel-body { - top: -0em; - padding-top: 4em; word-wrap: break-word; background: linear-gradient(to bottom, rgba(0, 0, 0, 0), $fallback--bg 80%); background: linear-gradient(to bottom, rgba(0, 0, 0, 0), var(--bg, $fallback--bg) 80%) } .user-info { - color: white; - padding: 0 16px 16px 16px; - margin-bottom: -4em; + color: $fallback--lightFg; + color: var(--lightFg, $fallback--lightFg); + padding: 0 16px; .container { - padding: 16px 10px 4px 10px; + padding: 16px 10px 6px 10px; display: flex; max-height: 56px; overflow: hidden; @@ -154,10 +150,9 @@ } } - text-shadow: 0px 1px 1.5px rgba(0, 0, 0, 1.0); - .usersettings { - color: #fff; + color: $fallback--lightFg; + color: var(--lightFg, $fallback--lightFg); opacity: .8; } @@ -171,14 +166,15 @@ } .user-name{ - color: white; text-overflow: ellipsis; overflow: hidden; } .user-screen-name { - color: white; - font-weight: lighter; + color: $fallback--lightFg; + color: var(--lightFg, $fallback--lightFg); + display: inline-block; + font-weight: light; font-size: 15px; padding-right: 0.1em; } @@ -191,14 +187,11 @@ div { flex: 1; } - margin-top: 0.7em; - margin-bottom: -1.0em; .following { - color: white; font-size: 14px; flex: 0 0 100%; - margin: -0.7em 0.0em 0.3em 0.0em; + margin: 0 0 .4em 0; padding-left: 16px; text-align: left; } @@ -238,12 +231,37 @@ .user-counts { display: flex; line-height:16px; - padding: 1em 1.5em 0em 1em; + padding: .5em 1.5em 0em 1.5em; text-align: center; + justify-content: space-between; + color: $fallback--lightFg; + color: var(--lightFg, $fallback--lightFg); + + &.clickable { + .user-count { + cursor: pointer; + + &:hover:not(.selected) { + transition: border-bottom 100ms; + border-bottom: 3px solid $fallback--link; + border-bottom: 3px solid var(--link, $fallback--link); + } + } + } } .user-count { flex: 1; + padding: .5em 0 .5em 0; + margin: 0 .5em; + + &.selected { + transition: none; + border-bottom: 5px solid $fallback--link; + border-bottom: 5px solid var(--link, $fallback--link); + border-radius: $fallback--btnRadius; + border-radius: var(--btnRadius, $fallback--btnRadius); + } h5 { font-size:1em; @@ -256,7 +274,8 @@ } .dailyAvg { - font-size: 0.8em; - opacity: 0.5; + margin-left: 1em; + font-size: 0.7em; + color: #CCC; } </style> diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index 838a43ab..f8502907 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -1,7 +1,7 @@ <template> <div> <div v-if="user" class="user-profile panel panel-default"> - <user-card-content :user="user" :switcher="true"></user-card-content> + <user-card-content :user="user" :switcher="true" :selected="timeline.viewing"></user-card-content> </div> <Timeline :title="$t('user_profile.timeline_title')" :timeline="timeline" :timeline-name="'user'" :user-id="userId"/> </div> diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js index 25ee1f35..b6026e18 100644 --- a/src/components/user_settings/user_settings.js +++ b/src/components/user_settings/user_settings.js @@ -8,8 +8,15 @@ const UserSettings = { followList: null, followImportError: false, followsImported: false, + enableFollowsExport: true, uploading: [ false, false, false, false ], - previews: [ null, null, null ] + previews: [ null, null, null ], + deletingAccount: false, + deleteAccountConfirmPasswordInput: '', + deleteAccountError: false, + changePasswordInputs: [ '', '', '' ], + changedPassword: false, + changePasswordError: false } }, components: { @@ -137,6 +144,37 @@ const UserSettings = { this.uploading[3] = false }) }, + /* This function takes an Array of Users + * and outputs a file with all the addresses for the user to download + */ + exportPeople (users, filename) { + // Get all the friends addresses + var UserAddresses = users.map(function (user) { + // check is it's a local user + if (user && user.is_local) { + // append the instance address + // eslint-disable-next-line no-undef + user.screen_name += '@' + location.hostname + } + return user.screen_name + }).join('\n') + // Make the user download the file + var fileToDownload = document.createElement('a') + fileToDownload.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(UserAddresses)) + fileToDownload.setAttribute('download', filename) + fileToDownload.style.display = 'none' + document.body.appendChild(fileToDownload) + fileToDownload.click() + document.body.removeChild(fileToDownload) + }, + exportFollows () { + this.enableFollowsExport = false + this.$store.state.api.backendInteractor + .fetchFriends({id: this.$store.state.users.currentUser.id}) + .then((friendList) => { + this.exportPeople(friendList, 'friends.csv') + }) + }, followListChange () { // eslint-disable-next-line no-undef let formData = new FormData() @@ -146,6 +184,37 @@ const UserSettings = { dismissImported () { this.followsImported = false this.followImportError = false + }, + confirmDelete () { + this.deletingAccount = true + }, + deleteAccount () { + this.$store.state.api.backendInteractor.deleteAccount({password: this.deleteAccountConfirmPasswordInput}) + .then((res) => { + if (res.status === 'success') { + this.$store.dispatch('logout') + this.$router.push('/main/all') + } else { + this.deleteAccountError = res.error + } + }) + }, + changePassword () { + const params = { + password: this.changePasswordInputs[0], + newPassword: this.changePasswordInputs[1], + newPasswordConfirmation: this.changePasswordInputs[2] + } + this.$store.state.api.backendInteractor.changePassword(params) + .then((res) => { + if (res.status === 'success') { + this.changedPassword = true + this.changePasswordError = false + } else { + this.changedPassword = false + this.changePasswordError = res.error + } + }) } } } diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue index ed1864cc..fbf3f651 100644 --- a/src/components/user_settings/user_settings.vue +++ b/src/components/user_settings/user_settings.vue @@ -49,6 +49,25 @@ <i class=" icon-spin4 animate-spin uploading" v-if="uploading[2]"></i> <button class="btn btn-default" v-else-if="previews[2]" @click="submitBg">{{$t('general.submit')}}</button> </div> + <div class="setting-item"> + <h3>{{$t('settings.change_password')}}</h3> + <div> + <p>{{$t('settings.current_password')}}</p> + <input type="password" v-model="changePasswordInputs[0]"> + </div> + <div> + <p>{{$t('settings.new_password')}}</p> + <input type="password" v-model="changePasswordInputs[1]"> + </div> + <div> + <p>{{$t('settings.confirm_new_password')}}</p> + <input type="password" v-model="changePasswordInputs[2]"> + </div> + <button class="btn btn-default" @click="changePassword">{{$t('general.submit')}}</button> + <p v-if="changedPassword">{{$t('settings.changed_password')}}</p> + <p v-else-if="changePasswordError !== false">{{$t('settings.change_password_error')}}</p> + <p v-if="changePasswordError">{{changePasswordError}}</p> + </div> <div class="setting-item" v-if="pleromaBackend"> <h3>{{$t('settings.follow_import')}}</h3> <p>{{$t('settings.import_followers_from_a_csv_file')}}</p> @@ -62,10 +81,31 @@ <p>{{$t('settings.follows_imported')}}</p> </div> <div v-else-if="followImportError"> - <i class="icon-cross" @click="dismissImported"</i> + <i class="icon-cross" @click="dismissImported"></i> <p>{{$t('settings.follow_import_error')}}</p> </div> </div> + <div class="setting-item" v-if="enableFollowsExport"> + <h3>{{$t('settings.follow_export')}}</h3> + <button class="btn btn-default" @click="exportFollows">{{$t('settings.follow_export_button')}}</button> + </div> + <div class="setting-item" v-else> + <h3>{{$t('settings.follow_export_processing')}}</h3> + </div> + <hr> + <div class="setting-item"> + <h3>{{$t('settings.delete_account')}}</h3> + <p v-if="!deletingAccount">{{$t('settings.delete_account_description')}}</p> + <div v-if="deletingAccount"> + <p>{{$t('settings.delete_account_instructions')}}</p> + <p>{{$t('login.password')}}</p> + <input type="password" v-model="deleteAccountConfirmPasswordInput"> + <button class="btn btn-default" @click="deleteAccount">{{$t('settings.delete_account')}}</button> + </div> + <p v-if="deleteAccountError !== false">{{$t('settings.delete_account_error')}}</p> + <p v-if="deleteAccountError">{{deleteAccountError}}</p> + <button class="btn btn-default" v-if="!deletingAccount" @click="confirmDelete">{{$t('general.submit')}}</button> + </div> </div> </div> </template> diff --git a/src/components/who_to_follow_panel/who_to_follow_panel.js b/src/components/who_to_follow_panel/who_to_follow_panel.js new file mode 100644 index 00000000..51b9f469 --- /dev/null +++ b/src/components/who_to_follow_panel/who_to_follow_panel.js @@ -0,0 +1,123 @@ +function showWhoToFollow (panel, reply, aHost, aUser) { + var users = reply.ids + var cn + var index = 0 + var random = Math.floor(Math.random() * 10) + for (cn = random; cn < users.length; cn = cn + 10) { + var user + user = users[cn] + var img + if (user.icon) { + img = user.icon + } else { + img = '/images/avi.png' + } + var name = user.to_id + if (index === 0) { + panel.img1 = img + panel.name1 = name + panel.$store.state.api.backendInteractor.externalProfile(name) + .then((externalUser) => { + if (!externalUser.error) { + panel.$store.commit('addNewUsers', [externalUser]) + panel.id1 = externalUser.id + } + }) + } else if (index === 1) { + panel.img2 = img + panel.name2 = name + panel.$store.state.api.backendInteractor.externalProfile(name) + .then((externalUser) => { + if (!externalUser.error) { + panel.$store.commit('addNewUsers', [externalUser]) + panel.id2 = externalUser.id + } + }) + } else if (index === 2) { + panel.img3 = img + panel.name3 = name + panel.$store.state.api.backendInteractor.externalProfile(name) + .then((externalUser) => { + if (!externalUser.error) { + panel.$store.commit('addNewUsers', [externalUser]) + panel.id3 = externalUser.id + } + }) + } + index = index + 1 + if (index > 2) { + break + } + } +} + +function getWhoToFollow (panel) { + var user = panel.$store.state.users.currentUser.screen_name + if (user) { + panel.name1 = 'Loading...' + panel.name2 = 'Loading...' + panel.name3 = 'Loading...' + var host = window.location.hostname + var whoToFollowProvider = panel.$store.state.config.whoToFollowProvider + var url + url = whoToFollowProvider.replace(/{{host}}/g, encodeURIComponent(host)) + url = url.replace(/{{user}}/g, encodeURIComponent(user)) + window.fetch(url, {mode: 'cors'}).then(function (response) { + if (response.ok) { + return response.json() + } else { + panel.name1 = '' + panel.name2 = '' + panel.name3 = '' + } + }).then(function (reply) { + showWhoToFollow(panel, reply, host, user) + }) + } +} + +const WhoToFollowPanel = { + data: () => ({ + img1: '/images/avi.png', + name1: '', + id1: 0, + img2: '/images/avi.png', + name2: '', + id2: 0, + img3: '/images/avi.png', + name3: '', + id3: 0 + }), + computed: { + user: function () { + return this.$store.state.users.currentUser.screen_name + }, + moreUrl: function () { + var host = window.location.hostname + var user = this.user + var whoToFollowLink = this.$store.state.config.whoToFollowLink + var url + url = whoToFollowLink.replace(/{{host}}/g, encodeURIComponent(host)) + url = url.replace(/{{user}}/g, encodeURIComponent(user)) + return url + }, + showWhoToFollowPanel () { + return this.$store.state.config.showWhoToFollowPanel + } + }, + watch: { + user: function (user, oldUser) { + if (this.showWhoToFollowPanel) { + getWhoToFollow(this) + } + } + }, + mounted: + function () { + if (this.showWhoToFollowPanel) { + getWhoToFollow(this) + } + } +} + +export default WhoToFollowPanel diff --git a/src/components/who_to_follow_panel/who_to_follow_panel.vue b/src/components/who_to_follow_panel/who_to_follow_panel.vue new file mode 100644 index 00000000..5af6d0d5 --- /dev/null +++ b/src/components/who_to_follow_panel/who_to_follow_panel.vue @@ -0,0 +1,37 @@ +<template> + <div class="who-to-follow-panel"> + <div class="panel panel-default base01-background"> + <div class="panel-heading timeline-heading base02-background base04"> + <div class="title"> + Who to follow + </div> + </div> + <div class="panel-body who-to-follow"> + <p> + <img v-bind:src="img1"/> <router-link :to="{ name: 'user-profile', params: { id: id1 } }">{{ name1 }}</router-link><br> + <img v-bind:src="img2"/> <router-link :to="{ name: 'user-profile', params: { id: id2 } }">{{ name2 }}</router-link><br> + <img v-bind:src="img3"/> <router-link :to="{ name: 'user-profile', params: { id: id3 } }">{{ name3 }}</router-link><br> + <img v-bind:src="$store.state.config.logo"> <a v-bind:href="moreUrl" target="_blank">More</a> + </p> + </div> + </div> + </div> +</template> + +<script src="./who_to_follow_panel.js" ></script> + +<style lang="scss"> + .who-to-follow * { + vertical-align: middle; + } + .who-to-follow img { + width: 32px; + height: 32px; + } + .who-to-follow p { + line-height: 40px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } +</style> |
