More fixes (#9006)

* Reset user when clicking profile nav

* Added editprofile

* Add scroll to conversations and scroll to bottom on messages

* Fixed profile click

* Added notification mixin

* Fixed challenge edit assignment

* Guild list item now returns summary

* Fixed tier icons

* Fixed quest invitaion check

* Fixed extra loading of party members in modals

* Removed background from avatar in tutorial

* Changed port back

* Fixed task adding
This commit is contained in:
Keith Holliday
2017-08-30 15:19:17 -06:00
committed by GitHub
parent 5c16600b25
commit 65aabc8333
16 changed files with 125 additions and 61 deletions

View File

@@ -1,5 +1,5 @@
<template lang="pug"> <template lang="pug">
b-modal#quest-invitation(v-if='user.party.quest.key', :title="$t('questInvitation')", size='lg', :hide-footer="true") b-modal#quest-invitation(v-if='user.party.quest.key && quests[user.party.quest.key]', :title="$t('questInvitation')", size='lg', :hide-footer="true")
.modal-header .modal-header
h4 {{ $t('questInvitation') }} h4 {{ $t('questInvitation') }}
|&nbsp;{{quests[user.party.quest.key].text()}} |&nbsp;{{quests[user.party.quest.key].text()}}

View File

@@ -136,9 +136,9 @@ export default {
}, },
openPartyModal () { openPartyModal () {
if (this.user.party._id) { if (this.user.party._id) {
this.$store.state.groupId = this.user.party._id; this.$store.state.memberModalOptions.groupId = this.user.party._id;
// @TODO: do we need to fetch party? // @TODO: do we need to fetch party?
this.$store.state.memberModalOptions.group = this.$store.state.party; // this.$store.state.memberModalOptions.group = this.$store.state.party;
this.$root.$emit('show::modal', 'members-modal'); this.$root.$emit('show::modal', 'members-modal');
return; return;
} }

View File

@@ -70,9 +70,9 @@ div
span.small-text {{ $t('editAvatar') }} span.small-text {{ $t('editAvatar') }}
a.nav-link.dropdown-item(@click.prevent='showInbox()') {{ $t('messages') }} a.nav-link.dropdown-item(@click.prevent='showInbox()') {{ $t('messages') }}
a.dropdown-item(@click='showAvatar("backgrounds", "2017")') {{ $t('backgrounds') }} a.dropdown-item(@click='showAvatar("backgrounds", "2017")') {{ $t('backgrounds') }}
router-link.dropdown-item(:to="{name: 'stats'}") {{ $t('stats') }} a.dropdown-item(@click='showProfile("stats")') {{ $t('stats') }}
router-link.dropdown-item(:to="{name: 'achievements'}") {{ $t('achievements') }} a.dropdown-item(@click='showProfile("achievements")') {{ $t('achievements') }}
router-link.dropdown-item(:to="{name: 'profile'}") {{ $t('profile') }} a.dropdown-item(@click='showProfile("profile")') {{ $t('profile') }}
router-link.dropdown-item(:to="{name: 'site'}") {{ $t('settings') }} router-link.dropdown-item(:to="{name: 'site'}") {{ $t('settings') }}
a.nav-link.dropdown-item(to="/", @click.prevent='logout()') {{ $t('logout') }} a.nav-link.dropdown-item(to="/", @click.prevent='logout()') {{ $t('logout') }}
</template> </template>
@@ -278,6 +278,10 @@ export default {
this.$store.state.avatarEditorOptions.subpage = subpage; this.$store.state.avatarEditorOptions.subpage = subpage;
this.$root.$emit('show::modal', 'avatar-modal'); this.$root.$emit('show::modal', 'avatar-modal');
}, },
showProfile (startingPage) {
this.$store.state.profileOptions.startingPage = startingPage;
this.$root.$emit('show::modal', 'profile');
},
async getUserGroupPlans () { async getUserGroupPlans () {
this.groupPlans = await this.$store.dispatch('guilds:getGroupPlans'); this.groupPlans = await this.$store.dispatch('guilds:getGroupPlans');
}, },

View File

@@ -13,12 +13,12 @@
label label
strong(v-once) {{$t('challengeSummary')}} * strong(v-once) {{$t('challengeSummary')}} *
div.summary-count {{charactersRemaining}} {{ $t('charactersRemaining') }} div.summary-count {{charactersRemaining}} {{ $t('charactersRemaining') }}
b-form-input.summary-textarea(type="text", textarea, :placeholder="$t('challengeSummaryPlaceholder')", v-model="workingChallenge.summary") textarea.summary-textarea.form-control(:placeholder="$t('challengeSummaryPlaceholder')", v-model="workingChallenge.summary")
.form-group .form-group
label label
strong(v-once) {{$t('challengeDescription')}} * strong(v-once) {{$t('challengeDescription')}} *
a.float-right {{ $t('markdownFormattingHelp') }} a.float-right {{ $t('markdownFormattingHelp') }}
b-form-input.description-textarea(type="text", textarea, :placeholder="$t('challengeDescriptionPlaceholder')", v-model="workingChallenge.description") textarea.description-textarea.form-control(:placeholder="$t('challengeDescriptionPlaceholder')", v-model="workingChallenge.description")
.form-group(v-if='creating') .form-group(v-if='creating')
label label
strong(v-once) {{$t('challengeGuild')}} * strong(v-once) {{$t('challengeGuild')}} *
@@ -211,7 +211,22 @@ export default {
return { return {
creating: true, creating: true,
workingChallenge: {}, workingChallenge: {
name: '',
summary: '',
description: '',
categories: [],
group: '',
dailys: [],
habits: [],
leader: '',
members: [],
official: false,
prize: 1,
rewards: [],
shortName: '',
todos: [],
},
showCategorySelect: false, showCategorySelect: false,
categoryOptions, categoryOptions,
categoriesHashByKey, categoriesHashByKey,
@@ -221,7 +236,7 @@ export default {
async mounted () { async mounted () {
this.$root.$on('shown::modal', () => { this.$root.$on('shown::modal', () => {
if (this.challenge) { if (this.challenge) {
Object.assign(this.workingChallenge, this.challenge); this.workingChallenge = Object.assign(this.workingChallenge, this.challenge);
this.workingChallenge.categories = []; this.workingChallenge.categories = [];
if (this.challenge.categories) { if (this.challenge.categories) {

View File

@@ -20,7 +20,7 @@
.card-block .card-block
h3.leader(:class='userLevelStyle(cachedProfileData[msg.uuid])') h3.leader(:class='userLevelStyle(cachedProfileData[msg.uuid])')
| {{msg.user}} | {{msg.user}}
.svg-icon(v-html="`icons.tier${cachedProfileData[msg.uuid].contributor.level}`", v-if='cachedProfileData[msg.uuid] && cachedProfileData[msg.uuid].contributor && cachedProfileData[msg.uuid].contributor.level') .svg-icon(v-html="icons[`tier${cachedProfileData[msg.uuid].contributor.level}`]", v-if='cachedProfileData[msg.uuid] && cachedProfileData[msg.uuid].contributor && cachedProfileData[msg.uuid].contributor.level')
p {{msg.timestamp | timeAgo}} p {{msg.timestamp | timeAgo}}
.text(v-markdown='msg.text') .text(v-markdown='msg.text')
hr hr
@@ -103,16 +103,18 @@
color: #167e87; color: #167e87;
} }
.moderator { .tier8 {
color: #277eab; color: #277eab;
} }
.staff { .tier10 {
color: #6133b4; color: #6133b4;
} }
.npc { .tier9 {
color: $black; color: #77f4c7;
fill: #77f4c7;
stroke: #005737;
} }
// End of tier colors // End of tier colors
@@ -202,9 +204,9 @@ import tier4 from 'assets/svg/tier-4.svg';
import tier5 from 'assets/svg/tier-5.svg'; import tier5 from 'assets/svg/tier-5.svg';
import tier6 from 'assets/svg/tier-6.svg'; import tier6 from 'assets/svg/tier-6.svg';
import tier7 from 'assets/svg/tier-7.svg'; import tier7 from 'assets/svg/tier-7.svg';
import tierMod from 'assets/svg/tier-mod.svg'; import tier8 from 'assets/svg/tier-mod.svg';
import tierNPC from 'assets/svg/tier-npc.svg'; import tier9 from 'assets/svg/tier-npc.svg';
import tierStaff from 'assets/svg/tier-staff.svg'; import tier10 from 'assets/svg/tier-staff.svg';
export default { export default {
props: ['chat', 'groupId', 'groupName', 'inbox'], props: ['chat', 'groupId', 'groupName', 'inbox'],
@@ -243,9 +245,9 @@ export default {
tier5, tier5,
tier6, tier6,
tier7, tier7,
tierMod, tier8,
tierNPC, tier9,
tierStaff, tier10,
}), }),
copyingMessage: {}, copyingMessage: {},
currentDayDividerDisplay: moment().day(), currentDayDividerDisplay: moment().day(),

View File

@@ -8,7 +8,7 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
.section.avatar-section.row(:class='{"page-2": modalPage === 2}') .section.avatar-section.row(:class='{"page-2": modalPage === 2}')
.col-6.offset-3 .col-6.offset-3
.user-creation-bg .user-creation-bg
avatar(:member='user') avatar(:member='user', :avatarOnly='!editing')
.section(v-if='modalPage === 2') .section(v-if='modalPage === 2')
// @TODO Implement in V2 .section.row // @TODO Implement in V2 .section.row
@@ -282,7 +282,7 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
.next(v-once) {{$t('next')}} .next(v-once) {{$t('next')}}
.next-arrow .next-arrow
div(v-if='modalPage === 3', @click='done()') div(v-if='modalPage === 3', @click='done()')
button.btn.btn-primary.next(v-once) {{$t('done')}} button.btn.btn-primary.next(v-once, v-if='!loading') {{$t('done')}}
</template> </template>
<style> <style>
@@ -808,6 +808,7 @@ export default {
let backgroundShopSets = getBackgroundShopSets(); let backgroundShopSets = getBackgroundShopSets();
return { return {
loading: false,
backgroundShopSets, backgroundShopSets,
backgroundUpdate: new Date(), backgroundUpdate: new Date(),
icons: Object.freeze({ icons: Object.freeze({
@@ -895,6 +896,8 @@ export default {
this.user.items.gear.equipped[key] = !this.user.items.gear.equipped[key]; this.user.items.gear.equipped[key] = !this.user.items.gear.equipped[key];
}, },
async done () { async done () {
this.loading = true;
let tasksToCreate = []; let tasksToCreate = [];
this.taskCategories.forEach(category => { this.taskCategories.forEach(category => {
tasksToCreate = tasksToCreate.concat(tasksByCategory[category]); tasksToCreate = tasksToCreate.concat(tasksByCategory[category]);
@@ -905,7 +908,7 @@ export default {
let tasks = response.data.data; let tasks = response.data.data;
tasks.forEach(task => { tasks.forEach(task => {
this.$store.state.user.data.tasksOrder[`${task.type}s`].unshift(task._id); this.$store.state.user.data.tasksOrder[`${task.type}s`].unshift(task._id);
Object.assign(this.$store.state.tasks.data[`${task.type}s`][0], task); this.$store.state.tasks.data[`${task.type}s`].unshift(task);
}); });
this.$root.$emit('hide::modal', 'avatar-modal'); this.$root.$emit('hide::modal', 'avatar-modal');

View File

@@ -118,11 +118,6 @@ export default {
bDropdownItem, bDropdownItem,
MemberDetails, MemberDetails,
}, },
mounted () {
this.$root.$on('shown::modal', () => {
this.getMembers();
});
},
data () { data () {
return { return {
sortOption: '', sortOption: '',
@@ -157,6 +152,7 @@ export default {
computed: { computed: {
...mapState({user: 'user.data'}), ...mapState({user: 'user.data'}),
isLeader () { isLeader () {
if (!this.group.leader) return false;
return this.user._id === this.group.leader || this.user._id === this.group.leader._id; return this.user._id === this.group.leader || this.user._id === this.group.leader._id;
}, },
groupIsSubscribed () { groupIsSubscribed () {
@@ -165,6 +161,9 @@ export default {
group () { group () {
return this.$store.state.memberModalOptions.group; return this.$store.state.memberModalOptions.group;
}, },
groupId () {
return this.$store.state.memberModalOptions.groupId || this.group._id;
},
sortedMembers () { sortedMembers () {
let sortedMembers = this.members; let sortedMembers = this.members;
if (!this.sortOption) return sortedMembers; if (!this.sortOption) return sortedMembers;
@@ -185,9 +184,18 @@ export default {
return this.members; return this.members;
}, },
}, },
watch: {
groupId () {
// @TOOD: We might not need this since groupId is computed now
this.getMembers();
},
group () {
this.getMembers();
},
},
methods: { methods: {
async getMembers () { async getMembers () {
let groupId = this.$store.state.memberModalOptions.groupId || this.group._id; let groupId = this.groupId;
if (groupId && groupId !== 'challenge') { if (groupId && groupId !== 'challenge') {
let members = await this.$store.dispatch('members:getGroupMembers', { let members = await this.$store.dispatch('members:getGroupMembers', {
groupId, groupId,

View File

@@ -13,7 +13,7 @@ router-link.card-link(:to="{ name: 'guild', params: { groupId: guild._id } }")
.col-md-8 .col-md-8
router-link(:to="{ name: 'guild', params: { groupId: guild._id } }") router-link(:to="{ name: 'guild', params: { groupId: guild._id } }")
h3 {{ guild.name }} h3 {{ guild.name }}
p {{ guild.description }} p {{ guild.summary }}
.col-md-2.cta-container .col-md-2.cta-container
button.btn.btn-danger(v-if='isMember && displayLeave' @click='leave()', v-once) {{ $t('leave') }} button.btn.btn-danger(v-if='isMember && displayLeave' @click='leave()', v-once) {{ $t('leave') }}
button.btn.btn-success(v-if='!isMember' @click='join()', v-once) {{ $t('join') }} button.btn.btn-success(v-if='!isMember' @click='join()', v-once) {{ $t('join') }}

View File

@@ -222,6 +222,7 @@ export default {
percent, percent,
showMemberModal (member) { showMemberModal (member) {
this.$store.state.profileUser = member; this.$store.state.profileUser = member;
this.$store.state.profileOptions.startingPage = 'profile';
this.$root.$emit('show::modal', 'profile'); this.$root.$emit('show::modal', 'profile');
}, },
}, },

View File

@@ -163,12 +163,6 @@ export default {
if (this.user.stats.lvl === 0) return; if (this.user.stats.lvl === 0) return;
this.hp(after - before, 'hp'); this.hp(after - before, 'hp');
// @TODO: I am pretty sure we no long need this with $store
// this.$broadcast('syncPartyRequest', {
// type: 'user_update',
// user: this.user,
// });
if (after < 0) this.playSound('Minus_Habit'); if (after < 0) this.playSound('Minus_Habit');
}, },
userExp (after, before) { userExp (after, before) {

View File

@@ -105,11 +105,6 @@ export default {
bDropdownItem, bDropdownItem,
MemberDetails, MemberDetails,
}, },
mounted () {
this.$root.$on('shown::modal', () => {
this.getMembers();
});
},
data () { data () {
return { return {
sortOption: '', sortOption: '',
@@ -161,10 +156,18 @@ export default {
return this.members; return this.members;
}, },
groupId () {
return this.$store.state.groupId || this.group._id;
},
},
watch: {
groupId () {
this.getMembers();
},
}, },
methods: { methods: {
async getMembers () { async getMembers () {
let groupId = this.$store.state.groupId || this.group._id; let groupId = this.groupId;
if (groupId && groupId !== 'challenge') { if (groupId && groupId !== 'challenge') {
let members = await this.$store.dispatch('members:getGroupMembers', { let members = await this.$store.dispatch('members:getGroupMembers', {
groupId, groupId,

View File

@@ -23,13 +23,14 @@
h4(v-once) {{$t('emptyMessagesLine1')}} h4(v-once) {{$t('emptyMessagesLine1')}}
p(v-once) {{$t('emptyMessagesLine2')}} p(v-once) {{$t('emptyMessagesLine2')}}
.conversations(v-if='filtersConversations.length > 0') .conversations(v-if='filtersConversations.length > 0')
.conversation(v-for='conversation in conversations', @click='selectConversation(conversation.key)', :class="{active: selectedConversation === conversation.key}") .conversation(v-for='conversation in conversations', @click='selectConversation(conversation.key)',
:class="{active: selectedConversation === conversation.key}")
div div
span(:class="userLevelStyle(conversation)") {{conversation.name}} span(:class="userLevelStyle(conversation)") {{conversation.name}}
span.timeago {{conversation.date | timeAgo}} span.timeago {{conversation.date | timeAgo}}
div {{conversation.lastMessageText.substring(0, 30)}} div {{conversation.lastMessageText.substring(0, 30)}}
.col-8.messages .col-8.messages
chat-message.container-fluid.message-scroll(:chat.sync='activeChat', :inbox='true') chat-message.container-fluid.message-scroll(:chat.sync='activeChat', :inbox='true', ref="chatscroll")
// @TODO: Implement new message header here when we fix the above // @TODO: Implement new message header here when we fix the above
@@ -113,6 +114,11 @@
} }
} }
.conversations {
max-height: 400px;
overflow: scroll;
}
.conversation { .conversation {
padding: 1.5em; padding: 1.5em;
background: $white; background: $white;
@@ -133,6 +139,7 @@
</style> </style>
<script> <script>
import Vue from 'vue';
import moment from 'moment'; import moment from 'moment';
import filter from 'lodash/filter'; import filter from 'lodash/filter';
// import sortBy from 'lodash/sortBy'; // import sortBy from 'lodash/sortBy';
@@ -223,6 +230,10 @@ export default {
// return o.timestamp; // return o.timestamp;
// }]); // }]);
this.$set(this, 'activeChat', activeChat); this.$set(this, 'activeChat', activeChat);
Vue.nextTick(() => {
let chatscroll = this.$refs.chatscroll.$el;
chatscroll.scrollTop = chatscroll.scrollHeight;
});
}, },
sendPrivateMessage () { sendPrivateMessage () {
this.$store.dispatch('members:sendPrivateMessage', { this.$store.dispatch('members:sendPrivateMessage', {
@@ -241,6 +252,11 @@ export default {
this.conversations[this.selectedConversation].date = new Date(); this.conversations[this.selectedConversation].date = new Date();
this.newMessage = ''; this.newMessage = '';
Vue.nextTick(() => {
let chatscroll = this.$refs.chatscroll.$el;
chatscroll.scrollTop = chatscroll.scrollHeight;
});
}, },
}, },
}; };

View File

@@ -60,19 +60,20 @@ div
label {{ $t('about') }} label {{ $t('about') }}
textarea.form-control(rows=5, :placeholder="$t('displayBlurbPlaceholder')", v-model='editingProfile.blurb') textarea.form-control(rows=5, :placeholder="$t('displayBlurbPlaceholder')", v-model='editingProfile.blurb')
// include ../../shared/formatting-help // include ../../shared/formatting-help
.form-group //- .form-group
label Facebook //- label Facebook
input.form-control(type='text', placeholder="Paste your link here", v-model='editingProfile.facebook') //- input.form-control(type='text', placeholder="Paste your link here", v-model='editingProfile.facebook')
.form-group //- .form-group
label Instagram //- label Instagram
input.form-control(type='text', placeholder="Paste your link here", v-model='editingProfile.instagram') //- input.form-control(type='text', placeholder="Paste your link here", v-model='editingProfile.instagram')
.form-group //- .form-group
label Twitter //- label Twitter
input.form-control(type='text', placeholder="Paste your link here", v-model='editingProfile.twitter') //- input.form-control(type='text', placeholder="Paste your link here", v-model='editingProfile.twitter')
.col-3.offset-6.text.center .col-12.text-center
button.btn.btn-primary(@click='save()') {{ $t("save") }} button.btn.btn-primary(@click='save()') {{ $t("save") }}
button.btn.btn-warning(@click='editing = false') {{ $t("cancel") }} button.btn.btn-warning(@click='editing = false') {{ $t("cancel") }}
.standard-page.container(v-show='selectedPage === "achievements"', v-if='user.achievements') .standard-page.container(v-show='selectedPage === "achievements"', v-if='user.achievements')
.row(v-for='(category, key) in achievements') .row(v-for='(category, key) in achievements')
h2.col-12 {{ $t(key+'Achievs') }} h2.col-12 {{ $t(key+'Achievs') }}
@@ -334,6 +335,7 @@ import { beastMasterProgress, mountMasterProgress } from '../../../common/script
import statsComputed from '../../../common/script/libs/statsComputed'; import statsComputed from '../../../common/script/libs/statsComputed';
import autoAllocate from '../../../common/script/fns/autoAllocate'; import autoAllocate from '../../../common/script/fns/autoAllocate';
import allocate from '../../../common/script/ops/allocate'; import allocate from '../../../common/script/ops/allocate';
import notifications from 'client/mixins/notifications';
import achievementsLib from '../../../common/script/libs/achievements'; import achievementsLib from '../../../common/script/libs/achievements';
// @TODO: EMAILS.COMMUNITY_MANAGER_EMAIL // @TODO: EMAILS.COMMUNITY_MANAGER_EMAIL
@@ -346,6 +348,7 @@ export default {
components: { components: {
bModal, bModal,
}, },
mixins: [notifications],
data () { data () {
return { return {
userIdToMessage: '', userIdToMessage: '',
@@ -430,6 +433,14 @@ export default {
return classTexts[this.user.stats.class]; return classTexts[this.user.stats.class];
}, },
startingPageOption () {
return this.$store.state.profileOptions.startingPage;
},
},
watch: {
startingPageOption () {
this.selectedPage = this.$store.state.profileOptions.startingPage;
},
}, },
methods: { methods: {
sendMessage () { sendMessage () {
@@ -443,8 +454,7 @@ export default {
message: this.privateMessage, message: this.privateMessage,
toUserId: this.userIdToMessage, toUserId: this.userIdToMessage,
}); });
alert(this.$t('messageSentAlert')); this.text(this.$t('messageSentAlert'));
// @TODO: Notification.text(window.env.t('messageSentAlert'));
}, },
getProgressDisplay () { getProgressDisplay () {
// let currentLoginDay = Content.loginIncentives[this.user.loginIncentives]; // let currentLoginDay = Content.loginIncentives[this.user.loginIncentives];
@@ -472,11 +482,14 @@ export default {
each(this.editingProfile, (value, key) => { each(this.editingProfile, (value, key) => {
// Using toString because we need to compare two arrays (websites) // Using toString because we need to compare two arrays (websites)
let curVal = this.user.profile[key]; let curVal = this.user.profile[key];
if (!curVal || this.editingProfile[key].toString() !== curVal.toString()) values[`profile.${key}`] = value; if (!curVal || this.editingProfile[key].toString() !== curVal.toString()) {
values[`profile.${key}`] = value;
this.$set(this.userLoggedIn.profile, key, value);
this.$set(this.user.profile, key, value);
}
}); });
// @TODO: dispatch this.$store.dispatch('user:set', values);
// User.set(values);
this.editing = false; this.editing = false;
}, },

View File

@@ -11,6 +11,7 @@ export default {
profile, profile,
}, },
mounted () { mounted () {
this.$store.state.profileUser = {};
this.$root.$emit('show::modal', 'profile'); this.$root.$emit('show::modal', 'profile');
}, },
}; };

View File

@@ -98,6 +98,9 @@ export default function () {
castingSpell: false, castingSpell: false,
spellDrawOpen: true, spellDrawOpen: true,
}, },
profileOptions: {
startingPage: '',
},
profileUser: {}, profileUser: {},
upgradingGroup: {}, upgradingGroup: {},
notificationStore: [], notificationStore: [],

View File

@@ -186,5 +186,6 @@
"timezone": "Time Zone", "timezone": "Time Zone",
"timezoneUTC": "Habitica uses the time zone set on your PC, which is: <strong><%= utc %></strong>", "timezoneUTC": "Habitica uses the time zone set on your PC, which is: <strong><%= utc %></strong>",
"timezoneInfo": "If that time zone is wrong, first reload this page using your browser's reload or refresh button to ensure that Habitica has the most recent information. If it is still wrong, adjust the time zone on your PC and then reload this page again.<br><br> <strong>If you use Habitica on other PCs or mobile devices, the time zone must be the same on them all.</strong> If your Dailies have been resetting at the wrong time, repeat this check on all other PCs and on a browser on your mobile devices.", "timezoneInfo": "If that time zone is wrong, first reload this page using your browser's reload or refresh button to ensure that Habitica has the most recent information. If it is still wrong, adjust the time zone on your PC and then reload this page again.<br><br> <strong>If you use Habitica on other PCs or mobile devices, the time zone must be the same on them all.</strong> If your Dailies have been resetting at the wrong time, repeat this check on all other PCs and on a browser on your mobile devices.",
"push": "Push" "push": "Push",
"about": "About"
} }