mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-30 16:18:31 +00:00
Clean up featured follow. Try to make the UX a bit nicer. Localize strings that weren't being localized. Add error handling.
This commit is contained in:
parent
8c5b6910ed
commit
c5564150b1
3 changed files with 151 additions and 106 deletions
|
@ -25,40 +25,48 @@
|
||||||
<a :href="'/' + user.login" :title="user.login" @click.prevent="route(user.login)"><p class="tw-ellipsis tw-flex-grow-1 tw-mg-l-1 tw-font-size-5">{{ user.displayName }}</p></a>
|
<a :href="'/' + user.login" :title="user.login" @click.prevent="route(user.login)"><p class="tw-ellipsis tw-flex-grow-1 tw-mg-l-1 tw-font-size-5">{{ user.displayName }}</p></a>
|
||||||
<div class="tw-flex-grow-1 tw-pd-x-2"/>
|
<div class="tw-flex-grow-1 tw-pd-x-2"/>
|
||||||
|
|
||||||
<button
|
<div v-if="user.error">
|
||||||
v-if="user.following"
|
{{ t('featured-follow.error', 'An error occured.') }}
|
||||||
:data-title="`Unfollow ${user.login}`"
|
</div>
|
||||||
data-tooltip-type="html"
|
<template v-else>
|
||||||
class="tw-button tw-button--status tw-button--success ffz-tooltip ffz--featured-button-unfollow"
|
<button
|
||||||
@click="unfollowUser(user.id)"
|
v-if="user.following"
|
||||||
>
|
:disabled="user.loading"
|
||||||
<span class="tw-button__icon tw-button__icon--status tw-flex">
|
:data-title="user.loading ? null : t('featured-follow.button.unfollow', 'Unfollow %{user}', {user: user.displayName})"
|
||||||
<figure class="ffz-i-heart ffz--featured-button-unfollow-button"/>
|
data-tooltip-type="html"
|
||||||
</span>
|
class="tw-button tw-button--status tw-button--success ffz-tooltip ffz--featured-button-unfollow"
|
||||||
</button>
|
@click="clickWithTip($event, unfollowUser, user.id)"
|
||||||
<button
|
>
|
||||||
v-if="user.following"
|
<span class="tw-button__icon tw-button__icon--status tw-flex">
|
||||||
:data-title="`${(user.disableNotifications ? 'Enable' : 'Disable')} Notifications`"
|
<figure class="ffz-i-heart ffz--featured-button-unfollow-button"/>
|
||||||
data-tooltip-type="html"
|
</span>
|
||||||
class="tw-button-icon tw-mg-l-05 ffz-tooltip ffz--featured-button-notification"
|
</button>
|
||||||
@click="updateNotificationStatus(user.id, user.disableNotifications)"
|
<button
|
||||||
>
|
v-if="user.following"
|
||||||
<span class="tw-button__icon tw-flex">
|
:disabled="user.loading"
|
||||||
<figure :class="{ 'ffz-i-bell': !user.disableNotifications, 'ffz-i-bell-off': user.disableNotifications }"/>
|
:data-title="notifyTip(user.disableNotifications)"
|
||||||
</span>
|
data-tooltip-type="html"
|
||||||
</button>
|
class="tw-button-icon tw-mg-l-05 ffz-tooltip ffz--featured-button-notification"
|
||||||
<button
|
@click="clickWithTip($event, updateNotificationStatus, user.id, user.disableNotifications)"
|
||||||
v-else
|
>
|
||||||
class="tw-button"
|
<span class="tw-button__icon tw-flex">
|
||||||
@click="followUser(user.id)"
|
<figure :class="{ 'ffz-i-bell': !user.disableNotifications, 'ffz-i-bell-off': user.disableNotifications }"/>
|
||||||
>
|
</span>
|
||||||
<span class="tw-button__icon tw-button__icon--left">
|
</button>
|
||||||
<figure class="ffz-i-heart"/>
|
<button
|
||||||
</span>
|
v-else
|
||||||
<span class="tw-button__text">
|
:disabled="user.loading"
|
||||||
{{ t('featured-follow.button.follow', 'Follow') }}
|
class="tw-button"
|
||||||
</span>
|
@click="followUser(user.id)"
|
||||||
</button>
|
>
|
||||||
|
<span class="tw-button__icon tw-button__icon--left">
|
||||||
|
<figure class="ffz-i-heart"/>
|
||||||
|
</span>
|
||||||
|
<span class="tw-button__text">
|
||||||
|
{{ t('featured-follow.button.follow', 'Follow') }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -72,6 +80,24 @@
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return this.$vnode.data;
|
return this.$vnode.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
clickWithTip(event, fn, ...args) {
|
||||||
|
const el = event.target,
|
||||||
|
tip = el && el._ffz_tooltip$0,
|
||||||
|
visible = tip && tip.visible;
|
||||||
|
|
||||||
|
visible && tip.hide();
|
||||||
|
fn.call(this, ...args);
|
||||||
|
visible && setTimeout(() => document.contains(el) && tip.show(), 0)
|
||||||
|
},
|
||||||
|
|
||||||
|
notifyTip(state) {
|
||||||
|
return state ?
|
||||||
|
this.t('featured-follow.notify.off', 'Notifications are currently disabled. Click to enable.') :
|
||||||
|
this.t('featured-follow.notify.on', 'Notifications are currently enabled. Click to disable.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,14 +5,14 @@
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
import {createElement as e} from 'utilities/dom';
|
import {createElement} from 'utilities/dom';
|
||||||
|
import {has} from 'utilities/object';
|
||||||
|
|
||||||
import FEATURED_QUERY from './featured_follow_query.gql';
|
import FEATURED_QUERY from './featured_follow_query.gql';
|
||||||
|
|
||||||
import FEATURED_FOLLOW from './featured_follow_follow.gql';
|
import FEATURED_FOLLOW from './featured_follow_follow.gql';
|
||||||
import FEATURED_UNFOLLOW from './featured_follow_unfollow.gql';
|
import FEATURED_UNFOLLOW from './featured_follow_unfollow.gql';
|
||||||
|
|
||||||
const TWITCH_URL = /^(?:https?:\/\/)?(?:www\.)?twitch\.tv\/([A-Za-z0-9_]+)/i;
|
|
||||||
|
|
||||||
export default class FeaturedFollow extends Module {
|
export default class FeaturedFollow extends Module {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
|
@ -84,31 +84,37 @@ export default class FeaturedFollow extends Module {
|
||||||
this.follow_data = {};
|
this.follow_data = {};
|
||||||
|
|
||||||
this.socket.on(':command:follow_buttons', data => {
|
this.socket.on(':command:follow_buttons', data => {
|
||||||
for(const channel_login in data) {
|
for(const channel_login in data)
|
||||||
if (!data.hasOwnProperty(channel_login)) continue;
|
if ( has(data, channel_login) )
|
||||||
|
this.follow_data[channel_login] = data[channel_login];
|
||||||
|
|
||||||
this.follow_data[channel_login] = data[channel_login];
|
if ( this.vueFeaturedFollow )
|
||||||
}
|
|
||||||
|
|
||||||
if (this.vueFeaturedFollow) {
|
|
||||||
this.vueFeaturedFollow.data.hasUpdate = true;
|
this.vueFeaturedFollow.data.hasUpdate = true;
|
||||||
}
|
|
||||||
|
|
||||||
this.metadata.updateMetadata('following');
|
this.metadata.updateMetadata('following');
|
||||||
});
|
});
|
||||||
|
|
||||||
// ffz.resolve('site.featured_follow').updateFeaturedChannels({ login: 'lordmau5' }, ['sirstendec','jugachi']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFollowsForLogin(login) {
|
async getFollowsForLogin(login) {
|
||||||
const follow_data = this.follow_data && this.follow_data[login];
|
const follow_data = this.follow_data && this.follow_data[login];
|
||||||
if (!follow_data || follow_data.length === 0) return [];
|
if ( ! follow_data || ! follow_data.length )
|
||||||
|
return [];
|
||||||
|
|
||||||
const ap_data = await this.apollo.client.query({ query: FEATURED_QUERY, variables: { logins: follow_data }});
|
const ap_data = await this.apollo.client.query({
|
||||||
|
query: FEATURED_QUERY,
|
||||||
|
variables: {
|
||||||
|
logins: follow_data
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
follows = {};
|
||||||
|
|
||||||
const follows = {};
|
|
||||||
for (const user of ap_data.data.users) {
|
for (const user of ap_data.data.users) {
|
||||||
|
if ( ! user.id )
|
||||||
|
continue;
|
||||||
|
|
||||||
follows[user.id] = {
|
follows[user.id] = {
|
||||||
|
loading: false,
|
||||||
|
error: false,
|
||||||
id: user.id,
|
id: user.id,
|
||||||
login: user.login,
|
login: user.login,
|
||||||
displayName: user.displayName,
|
displayName: user.displayName,
|
||||||
|
@ -117,12 +123,13 @@ export default class FeaturedFollow extends Module {
|
||||||
disableNotifications: user.self.follower.disableNotifications
|
disableNotifications: user.self.follower.disableNotifications
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return follows;
|
return follows;
|
||||||
}
|
}
|
||||||
|
|
||||||
buildFeaturedFollowMenu(vue, login, follows) {
|
buildFeaturedFollowMenu(vue, login, follows) {
|
||||||
const vueEl = new vue.Vue({
|
const vueEl = new vue.Vue({
|
||||||
el: e('div'),
|
el: createElement('div'),
|
||||||
render: h => this.vueFeaturedFollow = h('featured-follow', {
|
render: h => this.vueFeaturedFollow = h('featured-follow', {
|
||||||
login,
|
login,
|
||||||
follows,
|
follows,
|
||||||
|
@ -147,67 +154,82 @@ export default class FeaturedFollow extends Module {
|
||||||
return vueEl.$el;
|
return vueEl.$el;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFeaturedChannels(room, args) {
|
|
||||||
args = args.join(' ').trim().toLowerCase().split(/[ ,]+/);
|
|
||||||
|
|
||||||
const out = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < args.length; i++) {
|
|
||||||
let arg = args[i];
|
|
||||||
const match = arg.match(TWITCH_URL);
|
|
||||||
|
|
||||||
if (match)
|
|
||||||
arg = match[1];
|
|
||||||
|
|
||||||
if (arg !== '' && out.indexOf(arg) === -1)
|
|
||||||
out.push(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.socket.call('update_follow_buttons', room.login, out)
|
|
||||||
.then(() => {
|
|
||||||
// this.log.info('Success!', data);
|
|
||||||
})
|
|
||||||
.catch(() => 'There was an error communicating with the server.');
|
|
||||||
// , (success, data) => {
|
|
||||||
// if (!success) {
|
|
||||||
// this.log.warn('Not a Success: ', data);
|
|
||||||
// // f.room_message(room, data);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// this.log.info('Success!', data);
|
|
||||||
|
|
||||||
// // this.room.message(`The following buttons have been ${data ? 'updated' : 'disabled'}.`);
|
|
||||||
// }) )
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async followUser(follows, id) {
|
async followUser(follows, id) {
|
||||||
const ap_data = await this.apollo.client.mutate({ mutation: FEATURED_FOLLOW, variables: { targetID: id, disableNotifications: false }});
|
const f = follows[id];
|
||||||
|
f.loading = true;
|
||||||
const follow = ap_data.data.followUser.follow;
|
|
||||||
|
try {
|
||||||
follows[id].following = follow.followedAt != null;
|
const ap_data = await this.apollo.client.mutate({
|
||||||
follows[id].disableNotifications = follow.disableNotifications;
|
mutation: FEATURED_FOLLOW,
|
||||||
|
variables: {
|
||||||
|
targetID: id,
|
||||||
|
disableNotifications: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const update = ap_data.data.followUser.follow;
|
||||||
|
|
||||||
|
f.loading = false;
|
||||||
|
f.following = update.followedAt != null;
|
||||||
|
f.disableNotifications = update.disableNotifications;
|
||||||
|
|
||||||
|
} catch(err) {
|
||||||
|
this.log.warn('There was a problem following.', err);
|
||||||
|
f.error = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateNotificationStatus(follows, id, oldStatus) {
|
async updateNotificationStatus(follows, id, oldStatus) {
|
||||||
const ap_data = await this.apollo.client.mutate({ mutation: FEATURED_FOLLOW, variables: { targetID: id, disableNotifications: !oldStatus }});
|
const f = follows[id];
|
||||||
|
f.loading = true;
|
||||||
const follow = ap_data.data.followUser.follow;
|
|
||||||
|
// Immediate Feedback
|
||||||
follows[id].following = follow.followedAt != null;
|
f.disableNotifications = ! oldStatus;
|
||||||
follows[id].disableNotifications = follow.disableNotifications;
|
|
||||||
|
try {
|
||||||
|
const ap_data = await this.apollo.client.mutate({
|
||||||
|
mutation: FEATURED_FOLLOW,
|
||||||
|
variables: {
|
||||||
|
targetID: id,
|
||||||
|
disableNotifications: !oldStatus
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const update = ap_data.data.followUser.follow;
|
||||||
|
|
||||||
|
f.loading = false;
|
||||||
|
f.following = update.followedAt != null;
|
||||||
|
f.disableNotifications = update.disableNotifications;
|
||||||
|
|
||||||
|
} catch(err) {
|
||||||
|
this.log.warn('There was a problem updating notification status.', err);
|
||||||
|
f.error = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async unfollowUser(follows, id) {
|
async unfollowUser(follows, id) {
|
||||||
await this.apollo.client.mutate({ mutation: FEATURED_UNFOLLOW, variables: { targetID: id }});
|
const f = follows[id];
|
||||||
|
f.loading = true;
|
||||||
follows[id].following = false;
|
|
||||||
follows[id].disableNotifications = false;
|
try {
|
||||||
|
await this.apollo.client.mutate({
|
||||||
|
mutation: FEATURED_UNFOLLOW,
|
||||||
|
variables: {
|
||||||
|
targetID: id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
f.loading = false;
|
||||||
|
f.following = false;
|
||||||
|
f.disableNotifications = false;
|
||||||
|
|
||||||
|
} catch(err) {
|
||||||
|
this.log.warn('There was a problem unfollowing.', err);
|
||||||
|
f.error = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
route(channel) {
|
route(channel) {
|
||||||
this.router.navigate('user', { userName: channel });
|
this.router.navigate('user', { userName: channel });
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,15 +4,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.ffz--featured-button-unfollow {
|
.ffz--featured-button-unfollow {
|
||||||
&:hover &:before {
|
&:hover figure:before {
|
||||||
content: '\e813';
|
content: '\e813';
|
||||||
}
|
}
|
||||||
|
|
||||||
> * {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz--featured-button-unfollow,
|
||||||
.ffz--featured-button-notification {
|
.ffz--featured-button-notification {
|
||||||
> * {
|
> * {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue