Merge branch 'develop' into issue-11615
@@ -135,6 +135,15 @@
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.closepadding {
|
||||
margin: 11px 24px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
right: 0;
|
||||
top: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.container-fluid {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
@@ -165,15 +174,6 @@
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.closepadding {
|
||||
margin: 11px 24px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
right: 0;
|
||||
top: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.content {
|
||||
font-size: 12px;
|
||||
@@ -239,7 +239,11 @@ import subCancelModalConfirm from '@/components/payments/cancelModalConfirm';
|
||||
import subCanceledModal from '@/components/payments/canceledModal';
|
||||
|
||||
import spellsMixin from '@/mixins/spells';
|
||||
import { CONSTANTS, getLocalSetting, removeLocalSetting } from '@/libs/userlocalManager';
|
||||
import {
|
||||
CONSTANTS,
|
||||
getLocalSetting,
|
||||
removeLocalSetting,
|
||||
} from '@/libs/userlocalManager';
|
||||
|
||||
import svgClose from '@/assets/svg/close.svg';
|
||||
import bannedAccountModal from '@/components/bannedAccountModal';
|
||||
|
||||
@@ -1,48 +1,78 @@
|
||||
.promo_armoire_backgrounds_201911 {
|
||||
.promo_armoire_backgrounds_202001 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -451px;
|
||||
background-position: -424px -175px;
|
||||
width: 423px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_costume_achievement {
|
||||
.promo_g1g1_2019 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -747px;
|
||||
width: 144px;
|
||||
height: 156px;
|
||||
background-position: -403px -471px;
|
||||
width: 357px;
|
||||
height: 144px;
|
||||
}
|
||||
.promo_delightful_dinos {
|
||||
.promo_mystery_202001 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -424px -451px;
|
||||
width: 423px;
|
||||
background-position: -241px -619px;
|
||||
width: 279px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_ember_thunderstorm_potions {
|
||||
.promo_snowballs {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -599px;
|
||||
width: 423px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_mystery_201910 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -424px -599px;
|
||||
width: 282px;
|
||||
height: 147px;
|
||||
background-position: 0px 0px;
|
||||
width: 420px;
|
||||
height: 174px;
|
||||
}
|
||||
.promo_take_this {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -451px -365px;
|
||||
background-position: -761px -471px;
|
||||
width: 96px;
|
||||
height: 69px;
|
||||
}
|
||||
.scene_habitica_map {
|
||||
.promo_winter_potions_2020 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px 0px;
|
||||
width: 450px;
|
||||
height: 450px;
|
||||
background-position: 0px -323px;
|
||||
width: 423px;
|
||||
height: 147px;
|
||||
}
|
||||
.scene_seaserpent {
|
||||
.promo_winter_quests_bundle {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -451px 0px;
|
||||
width: 476px;
|
||||
height: 364px;
|
||||
background-position: 0px -175px;
|
||||
width: 423px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_winter_wonderland_2019 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -471px;
|
||||
width: 402px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_winter_wonderland_2020 {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -421px 0px;
|
||||
width: 468px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_wintery_hair {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -521px -619px;
|
||||
width: 152px;
|
||||
height: 75px;
|
||||
}
|
||||
.promo_wintery_skins {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -424px -323px;
|
||||
width: 420px;
|
||||
height: 147px;
|
||||
}
|
||||
.customize-option.promo_wintery_skins {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -449px -338px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
.scene_list {
|
||||
background-image: url('~@/assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -619px;
|
||||
width: 240px;
|
||||
height: 195px;
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 481 KiB After Width: | Height: | Size: 476 KiB |
|
Before Width: | Height: | Size: 669 KiB After Width: | Height: | Size: 687 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 369 KiB After Width: | Height: | Size: 313 KiB |
|
Before Width: | Height: | Size: 318 KiB After Width: | Height: | Size: 353 KiB |
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 153 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 160 KiB |
|
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 178 KiB |
|
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 159 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 134 KiB |
@@ -20,7 +20,7 @@
|
||||
@include btn-focus-hover-shadow();
|
||||
}
|
||||
|
||||
&:hover:not(.btn-flat):not(.disabled) {
|
||||
&:hover:not(.btn-flat):not(.disabled):not(:disabled) {
|
||||
@include btn-focus-hover-shadow();
|
||||
border-color: transparent;
|
||||
}
|
||||
@@ -49,7 +49,7 @@
|
||||
background: $purple-200;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled), &:active, &.active, &:focus {
|
||||
&:hover:not(:disabled):not(.disabled), &:active:not(:disabled):not(.disabled), &.active:not(:disabled):not(.disabled), &:focus:not(:disabled):not(.disabled) {
|
||||
background: #5d3b9c !important;
|
||||
color: $white;
|
||||
}
|
||||
@@ -59,7 +59,7 @@
|
||||
color: $gray-50;
|
||||
background: $white !important;
|
||||
|
||||
&:hover:not(:disabled):not(.disabled), &:active, &.active, &:focus {
|
||||
&:hover:not(:disabled):not(.disabled), &:active:not(:disabled):not(.disabled), &.active:not(:disabled):not(.disabled), &:focus:not(:disabled):not(.disabled) {
|
||||
color: $purple-200 !important;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
background: $green-100;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled), &:active, &.active {
|
||||
&:hover:not(:disabled):not(.disabled), &:active:not(:disabled):not(.disabled), &.active:not(:disabled):not(.disabled) {
|
||||
background: $green-50;
|
||||
}
|
||||
}
|
||||
@@ -96,8 +96,12 @@
|
||||
background: $blue-50;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled), &:active, &.active {
|
||||
background: $blue-100;
|
||||
&:hover:not(:disabled):not(.disabled) {
|
||||
background-color: $blue-100;
|
||||
}
|
||||
|
||||
&:active:not(:disabled):not(.disabled), &.active:not(:disabled):not(.disabled) {
|
||||
background: $blue-50;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +112,11 @@
|
||||
background: $red-50;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled), &:active, &.active {
|
||||
&:hover:not(:disabled):not(.disabled) {
|
||||
background: $red-100;
|
||||
}
|
||||
|
||||
&:active:not(:disabled):not(.disabled), &.active:not(:disabled):not(.disabled) {
|
||||
background: $red-100;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ input, textarea, input.form-control, textarea.form-control {
|
||||
padding-right: 40px;
|
||||
background-image: url(~@/assets/svg/for-css/alert.svg);
|
||||
background-size: 16px 16px;
|
||||
border-color: $red-100 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,3 +277,19 @@ $bg-disabled-control: #34303a;
|
||||
.toggle-switch-container.no-margin {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Disable default style Firefox for invalid elements.
|
||||
// Selectors taken from view-source:resource://gre-resources/forms.css on Firefox
|
||||
:not(output):-moz-ui-invalid {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:not(output):-moz-ui-invalid:-moz-focusring {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
output:-moz-ui-invalid {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@@ -50,3 +50,18 @@
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
right: 24px;
|
||||
cursor: pointer;
|
||||
|
||||
& svg path {
|
||||
stroke: $gray-200;
|
||||
}
|
||||
|
||||
&:hover svg path {
|
||||
stroke: $gray-100;
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
.tier3 {
|
||||
color: #d70e14;
|
||||
|
||||
:hover, :active, :focus {
|
||||
&:hover, &:active, &:focus {
|
||||
color: #d70e14;
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@
|
||||
.tier4 {
|
||||
color: #c24d00;
|
||||
|
||||
&:hover, &:active, :focus {
|
||||
&:hover, &:active, &:focus {
|
||||
color: #c24d00;
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
.tier5 {
|
||||
color: #9e650f;
|
||||
|
||||
:hover, :active, :focus {
|
||||
&:hover, &:active, &:focus {
|
||||
color: #9e650f;
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@
|
||||
.tier6 {
|
||||
color: #2b8363;
|
||||
|
||||
:hover, :active, &:focus {
|
||||
&:hover, &:active, &:focus {
|
||||
color: #2b8363;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// possible values are: normal, fall, habitoween, thanksgiving, winter, nye, birthday, valentines, spring, summer
|
||||
// more to be added on future seasons
|
||||
|
||||
$npc_market_flavor: 'normal';
|
||||
$npc_quests_flavor: 'normal';
|
||||
$npc_seasonal_flavor: 'normal';
|
||||
$npc_timetravelers_flavor: 'normal';
|
||||
$npc_tavern_flavor: 'normal';
|
||||
$npc_market_flavor: 'nye';
|
||||
$npc_quests_flavor: 'nye';
|
||||
$npc_seasonal_flavor: 'nye';
|
||||
$npc_timetravelers_flavor: 'winter';
|
||||
$npc_tavern_flavor: 'nye';
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="52" height="62" viewBox="0 0 52 62">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path stroke="#FFA624" stroke-width="2" d="M18.286 6.252c.186-1.717-.286-3.6-1.658-4.473-1.373-.872-2.831-.21-2.994 1.027-.163 1.237.714 1.988 3.413 4.686.766.766 1.054.477 1.24-1.24z"/>
|
||||
<path stroke="#FFBE5D" stroke-width="2" d="M21.882 8.328c1.395-1.018 3.262-1.552 4.704-.8 1.441.753 1.597 2.347.607 3.107-.99.76-2.079.375-5.764-.613-1.047-.28-.941-.675.453-1.694z"/>
|
||||
<path fill="#EE9109" d="M20.698 6.227c-2.754-1.59-2.924 2.514-1.82 3.152 1.106.637 4.574-1.562 1.82-3.152z"/>
|
||||
<path fill="#F8F9F9" d="M19.167 8.88l10.18 5.878-2.939 5.09-10.18-5.878z"/>
|
||||
<path fill="#DDF3F3" d="M8.986 3.003l10.18 5.877-2.938 5.09-10.18-5.877z"/>
|
||||
<path fill="#FFBE5D" d="M15.773 6.921l6.787 3.918-2.939 5.09-6.787-3.918z"/>
|
||||
<path fill="#FFA624" d="M15.773 6.921l3.394 1.96-2.94 5.09-3.393-1.96z"/>
|
||||
<path fill="#FFA624" d="M19.167 8.88l3.393 1.96-.98 1.696-3.393-1.96z"/>
|
||||
<path fill="#EE9109" d="M15.773 6.921l3.394 1.96-.98 1.696-3.393-1.96z"/>
|
||||
<path fill="#F8F9F9" d="M16.228 13.97l8.483 4.898-6.857 11.877-8.483-4.898z"/>
|
||||
<path fill="#DDF3F3" d="M7.744 9.072l8.484 4.898L9.37 25.847.887 20.95z"/>
|
||||
<path fill="#FFA624" d="M14.531 12.99l1.697.98L9.37 25.847l-1.697-.98z"/>
|
||||
<path fill="#FFBE5D" d="M16.228 13.97l1.696.98-6.857 11.876-1.696-.98z"/>
|
||||
<path fill="#EE9109" d="M8.654 23.171l1.696.98-.98 1.696-1.696-.98zM14.531 12.99l1.697.98-.98 1.697-1.697-.98z"/>
|
||||
<path fill="#C1E9E9" d="M7.744 9.072l6.787 3.919-.98 1.696-6.786-3.918z"/>
|
||||
<path fill="#C1E9E9" d="M7.027 6.396l6.787 3.918-.98 1.697-6.786-3.918zM1.867 19.253l6.787 3.918-.98 1.697-6.787-3.919z"/>
|
||||
<path fill="#DDF3F3" d="M17.924 14.95l6.787 3.918-.98 1.697-6.786-3.918z"/>
|
||||
<path fill="#DDF3F3" d="M20.6 14.233l6.788 3.918-.98 1.697-6.787-3.918zM12.047 25.13l6.787 3.919-.98 1.696-6.787-3.918z"/>
|
||||
<path fill="#FFA624" d="M10.35 24.15l1.697.98-.98 1.697-1.696-.98zM16.228 13.97l1.696.98-.98 1.697-1.696-.98z"/>
|
||||
<path fill="#8EEDF6" d="M32.753 23.621l-.564 2.362 2.228-.966 2.361.564-.965-2.228.564-2.361-2.228.965-2.361-.564zM17.733 45.172l-.564 2.362 2.228-.966 2.361.564-.966-2.228.565-2.361-2.228.965-2.362-.564zM42.733 15.02l.769 2.958 1.905-2.39 2.96-.769-2.391-1.905-.769-2.96-1.906 2.391-2.958.769z"/>
|
||||
<g>
|
||||
<path fill="#F8F9F9" d="M37.688 45.676l9.462-2.536 3.55 13.247-9.463 2.536z"/>
|
||||
<path fill="#DDF3F3" d="M28.225 48.211l9.463-2.535 3.55 13.247-9.463 2.535z"/>
|
||||
<path fill="#FFA624" d="M35.795 46.183l1.893-.507 3.55 13.247-1.893.507z"/>
|
||||
<path fill="#FFBE5D" d="M37.688 45.676l1.892-.507 3.55 13.247-1.893.507z"/>
|
||||
<path fill="#EE9109" d="M38.838 57.537l1.892-.507.507 1.893-1.892.507zM35.795 46.183l1.893-.507.507 1.892-1.893.507z"/>
|
||||
<path fill="#C1E9E9" d="M28.225 48.211l7.57-2.028.507 1.892-7.57 2.028zM31.268 59.566l7.57-2.029.507 1.893-7.57 2.028z"/>
|
||||
<path fill="#DDF3F3" d="M39.58 45.169l7.57-2.029.507 1.893-7.57 2.028zM42.622 56.523l7.57-2.028.507 1.892-7.57 2.029z"/>
|
||||
<path fill="#FFA624" d="M40.73 57.03l1.892-.507.507 1.893-1.892.507zM37.688 45.676l1.892-.507.507 1.892-1.892.507z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path stroke="#FFA624" stroke-width="2" d="M37.861 35.357c-.471-1.661-1.614-3.232-3.214-3.526-1.599-.294-2.703.866-2.39 2.074.311 1.208 1.407 1.575 4.92 3.066.996.423 1.156.047.684-1.614z"/>
|
||||
<path stroke="#FFBE5D" stroke-width="2" d="M41.973 35.935c.911-1.467 2.443-2.662 4.061-2.504 1.619.158 2.36 1.578 1.727 2.653-.633 1.075-1.787 1.126-5.574 1.591-1.075.132-1.125-.273-.214-1.74z"/>
|
||||
<path fill="#EE9109" d="M40.088 34.43c-3.15-.442-1.77 3.426-.506 3.604 1.263.177 3.655-3.161.506-3.604z"/>
|
||||
<path fill="#F8F9F9" d="M39.662 37.464l11.64 1.636-.818 5.82-11.64-1.636z"/>
|
||||
<path fill="#DDF3F3" d="M28.02 35.828l11.642 1.636-.818 5.82-11.641-1.636z"/>
|
||||
<path fill="#FFBE5D" d="M35.781 36.919l7.76 1.09-.817 5.82-7.76-1.09z"/>
|
||||
<path fill="#FFA624" d="M35.781 36.919l3.88.545-.817 5.82-3.88-.545zM39.662 37.464l3.88.545-.273 1.94-3.88-.545z"/>
|
||||
<path fill="#EE9109" d="M35.781 36.919l3.88.545-.272 1.94-3.88-.545z"/>
|
||||
<path fill="#C1E9E9" d="M27.476 39.708l7.76 1.09-.273 1.941-7.76-1.09z"/>
|
||||
<path fill="#DDF3F3" d="M42.997 41.89l7.76 1.09-.273 1.94-7.76-1.09z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1,48 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="48" viewBox="0 0 150 48">
|
||||
<defs>
|
||||
<filter id="a" width="225%" height="225%" x="-62.5%" y="-62.5%" filterUnits="objectBoundingBox">
|
||||
<feOffset dy="2" in="SourceAlpha" result="shadowOffsetOuter1"/>
|
||||
<feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="4"/>
|
||||
<feColorMatrix in="shadowBlurOuter1" result="shadowMatrixOuter1" values="0 0 0 0 0.101960784 0 0 0 0 0.0941176471 0 0 0 0 0.11372549 0 0 0 0.32 0"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="shadowMatrixOuter1"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<g fill="#FFA624">
|
||||
<path d="M125.45 35.414l3.346-2.967-4.382-.896-2.967-3.347-.896 4.382-3.347 2.967 4.382.896 2.967 3.347z" opacity=".75"/>
|
||||
<path d="M136.43 30.407l3.325-.442-2.348-2.395-.442-3.325-2.395 2.348-3.325.442 2.348 2.395.442 3.325z" opacity=".5"/>
|
||||
<path d="M146.901 35.41l2.067.854-.557-2.165.853-2.067-2.165.557-2.067-.853.557 2.165-.853 2.067z" opacity=".25"/>
|
||||
<path d="M138.353 39.874l1.488-.093-.967-1.134-.093-1.488-1.134.967-1.488.093.967 1.134.093 1.488z" opacity=".35"/>
|
||||
</g>
|
||||
<g fill="#FFA624">
|
||||
<path d="M24.55 35.414l-3.346-2.967 4.382-.896 2.967-3.347.896 4.382 3.347 2.967-4.382.896-2.967 3.347z" opacity=".75"/>
|
||||
<path d="M13.57 30.407l-3.325-.442 2.348-2.395.442-3.325 2.395 2.348 3.325.442-2.348 2.395-.442 3.325z" opacity=".5"/>
|
||||
<path d="M3.099 35.41l-2.067.854.557-2.165-.853-2.067 2.165.557 2.067-.853-.557 2.165.853 2.067z" opacity=".25"/>
|
||||
<path d="M11.647 39.874l-1.488-.093.967-1.134.093-1.488 1.134.967 1.488.093-.967 1.134-.093 1.488z" opacity=".35"/>
|
||||
</g>
|
||||
<g opacity=".5" transform="translate(83 22)">
|
||||
<circle cx="12" cy="12" r="12" fill="#FFA623"/>
|
||||
<path fill="#FFF" d="M6.3 17.7c-3.1-3.1-3.1-8.2 0-11.3 3.1-3.1 8.2-3.1 11.3 0" opacity=".5"/>
|
||||
<path fill="#FFF" d="M17.7 6.3c3.1 3.1 3.1 8.2 0 11.3-3.1 3.1-8.2 3.1-11.3 0" opacity=".25"/>
|
||||
<path fill="#BF7D1A" d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm0 18c-4.4 0-8-3.6-8-8s3.6-8 8-8 8 3.6 8 8-3.6 8-8 8z" opacity=".5"/>
|
||||
<path fill="#BF7D1A" d="M13 9v2h-2V9H9v6h2v-2h2v2h2V9z" opacity=".75"/>
|
||||
</g>
|
||||
<g opacity=".5" transform="translate(43 22)">
|
||||
<circle cx="12" cy="12" r="12" fill="#FFA623"/>
|
||||
<path fill="#FFF" d="M6.3 17.7c-3.1-3.1-3.1-8.2 0-11.3 3.1-3.1 8.2-3.1 11.3 0" opacity=".5"/>
|
||||
<path fill="#FFF" d="M17.7 6.3c3.1 3.1 3.1 8.2 0 11.3-3.1 3.1-8.2 3.1-11.3 0" opacity=".25"/>
|
||||
<path fill="#BF7D1A" d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm0 18c-4.4 0-8-3.6-8-8s3.6-8 8-8 8 3.6 8 8-3.6 8-8 8z" opacity=".5"/>
|
||||
<path fill="#BF7D1A" d="M13 9v2h-2V9H9v6h2v-2h2v2h2V9z" opacity=".75"/>
|
||||
</g>
|
||||
<g transform="translate(59 6)">
|
||||
<circle cx="16" cy="16" r="16" fill="#FFA623"/>
|
||||
<path fill="#FFF" d="M8.4 23.6c-4.133-4.133-4.133-10.933 0-15.067 4.133-4.133 10.933-4.133 15.067 0" opacity=".5"/>
|
||||
<path fill="#FFF" d="M23.6 8.4c4.133 4.133 4.133 10.933 0 15.067-4.133 4.133-10.933 4.133-15.067 0" opacity=".25"/>
|
||||
<path fill="#BF7D1A" d="M16 2.667c-7.333 0-13.333 6-13.333 13.333s6 13.333 13.333 13.333 13.333-6 13.333-13.333S23.333 2.667 16 2.667zm0 24c-5.867 0-10.667-4.8-10.667-10.667S10.133 5.333 16 5.333 26.667 10.133 26.667 16 21.867 26.667 16 26.667z" opacity=".5"/>
|
||||
<path fill="#BF7D1A" d="M17.333 12v2.667h-2.666V12H12v8h2.667v-2.667h2.666V20H20v-8z" opacity=".75"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="11" viewBox="0 0 12 11">
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M6 8.76l-3.527 2.094.902-4.001L.294 4.146l4.084-.379L6 0l1.622 3.767 4.084.379-3.081 2.707.902 4.001z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 241 B |
@@ -1,104 +1,16 @@
|
||||
<template>
|
||||
<div
|
||||
class="modal-footer"
|
||||
style="margin-top:0"
|
||||
ng-init="loadWidgets()"
|
||||
>
|
||||
<div class="container-fluid share-buttons">
|
||||
<div class="row">
|
||||
<div class="col-12 text-center">
|
||||
<a
|
||||
class="twitter-share-button share-button"
|
||||
:href="twitterLink"
|
||||
target="_blank"
|
||||
>
|
||||
<div
|
||||
class="social-icon twitter svg-icon"
|
||||
v-html="icons.twitter"
|
||||
></div>
|
||||
{{ $t('tweet') }}
|
||||
</a>
|
||||
<a
|
||||
class="fb-share-button share-button"
|
||||
:href="facebookLink"
|
||||
target="_blank"
|
||||
>
|
||||
<div
|
||||
class="social-icon facebook svg-icon"
|
||||
v-html="icons.facebook"
|
||||
></div>
|
||||
{{ $t('share') }}
|
||||
</a>
|
||||
<!-- @TODO: Still want this?
|
||||
.col-4a.tumblr-share-button(:data-href='socialLevelLink', data-notes='none')-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO disabled for now, see https://github.com/HabitRPG/habitica/issues/11283 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.share-buttons {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.share-button {
|
||||
display: inline-block;
|
||||
width: 77px;
|
||||
padding: .5em;
|
||||
border-radius: 2px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.fb-share-button {
|
||||
background-color: #2995cd;
|
||||
}
|
||||
|
||||
.twitter-share-button {
|
||||
margin-right: .5em;
|
||||
background-color: #3bcad7;
|
||||
}
|
||||
|
||||
.social-icon {
|
||||
width: 16px;
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
margin-right: .5em;
|
||||
}
|
||||
|
||||
.social-icon.facebook svg {
|
||||
width: 7.5px;
|
||||
margin-bottom: .2em;
|
||||
}
|
||||
|
||||
.social-icon.twitter {
|
||||
margin-bottom: .2em;
|
||||
}
|
||||
<style lang="scss" scoped>
|
||||
.modal-footer {
|
||||
border-top: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// @TODO:
|
||||
import twitter from '@/assets/svg/twitter.svg';
|
||||
import facebook from '@/assets/svg/facebook.svg';
|
||||
|
||||
const BASE_URL = 'https://habitica.com';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
const tweet = this.$t('achievementShare');
|
||||
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
twitter,
|
||||
facebook,
|
||||
}),
|
||||
tweet,
|
||||
achievementLink: `${BASE_URL}`,
|
||||
twitterLink: `https://twitter.com/intent/tweet?text=${tweet}&via=habitica&url=${BASE_URL}&count=none`,
|
||||
facebookLink: `https://www.facebook.com/sharer/sharer.php?text=${tweet}&u=${BASE_URL}`,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -225,30 +225,30 @@ export default {
|
||||
classGear (heroClass) {
|
||||
if (heroClass === 'rogue') {
|
||||
return {
|
||||
armor: 'armor_rogue_5',
|
||||
head: 'head_rogue_5',
|
||||
shield: 'shield_rogue_6',
|
||||
weapon: 'weapon_rogue_6',
|
||||
armor: 'armor_special_winter2020Rogue',
|
||||
head: 'head_special_winter2020Rogue',
|
||||
shield: 'shield_special_winter2020Rogue',
|
||||
weapon: 'weapon_special_winter2020Rogue',
|
||||
};
|
||||
} if (heroClass === 'wizard') {
|
||||
return {
|
||||
armor: 'armor_wizard_5',
|
||||
head: 'head_wizard_5',
|
||||
weapon: 'weapon_wizard_6',
|
||||
armor: 'armor_special_winter2020Mage',
|
||||
head: 'head_special_winter2020Mage',
|
||||
weapon: 'weapon_special_winter2020Mage',
|
||||
};
|
||||
} if (heroClass === 'healer') {
|
||||
return {
|
||||
armor: 'armor_healer_5',
|
||||
head: 'head_healer_5',
|
||||
shield: 'shield_healer_5',
|
||||
weapon: 'weapon_healer_6',
|
||||
armor: 'armor_special_winter2020Healer',
|
||||
head: 'head_special_winter2020Healer',
|
||||
shield: 'shield_special_winter2020Healer',
|
||||
weapon: 'weapon_special_winter2020Healer',
|
||||
};
|
||||
}
|
||||
return {
|
||||
armor: 'armor_warrior_5',
|
||||
head: 'head_warrior_5',
|
||||
shield: 'shield_warrior_5',
|
||||
weapon: 'weapon_warrior_6',
|
||||
armor: 'armor_special_winter2020Warrior',
|
||||
head: 'head_special_winter2020Warrior',
|
||||
shield: 'shield_special_winter2020Warrior',
|
||||
weapon: 'weapon_special_winter2020Warrior',
|
||||
};
|
||||
},
|
||||
selectionBox (selectedClass, heroClass) {
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
:avatar-only="true"
|
||||
:with-background="true"
|
||||
/>
|
||||
<!-- @TOOD: Sleep +generatedAvatar({sleep:true})-->
|
||||
<!-- @TODO: Sleep +generatedAvatar({sleep:true})-->
|
||||
<span class="knockout"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,14 +3,32 @@
|
||||
id="generic-achievement"
|
||||
:title="data.message"
|
||||
size="md"
|
||||
:hide-footer="true"
|
||||
:hide-header="true"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<div class="col-12">
|
||||
<achievement-avatar class="avatar" />
|
||||
<span
|
||||
class="close-icon svg-icon inline icon-10"
|
||||
@click="close()"
|
||||
v-html="icons.close"
|
||||
></span>
|
||||
<div class="content">
|
||||
<div
|
||||
v-once
|
||||
class="dialog-header title"
|
||||
>
|
||||
{{ $t('earnedAchievement') }}
|
||||
</div>
|
||||
<div class="col-6 offset-3 text-center">
|
||||
<p v-html="data.modalText"></p>
|
||||
<div class="inner-content">
|
||||
<div class="achievement-background d-flex align-items-center">
|
||||
<div
|
||||
class="icon"
|
||||
:class="achievementClass"
|
||||
></div>
|
||||
</div>
|
||||
<h4
|
||||
class="title"
|
||||
v-html="$t(achievement.titleKey)"
|
||||
>
|
||||
</h4>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="close()"
|
||||
@@ -19,33 +37,86 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<achievement-footer />
|
||||
<div
|
||||
slot="modal-footer"
|
||||
class="clearfix"
|
||||
></div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.avatar {
|
||||
width: 140px;
|
||||
<style lang="scss">
|
||||
@import '~@/assets/scss/modal.scss';
|
||||
|
||||
#generic-achievement {
|
||||
@include centeredModal();
|
||||
|
||||
.modal-dialog {
|
||||
width: 330px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding-top: 0px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
.content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.inner-content {
|
||||
margin: 24px auto auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.achievement-background {
|
||||
width: 112px;
|
||||
height: 112px;
|
||||
border-radius: 4px;
|
||||
background-color: $gray-700;
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
margin-top: 16px !important;
|
||||
color: $purple-200 !important;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: 24px !important;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin: 0 auto;
|
||||
margin-bottom: 1.5em;
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import achievementFooter from './achievementFooter';
|
||||
import achievementAvatar from './achievementAvatar';
|
||||
|
||||
import { mapState } from '@/libs/store';
|
||||
import achievements from '@/../../common/script/content/achievements';
|
||||
import svgClose from '@/assets/svg/close.svg';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
achievementFooter,
|
||||
achievementAvatar,
|
||||
},
|
||||
props: ['data'],
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
close: svgClose,
|
||||
}),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
achievement () {
|
||||
return achievements[this.data.achievement];
|
||||
},
|
||||
achievementClass () {
|
||||
return `${this.achievement.icon}2x`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
|
||||
@@ -27,36 +27,6 @@
|
||||
label(style='display:inline-block') {{ $t('dontShowAgain') }}
|
||||
-->
|
||||
</div>
|
||||
<div class="container-fluid share-buttons">
|
||||
<div class="row">
|
||||
<div class="col-12 text-center">
|
||||
<a
|
||||
class="twitter-share-button share-button"
|
||||
:href="twitterLink"
|
||||
target="_blank"
|
||||
>
|
||||
<div
|
||||
class="social-icon twitter svg-icon"
|
||||
v-html="icons.twitter"
|
||||
></div>
|
||||
{{ $t('tweet') }}
|
||||
</a>
|
||||
<a
|
||||
class="fb-share-button share-button"
|
||||
:href="facebookLink"
|
||||
target="_blank"
|
||||
>
|
||||
<div
|
||||
class="social-icon facebook svg-icon"
|
||||
v-html="icons.facebook"
|
||||
></div>
|
||||
{{ $t('share') }}
|
||||
</a>
|
||||
</div>
|
||||
<!-- @TODO: Still want this? .col-4a.tumblr
|
||||
-share-button(:data-href='socialLevelLink', data-notes='none')-->
|
||||
</div>
|
||||
</div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
@@ -72,7 +42,6 @@ label(style='display:inline-block') {{ $t('dontShowAgain') }}
|
||||
|
||||
.modal-body {
|
||||
padding-top: 1em;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
@@ -99,45 +68,6 @@ label(style='display:inline-block') {{ $t('dontShowAgain') }}
|
||||
margin-top: 1em;
|
||||
min-height: 0px;
|
||||
}
|
||||
|
||||
.share-buttons {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.share-button {
|
||||
display: inline-block;
|
||||
width: 77px;
|
||||
padding: .5em;
|
||||
border-radius: 2px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.fb-share-button {
|
||||
background-color: #2995cd;
|
||||
}
|
||||
|
||||
.twitter-share-button {
|
||||
margin-right: .5em;
|
||||
background-color: #3bcad7;
|
||||
}
|
||||
|
||||
.social-icon {
|
||||
width: 16px;
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
margin-right: .5em;
|
||||
}
|
||||
|
||||
.social-icon.facebook svg {
|
||||
width: 7.5px;
|
||||
margin-bottom: .2em;
|
||||
}
|
||||
|
||||
.social-icon.twitter {
|
||||
margin-bottom: .2em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -152,10 +82,6 @@ import Avatar from '../avatar';
|
||||
import { mapState } from '@/libs/store';
|
||||
import { MAX_HEALTH as maxHealth } from '@/../../common/script/constants';
|
||||
import styleHelper from '@/mixins/styleHelper';
|
||||
import twitter from '@/assets/svg/twitter.svg';
|
||||
import facebook from '@/assets/svg/facebook.svg';
|
||||
|
||||
const BASE_URL = 'https://habitica.com';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -163,18 +89,9 @@ export default {
|
||||
},
|
||||
mixins: [styleHelper],
|
||||
data () {
|
||||
const tweet = this.$t('levelUpShare');
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
twitter,
|
||||
facebook,
|
||||
}),
|
||||
statsAllocationBoxIsOpen: true,
|
||||
maxHealth,
|
||||
tweet,
|
||||
socialLevelLink: `${BASE_URL}/social/level-up`,
|
||||
twitterLink: `https://twitter.com/intent/tweet?text=${tweet}&via=habitica&url=${BASE_URL}/social/level-up&count=none`,
|
||||
facebookLink: `https://www.facebook.com/sharer/sharer.php?text=${tweet}&u=${BASE_URL}/social/level-up`,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -183,16 +100,10 @@ export default {
|
||||
return this.$store.getters['members:hasClass'](this.user) && !this.user.preferences.automaticAllocation;
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.loadWidgets();
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'level-up');
|
||||
},
|
||||
loadWidgets () {
|
||||
// @TODO:
|
||||
},
|
||||
changeLevelupSuppress () {
|
||||
// @TODO: dispatch set({"preferences.suppressModals.levelUp":
|
||||
// user.preferences.suppressModals.levelUp?true: false})
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="onboarding-complete"
|
||||
size="sm"
|
||||
:hide-footer="true"
|
||||
:hide-header="true"
|
||||
>
|
||||
<div class="content text-center">
|
||||
<span
|
||||
class="close-icon svg-icon inline icon-10"
|
||||
@click="close()"
|
||||
v-html="icons.close"
|
||||
></span>
|
||||
<h2>{{ $t('congratulations') }}</h2>
|
||||
<img
|
||||
class="onboarding-complete-banner d-block"
|
||||
src="~@/assets/images/onboarding-complete-banner@2x.png"
|
||||
>
|
||||
<p
|
||||
class="onboarding-complete-text"
|
||||
v-html="$t('onboardingCompleteDesc')"
|
||||
></p>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="closeWithAction()"
|
||||
>
|
||||
{{ $t('viewAchievements') }}
|
||||
</button>
|
||||
</div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
#onboarding-complete {
|
||||
.modal-content {
|
||||
min-width: 330px;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding-top: 1em;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
h2 {
|
||||
color: $purple-200;
|
||||
margin-bottom: 24px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 0 8px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.onboarding-complete-banner {
|
||||
width: 282px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.onboarding-complete-text {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.onboarding-complete-text ::v-deep .gold-amount {
|
||||
color: $yellow-5;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import svgClose from '@/assets/svg/close.svg';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
close: svgClose,
|
||||
}),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'onboarding-complete');
|
||||
},
|
||||
closeWithAction () {
|
||||
this.close();
|
||||
setTimeout(() => {
|
||||
this.$router.push({ name: 'achievements' });
|
||||
}, 200);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -30,10 +30,10 @@
|
||||
</button>
|
||||
<div class="checkbox">
|
||||
<input
|
||||
id="user-preferences-suppressModals-streak"
|
||||
v-model="user.preferences.suppressModals.streak"
|
||||
type="checkbox"
|
||||
@change="suppressModals"
|
||||
id="user-preferences-suppressModals-streak"
|
||||
>
|
||||
<label for="user-preferences-suppressModals-streak">{{ $t('dontShowAgain') }}</label>
|
||||
</div>
|
||||
@@ -71,8 +71,8 @@ export default {
|
||||
this.$root.$emit('bv::hide::modal', 'streak');
|
||||
},
|
||||
suppressModals () {
|
||||
const surpress = !!this.user.preferences.suppressModals.streak;
|
||||
this.$store.dispatch('user:set', { 'preferences.suppressModals.streak': surpress });
|
||||
const suppress = !!this.user.preferences.suppressModals.streak;
|
||||
this.$store.dispatch('user:set', { 'preferences.suppressModals.streak': suppress });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -46,6 +46,13 @@
|
||||
:placeholder="$t('usernamePlaceholder')"
|
||||
:class="{'input-valid': usernameValid, 'input-invalid': usernameInvalid}"
|
||||
>
|
||||
<div
|
||||
v-for="issue in usernameIssues"
|
||||
:key="issue"
|
||||
class="input-error"
|
||||
>
|
||||
{{ issue }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!registering"
|
||||
@@ -97,7 +104,17 @@
|
||||
class="form-control"
|
||||
type="password"
|
||||
:placeholder="$t(registering ? 'passwordPlaceholder' : 'password')"
|
||||
:class="{
|
||||
'input-valid': registering ? passwordValid : false,
|
||||
'input-invalid': registering ? passwordInvalid: false,
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-if="passwordInvalid && registering"
|
||||
class="input-error"
|
||||
>
|
||||
{{ $t('minPasswordLength') }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="registering"
|
||||
@@ -115,6 +132,12 @@
|
||||
:placeholder="$t('confirmPasswordPlaceholder')"
|
||||
:class="{'input-invalid': passwordConfirmInvalid, 'input-valid': passwordConfirmValid}"
|
||||
>
|
||||
<div
|
||||
v-if="passwordConfirmInvalid"
|
||||
class="input-error"
|
||||
>
|
||||
{{ $t('passwordConfirmationMatch') }}
|
||||
</div>
|
||||
<small
|
||||
v-once
|
||||
class="form-text"
|
||||
@@ -183,8 +206,11 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.input-valid {
|
||||
color: #fff;
|
||||
.input-error {
|
||||
margin-top: 0.25em;
|
||||
font-weight: normal;
|
||||
font-size: 90%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -194,7 +220,7 @@ import hello from 'hellojs';
|
||||
import debounce from 'lodash/debounce';
|
||||
import isEmail from 'validator/lib/isEmail';
|
||||
import { setUpAxios } from '@/libs/auth';
|
||||
|
||||
import { MINIMUM_PASSWORD_LENGTH } from '@/../../common/script/constants';
|
||||
import facebookSquareIcon from '@/assets/svg/facebook-square.svg';
|
||||
import googleIcon from '@/assets/svg/google.svg';
|
||||
|
||||
@@ -223,6 +249,7 @@ export default {
|
||||
return isEmail(this.email);
|
||||
},
|
||||
emailInvalid () {
|
||||
if (this.email.length <= 3) return false;
|
||||
return !this.emailValid;
|
||||
},
|
||||
usernameValid () {
|
||||
@@ -230,13 +257,23 @@ export default {
|
||||
return this.usernameIssues.length === 0;
|
||||
},
|
||||
usernameInvalid () {
|
||||
if (this.username.length < 1) return false;
|
||||
return !this.usernameValid;
|
||||
},
|
||||
passwordValid () {
|
||||
if (this.password.length <= 0) return false;
|
||||
return this.password.length >= MINIMUM_PASSWORD_LENGTH;
|
||||
},
|
||||
passwordInvalid () {
|
||||
if (this.password.length <= 0) return false;
|
||||
return this.password.length < MINIMUM_PASSWORD_LENGTH;
|
||||
},
|
||||
passwordConfirmValid () {
|
||||
if (this.passwordConfirm.length <= 3) return false;
|
||||
return this.passwordConfirm === this.password;
|
||||
},
|
||||
passwordConfirmInvalid () {
|
||||
if (this.passwordConfirm.length <= 3) return false;
|
||||
return !this.passwordConfirmValid;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
<template>
|
||||
<div class="form-wrapper">
|
||||
<div class="warning-banner d-flex" v-if="forgotPassword && preOutage">
|
||||
<div class="warning-box ml-auto my-auto mr-2 d-flex">
|
||||
<div
|
||||
class="svg-icon exclamation m-auto"
|
||||
v-html="icons.exclamation"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mr-auto my-auto">
|
||||
Habitica emails will be temporarily unavailable on <strong>January 11, 2020</strong> from
|
||||
<strong>1:00 - 7:00 AM EST</strong>.
|
||||
</div>
|
||||
</div>
|
||||
<div id="top-background">
|
||||
<div class="seamless_stars_varied_opacity_repeat"></div>
|
||||
</div>
|
||||
<form
|
||||
v-if="!forgotPassword && !resetPasswordSetNewOne"
|
||||
id="login-form"
|
||||
@submit.prevent="handleSubmit"
|
||||
@keyup.enter="handleSubmit"
|
||||
@submit.prevent.stop="handleSubmit"
|
||||
>
|
||||
<div class="text-center">
|
||||
<div>
|
||||
@@ -69,7 +81,7 @@
|
||||
<input
|
||||
id="usernameInput"
|
||||
v-model="username"
|
||||
class="form-control"
|
||||
class="form-control input-with-error"
|
||||
type="text"
|
||||
:placeholder="$t('usernamePlaceholder')"
|
||||
:class="{'input-valid': usernameValid, 'input-invalid': usernameInvalid}"
|
||||
@@ -132,7 +144,17 @@
|
||||
class="form-control"
|
||||
type="password"
|
||||
:placeholder="$t(registering ? 'passwordPlaceholder' : 'password')"
|
||||
:class="{
|
||||
'input-invalid input-with-error': registering && passwordInvalid,
|
||||
'input-valid': registering && passwordValid
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-if="passwordInvalid && registering"
|
||||
class="input-error"
|
||||
>
|
||||
{{ $t('minPasswordLength') }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="registering"
|
||||
@@ -145,11 +167,17 @@
|
||||
<input
|
||||
id="confirmPasswordInput"
|
||||
v-model="passwordConfirm"
|
||||
class="form-control"
|
||||
class="form-control input-with-error"
|
||||
type="password"
|
||||
:placeholder="$t('confirmPasswordPlaceholder')"
|
||||
:class="{'input-invalid': passwordConfirmInvalid, 'input-valid': passwordConfirmValid}"
|
||||
>
|
||||
<div
|
||||
v-if="passwordConfirmInvalid"
|
||||
class="input-error"
|
||||
>
|
||||
{{ $t('passwordConfirmationMatch') }}
|
||||
</div>
|
||||
<small
|
||||
v-once
|
||||
class="form-text"
|
||||
@@ -157,22 +185,22 @@
|
||||
></small>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div
|
||||
<button
|
||||
v-if="registering"
|
||||
v-once
|
||||
type="submit"
|
||||
class="btn btn-info"
|
||||
@click="register()"
|
||||
:disabled="signupFormInvalid"
|
||||
>
|
||||
{{ $t('joinHabitica') }}
|
||||
</div>
|
||||
<div
|
||||
</button>
|
||||
<button
|
||||
v-if="!registering"
|
||||
v-once
|
||||
type="submit"
|
||||
class="btn btn-info"
|
||||
@click="login()"
|
||||
>
|
||||
{{ $t('login') }}
|
||||
</div>
|
||||
</button>
|
||||
<div class="toggle-links">
|
||||
<router-link
|
||||
v-if="registering"
|
||||
@@ -275,10 +303,17 @@
|
||||
<input
|
||||
id="passwordInput"
|
||||
v-model="password"
|
||||
class="form-control"
|
||||
class="form-control input-with-error"
|
||||
type="password"
|
||||
:placeholder="$t('password')"
|
||||
:class="{'input-invalid': passwordInvalid, 'input-valid': passwordValid}"
|
||||
>
|
||||
<div
|
||||
v-if="passwordInvalid"
|
||||
class="input-error"
|
||||
>
|
||||
{{ $t('minPasswordLength') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
@@ -288,10 +323,17 @@
|
||||
<input
|
||||
id="confirmPasswordInput"
|
||||
v-model="passwordConfirm"
|
||||
class="form-control"
|
||||
class="form-control input-with-error"
|
||||
type="password"
|
||||
:placeholder="$t('confirmPasswordPlaceholder')"
|
||||
:class="{'input-invalid': passwordConfirmInvalid, 'input-valid': passwordConfirmValid}"
|
||||
>
|
||||
<div
|
||||
v-if="passwordConfirmInvalid"
|
||||
class="input-error"
|
||||
>
|
||||
{{ $t('passwordConfirmationMatch') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div
|
||||
@@ -426,10 +468,14 @@
|
||||
color: $white;
|
||||
}
|
||||
|
||||
#usernameInput.input-invalid {
|
||||
.input-with-error.input-invalid {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
#confirmPasswordInput + .input-error {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.form-text {
|
||||
font-size: 14px;
|
||||
color: $white;
|
||||
@@ -480,7 +526,7 @@
|
||||
background-image: url('~@/assets/images/auth/seamless_mountains_demo.png');
|
||||
background-repeat: repeat-x;
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
height: 300px;
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
bottom: 0;
|
||||
@@ -512,16 +558,37 @@
|
||||
color: #fff;
|
||||
font-size: 90%;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.warning-banner {
|
||||
color: $white;
|
||||
background-color: $maroon-100;
|
||||
height: 2.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.warning-box {
|
||||
font-weight: bold;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border: 2px solid;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.exclamation {
|
||||
width: 2px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import hello from 'hellojs';
|
||||
import moment from 'moment';
|
||||
import debounce from 'lodash/debounce';
|
||||
import isEmail from 'validator/lib/isEmail';
|
||||
|
||||
import { MINIMUM_PASSWORD_LENGTH } from '@/../../common/script/constants';
|
||||
import exclamation from '@/assets/svg/exclamation.svg';
|
||||
import gryphon from '@/assets/svg/gryphon.svg';
|
||||
import habiticaIcon from '@/assets/svg/habitica-logo.svg';
|
||||
import facebookSquareIcon from '@/assets/svg/facebook-square.svg';
|
||||
@@ -543,6 +610,7 @@ export default {
|
||||
};
|
||||
|
||||
data.icons = Object.freeze({
|
||||
exclamation,
|
||||
gryphon,
|
||||
habiticaIcon,
|
||||
facebookIcon: facebookSquareIcon,
|
||||
@@ -580,6 +648,14 @@ export default {
|
||||
if (this.username.length < 1) return false;
|
||||
return !this.usernameValid;
|
||||
},
|
||||
passwordValid () {
|
||||
if (this.password.length <= 0) return false;
|
||||
return this.password.length >= MINIMUM_PASSWORD_LENGTH;
|
||||
},
|
||||
passwordInvalid () {
|
||||
if (this.password.length <= 0) return false;
|
||||
return this.password.length < MINIMUM_PASSWORD_LENGTH;
|
||||
},
|
||||
passwordConfirmValid () {
|
||||
if (this.passwordConfirm.length <= 3) return false;
|
||||
return this.passwordConfirm === this.password;
|
||||
@@ -588,6 +664,15 @@ export default {
|
||||
if (this.passwordConfirm.length <= 3) return false;
|
||||
return !this.passwordConfirmValid;
|
||||
},
|
||||
signupFormInvalid () {
|
||||
return this.usernameInvalid
|
||||
|| this.emailInvalid
|
||||
|| this.passwordInvalid
|
||||
|| this.passwordConfirmInvalid;
|
||||
},
|
||||
preOutage () {
|
||||
return moment.utc().isBefore('2020-01-12');
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route: {
|
||||
@@ -653,7 +738,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
// @TODO: implement langauge and invite accepting
|
||||
// @TODO: implement language and invite accepting
|
||||
// var url = ApiUrl.get() + "/api/v4/user/auth/local/register";
|
||||
// if (location.search && location.search.indexOf('Invite=') !== -1)
|
||||
// { // matches groupInvite and partyInvite
|
||||
|
||||
@@ -212,7 +212,7 @@ export default {
|
||||
},
|
||||
visualBuffs () {
|
||||
return {
|
||||
snowball: 'snowman',
|
||||
snowball: `avatar_snowball_${this.member.stats.class}`,
|
||||
spookySparkles: 'ghost',
|
||||
shinySeed: `avatar_floral_${this.member.stats.class}`,
|
||||
seafoam: 'seafoam_star',
|
||||
|
||||
@@ -151,7 +151,13 @@ export default {
|
||||
'blackTopFrame', 'blueTopFrame', 'greenTopFrame', 'pinkTopFrame', 'redTopFrame', 'whiteTopFrame', 'yellowTopFrame',
|
||||
'blackHalfMoon', 'blueHalfMoon', 'greenHalfMoon', 'pinkHalfMoon', 'redHalfMoon', 'whiteHalfMoon', 'yellowHalfMoon',
|
||||
];
|
||||
const options = keys.map(key => {
|
||||
const noneOption = this.createGearItem(0, 'eyewear', 'base');
|
||||
noneOption.none = true;
|
||||
const options = [
|
||||
noneOption,
|
||||
];
|
||||
|
||||
for (const key of keys) {
|
||||
const newKey = `eyewear_special_${key}`;
|
||||
const option = {};
|
||||
option.key = key;
|
||||
@@ -164,8 +170,9 @@ export default {
|
||||
|
||||
return this.equip(newKey, type);
|
||||
};
|
||||
return option;
|
||||
});
|
||||
options.push(option);
|
||||
}
|
||||
|
||||
return options;
|
||||
},
|
||||
freeShirts () {
|
||||
@@ -179,20 +186,18 @@ export default {
|
||||
},
|
||||
headbands () {
|
||||
const keys = ['blackHeadband', 'blueHeadband', 'greenHeadband', 'pinkHeadband', 'redHeadband', 'whiteHeadband', 'yellowHeadband'];
|
||||
const options = keys.map(key => {
|
||||
const newKey = `headAccessory_special_${key}`;
|
||||
const option = {};
|
||||
option.key = key;
|
||||
option.active = this.user.preferences.costume
|
||||
? this.user.items.gear.costume.headAccessory === newKey
|
||||
: this.user.items.gear.equipped.headAccessory === newKey;
|
||||
option.class = `headAccessory_special_${option.key} headband`;
|
||||
option.click = () => {
|
||||
const type = this.user.preferences.costume ? 'costume' : 'equipped';
|
||||
return this.equip(newKey, type);
|
||||
};
|
||||
return option;
|
||||
});
|
||||
const noneOption = this.createGearItem(0, 'headAccessory', 'base', 'headband');
|
||||
noneOption.none = true;
|
||||
const options = [
|
||||
noneOption,
|
||||
];
|
||||
|
||||
for (const key of keys) {
|
||||
const option = this.createGearItem(key, 'headAccessory', 'special', 'headband');
|
||||
|
||||
options.push(option);
|
||||
}
|
||||
|
||||
return options;
|
||||
},
|
||||
chairs () {
|
||||
@@ -234,7 +239,14 @@ export default {
|
||||
// user purchases object, this is not recomputed. Hack for now
|
||||
let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
|
||||
const keys = this.animalItemKeys[category];
|
||||
const options = keys.map(key => {
|
||||
|
||||
const noneOption = this.createGearItem(0, category, 'base', category);
|
||||
noneOption.none = true;
|
||||
const options = [
|
||||
noneOption,
|
||||
];
|
||||
|
||||
for (const key of keys) {
|
||||
const newKey = `${category}_special_${key}`;
|
||||
const userPurchased = this.user.items.gear.owned[newKey];
|
||||
|
||||
@@ -265,8 +277,10 @@ export default {
|
||||
const type = this.user.preferences.costume ? 'costume' : 'equipped';
|
||||
return this.equip(newKey, type);
|
||||
};
|
||||
return option;
|
||||
});
|
||||
|
||||
options.push(option);
|
||||
}
|
||||
|
||||
return options;
|
||||
},
|
||||
animalItemsUnlockString (category) {
|
||||
@@ -285,6 +299,44 @@ export default {
|
||||
});
|
||||
return own;
|
||||
},
|
||||
createGearItem (key, gearType, subGearType, additionalClass) {
|
||||
const newKey = `${gearType}_${subGearType ? `${subGearType}_` : ''}${key}`;
|
||||
const option = {};
|
||||
option.key = key;
|
||||
const visibleGearType = this.user.preferences.costume ? 'costume' : 'equipped';
|
||||
const currentlyEquippedValue = this.user.items.gear[visibleGearType][gearType];
|
||||
|
||||
option.active = currentlyEquippedValue === newKey;
|
||||
|
||||
if (key === 0) {
|
||||
// if key is the "none" option check if a property
|
||||
// doesn't have a value and mark it as active
|
||||
option.active = option.active || !currentlyEquippedValue;
|
||||
}
|
||||
|
||||
option.class = `${newKey} ${additionalClass}`;
|
||||
option.click = () => {
|
||||
const type = this.user.preferences.costume ? 'costume' : 'equipped';
|
||||
const currentlyEquipped = this.user.items.gear[type][gearType];
|
||||
|
||||
// no need to call api/equip-op if its already selected
|
||||
if (currentlyEquipped === newKey || (key === 0 && !currentlyEquipped)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let keyToEquip = newKey;
|
||||
|
||||
if (option.none) {
|
||||
// you need to "equip" the current selected AGAIN in order to un-equip it
|
||||
// the "none-key" isn't allowed to be sent
|
||||
keyToEquip = currentlyEquipped;
|
||||
}
|
||||
|
||||
this.equip(keyToEquip, type);
|
||||
};
|
||||
|
||||
return option;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<challenge-modal @updatedChallenge="updatedChallenge" />
|
||||
<leave-challenge-modal :challenge-id="challenge._id" />
|
||||
<leave-challenge-modal
|
||||
:challenge-id="challenge._id"
|
||||
@update-challenge="updateChallenge"
|
||||
/>
|
||||
<close-challenge-modal
|
||||
:members="members"
|
||||
:challenge-id="challenge._id"
|
||||
@@ -493,12 +496,20 @@ export default {
|
||||
},
|
||||
async joinChallenge () {
|
||||
this.user.challenges.push(this.searchId);
|
||||
await this.$store.dispatch('challenges:joinChallenge', { challengeId: this.searchId });
|
||||
this.challenge = await this.$store.dispatch('challenges:joinChallenge', { challengeId: this.searchId });
|
||||
this.members = await this
|
||||
.loadMembers({ challengeId: this.searchId, includeAllPublicFields: true });
|
||||
|
||||
await this.$store.dispatch('tasks:fetchUserTasks', { forceLoad: true });
|
||||
},
|
||||
async leaveChallenge () {
|
||||
this.$root.$emit('bv::show::modal', 'leave-challenge-modal');
|
||||
},
|
||||
async updateChallenge () {
|
||||
this.challenge = await this.$store.dispatch('challenges:getChallenge', { challengeId: this.searchId });
|
||||
this.members = await this
|
||||
.loadMembers({ challengeId: this.searchId, includeAllPublicFields: true });
|
||||
},
|
||||
closeChallenge () {
|
||||
this.$root.$emit('bv::show::modal', 'close-challenge-modal');
|
||||
},
|
||||
|
||||
@@ -215,7 +215,6 @@
|
||||
}
|
||||
|
||||
.meta-info {
|
||||
height: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
@@ -295,8 +294,9 @@
|
||||
justify-content: space-evenly;
|
||||
background-color: $gray-700;
|
||||
text-align: center;
|
||||
padding: 16px;
|
||||
padding: 8px;
|
||||
border-radius: .25em;
|
||||
flex-wrap: wrap;
|
||||
|
||||
> div {
|
||||
.value {
|
||||
@@ -327,6 +327,8 @@
|
||||
}
|
||||
|
||||
> div.muted {
|
||||
margin: 8px;
|
||||
|
||||
.value {
|
||||
opacity: 0.5;
|
||||
font-size: 20px;
|
||||
|
||||
@@ -595,7 +595,7 @@ export default {
|
||||
this.$root.$emit('bv::hide::modal', 'challenge-modal');
|
||||
this.$router.push(`/challenges/${challenge._id}`);
|
||||
},
|
||||
updateChallenge () {
|
||||
async updateChallenge () {
|
||||
const categoryKeys = this.workingChallenge.categories;
|
||||
const serverCategories = [];
|
||||
categoryKeys.forEach(key => {
|
||||
@@ -610,10 +610,8 @@ export default {
|
||||
const challengeDetails = clone(this.workingChallenge);
|
||||
challengeDetails.categories = serverCategories;
|
||||
|
||||
this.$emit('updatedChallenge', {
|
||||
challenge: challengeDetails,
|
||||
});
|
||||
this.$store.dispatch('challenges:updateChallenge', { challenge: challengeDetails });
|
||||
const challenge = await this.$store.dispatch('challenges:updateChallenge', { challenge: challengeDetails });
|
||||
this.$emit('updatedChallenge', { challenge });
|
||||
this.resetWorkingChallenge();
|
||||
this.$root.$emit('bv::hide::modal', 'challenge-modal');
|
||||
},
|
||||
|
||||
@@ -68,6 +68,17 @@
|
||||
<style lang='scss' scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.header-row {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.col-10.standard-page {
|
||||
// full width on smaller devices
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.header-row {
|
||||
h1 {
|
||||
color: $purple-200;
|
||||
|
||||
@@ -54,6 +54,7 @@ export default {
|
||||
this.close();
|
||||
},
|
||||
close () {
|
||||
this.$emit('update-challenge');
|
||||
this.$root.$emit('bv::hide::modal', 'leave-challenge-modal');
|
||||
},
|
||||
},
|
||||
|
||||
@@ -88,6 +88,17 @@
|
||||
<style lang='scss' scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.header-row {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.col-10.standard-page {
|
||||
// full width on smaller devices
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.header-row {
|
||||
h1 {
|
||||
color: $purple-200;
|
||||
|
||||
@@ -282,7 +282,7 @@ export default {
|
||||
const promises = [];
|
||||
const noProfilesLoaded = Object.keys(this.cachedProfileData).length === 0;
|
||||
|
||||
// @TODO: write an explination
|
||||
// @TODO: write an explanation
|
||||
// @TODO: Remove this after enough messages are cached
|
||||
if (
|
||||
!noProfilesLoaded
|
||||
|
||||
@@ -6,7 +6,15 @@
|
||||
:hide-header="true"
|
||||
:hide-footer="true"
|
||||
:modal-class="{'page-2':modalPage > 1 && !editing}"
|
||||
:no-close-on-esc="!editing"
|
||||
:no-close-on-backdrop="!editing"
|
||||
>
|
||||
<span
|
||||
v-if="editing"
|
||||
class="close-icon svg-icon inline icon-10"
|
||||
@click="close()"
|
||||
v-html="icons.close"
|
||||
></span>
|
||||
<div
|
||||
v-if="modalPage === 1 && !editing"
|
||||
class="section row welcome-section"
|
||||
@@ -113,7 +121,7 @@
|
||||
v-if="editing"
|
||||
class="menu-container col-2"
|
||||
:class="{active: activeTopPage === 'backgrounds'}"
|
||||
@click="changeTopPage('backgrounds', '2019')"
|
||||
@click="changeTopPage('backgrounds', '2020')"
|
||||
>
|
||||
<div class="menu-item">
|
||||
<div
|
||||
@@ -321,7 +329,7 @@
|
||||
<div class="task-option">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input
|
||||
id="excercise"
|
||||
id="exercise"
|
||||
v-model="taskCategories"
|
||||
class="custom-control-input"
|
||||
type="checkbox"
|
||||
@@ -330,7 +338,7 @@
|
||||
<label
|
||||
v-once
|
||||
class="custom-control-label"
|
||||
for="excercise"
|
||||
for="exercise"
|
||||
>{{ $t('exercise') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1099,6 +1107,7 @@ import gold from '@/assets/svg/gold.svg';
|
||||
import pin from '@/assets/svg/pin.svg';
|
||||
import arrowRight from '@/assets/svg/arrow_right.svg';
|
||||
import arrowLeft from '@/assets/svg/arrow_left.svg';
|
||||
import svgClose from '@/assets/svg/close.svg';
|
||||
import isPinned from '@/../../common/script/libs/isPinned';
|
||||
import { avatarEditorUtilies } from '../mixins/avatarEditUtilities';
|
||||
|
||||
@@ -1138,6 +1147,7 @@ export default {
|
||||
gold,
|
||||
arrowRight,
|
||||
arrowLeft,
|
||||
close: svgClose,
|
||||
}),
|
||||
modalPage: 1,
|
||||
activeTopPage: 'body',
|
||||
@@ -1150,7 +1160,7 @@ export default {
|
||||
},
|
||||
],
|
||||
|
||||
bgSubMenuItems: ['2019', '2018', '2017', '2016', '2015', '2014'].map(y => ({
|
||||
bgSubMenuItems: ['2020', '2019', '2018', '2017', '2016', '2015', '2014'].map(y => ({
|
||||
id: y,
|
||||
label: y,
|
||||
})),
|
||||
@@ -1158,8 +1168,6 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
|
||||
|
||||
editing () {
|
||||
return this.$store.state.avatarEditorOptions.editingUser;
|
||||
},
|
||||
@@ -1175,6 +1183,7 @@ export default {
|
||||
2017: [],
|
||||
2018: [],
|
||||
2019: [],
|
||||
2020: [],
|
||||
};
|
||||
|
||||
// Hack to force update for now until we restructure the data
|
||||
@@ -1228,6 +1237,9 @@ export default {
|
||||
this.$root.$on('buyModal::boughtItem', this.backgroundPurchased);
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'avatar-modal');
|
||||
},
|
||||
purchase (type, key) {
|
||||
this.$store.dispatch('shops:purchase', {
|
||||
type,
|
||||
@@ -1278,6 +1290,9 @@ export default {
|
||||
if (this.$route.path !== '/') {
|
||||
this.$router.push('/');
|
||||
}
|
||||
|
||||
// NOTE: it's important this flag is set AFTER the onboarding default tasks
|
||||
// have been created or it'll break the onboarding guide achievement for creating a task
|
||||
this.$store.dispatch('user:set', {
|
||||
'flags.welcomed': true,
|
||||
});
|
||||
|
||||
@@ -479,11 +479,19 @@ export default {
|
||||
|
||||
if (!isEmpty(this.sortOption)) {
|
||||
// Use the memberlist filtered by searchTerm
|
||||
sortedMembers = orderBy(
|
||||
sortedMembers,
|
||||
[this.sortOption.value],
|
||||
[this.sortOption.direction],
|
||||
);
|
||||
if (this.sortOption.value === 'profile.name') {
|
||||
// If members are to be sorted by name, use localeCompare for case-
|
||||
// insensitive sort
|
||||
sortedMembers.sort(
|
||||
(a, b) => a.profile.name.localeCompare(b.profile.name),
|
||||
);
|
||||
} else {
|
||||
sortedMembers = orderBy(
|
||||
sortedMembers,
|
||||
[this.sortOption.value],
|
||||
[this.sortOption.direction],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return sortedMembers;
|
||||
@@ -491,7 +499,7 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
groupId () {
|
||||
// @TOOD: We might not need this since groupId is computed now
|
||||
// @TODO: We might not need this since groupId is computed now
|
||||
this.getMembers();
|
||||
},
|
||||
challengeId () {
|
||||
@@ -655,7 +663,7 @@ export default {
|
||||
memberId: member._id,
|
||||
groupId: this.groupId,
|
||||
});
|
||||
this.viewMembers();
|
||||
if (this.invites.length === 0) this.viewMembers();
|
||||
},
|
||||
async promoteToLeader (member) {
|
||||
const groupData = { ...this.group };
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<template functional>
|
||||
<span
|
||||
class="message-count"
|
||||
class="message-count d-flex align-items-center justify-content-center"
|
||||
:class="{'top-count': props.top === true, 'top-count-gray': props.gray === true}"
|
||||
> {{ props.count }} </span>
|
||||
v-html="props.badge || props.count"
|
||||
></span>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@@ -18,6 +19,11 @@
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
|
||||
svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.message-count.top-count {
|
||||
|
||||
@@ -10,7 +10,10 @@
|
||||
>
|
||||
<slot name="icon"></slot>
|
||||
</div>
|
||||
<div class="notification-content">
|
||||
<div
|
||||
class="notification-content"
|
||||
:class="{'has-text': hasText}"
|
||||
>
|
||||
<slot name="content"></slot>
|
||||
</div>
|
||||
<div
|
||||
@@ -107,17 +110,19 @@
|
||||
line-height: 1.43;
|
||||
color: $gray-50;
|
||||
|
||||
max-width: calc(100% - 26px); // to make space for the close icon
|
||||
max-width: 100%;
|
||||
|
||||
&.has-text {
|
||||
padding-right: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.notification-remove {
|
||||
// total distance from the notification top edge is 20 pixels
|
||||
margin-top: 7px;
|
||||
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-left: 12px;
|
||||
padding: 4px;
|
||||
right: 24px;
|
||||
|
||||
.svg-icon {
|
||||
width: 10px;
|
||||
@@ -131,7 +136,25 @@ import closeIcon from '@/assets/svg/close.svg';
|
||||
import { mapActions, mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
props: ['notification', 'canRemove', 'hasIcon', 'readAfterClick'],
|
||||
props: {
|
||||
notification: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
canRemove: {
|
||||
type: Boolean,
|
||||
},
|
||||
hasIcon: {
|
||||
type: Boolean,
|
||||
},
|
||||
readAfterClick: {
|
||||
type: Boolean,
|
||||
},
|
||||
hasText: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
:can-remove="canRemove"
|
||||
:has-icon="true"
|
||||
:notification="notification"
|
||||
:read-after-click="true"
|
||||
:read-after-click="false"
|
||||
@click="action"
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<base-notification
|
||||
:can-remove="canRemove"
|
||||
:has-icon="false"
|
||||
:read-after-click="true"
|
||||
:notification="notification"
|
||||
:has-text="false"
|
||||
@click="action"
|
||||
>
|
||||
<div
|
||||
slot="content"
|
||||
class="d-flex flex-column align-items-center onboarding-complete-content"
|
||||
>
|
||||
<img
|
||||
class="onboarding-complete-banner d-block"
|
||||
src="~@/assets/images/onboarding-complete-banner@2x.png"
|
||||
>
|
||||
<h3>{{ $t('congratulations') }}</h3>
|
||||
<p
|
||||
class="onboarding-complete-text"
|
||||
v-html="$t('onboardingCompleteDesc')"
|
||||
></p>
|
||||
<div class="notifications-buttons">
|
||||
<div
|
||||
class="btn btn-small btn-primary btn-block"
|
||||
>
|
||||
{{ $t('viewAchievements') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</base-notification>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
.onboarding-complete-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.onboarding-complete-banner {
|
||||
width: 282px;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.onboarding-complete-text {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.onboarding-complete-text ::v-deep .gold-amount {
|
||||
color: $yellow-5;
|
||||
}
|
||||
|
||||
.notifications-buttons {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import BaseNotification from './base';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BaseNotification,
|
||||
},
|
||||
props: ['notification', 'canRemove'],
|
||||
methods: {
|
||||
action () {
|
||||
this.$router.push({ name: 'achievements' });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -11,10 +11,11 @@
|
||||
:aria-label="$t('notifications')"
|
||||
>
|
||||
<message-count
|
||||
v-if="notificationsCount > 0"
|
||||
v-if="notificationsCount > 0 || hasSpecialBadge"
|
||||
:count="notificationsCount"
|
||||
:top="true"
|
||||
:gray="!hasUnseenNotifications"
|
||||
:gray="!hasUnseenNotifications && !hasSpecialBadge"
|
||||
:badge="hasSpecialBadge ? icons.starBadge : null"
|
||||
/>
|
||||
<div
|
||||
class="top-menu-icon svg-icon notifications"
|
||||
@@ -22,7 +23,10 @@
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="dropdown-content">
|
||||
<div
|
||||
v-if="openStatus === 1"
|
||||
slot="dropdown-content"
|
||||
>
|
||||
<div
|
||||
class="dropdown-item dropdown-separated
|
||||
d-flex justify-content-between dropdown-inactive align-items-center"
|
||||
@@ -41,6 +45,10 @@
|
||||
>{{ $t('dismissAll') }}</a>
|
||||
</div>
|
||||
<world-boss />
|
||||
<onboarding-guide
|
||||
v-if="showOnboardingGuide"
|
||||
:never-seen="hasSpecialBadge"
|
||||
/>
|
||||
<component
|
||||
:is="notification.type"
|
||||
v-for="notification in notifications"
|
||||
@@ -49,7 +57,7 @@
|
||||
:can-remove="!isActionable(notification)"
|
||||
/>
|
||||
<div
|
||||
v-if="notificationsCount === 0"
|
||||
v-if="notificationsCount === 0 && !showOnboardingGuide"
|
||||
class="dropdown-item dropdown-separated
|
||||
d-flex justify-content-center dropdown-inactive no-notifications flex-column"
|
||||
>
|
||||
@@ -106,10 +114,16 @@
|
||||
<script>
|
||||
import { mapState, mapActions } from '@/libs/store';
|
||||
import * as quests from '@/../../common/script/content/quests';
|
||||
import {
|
||||
hasCompletedOnboarding,
|
||||
hasActiveOnboarding,
|
||||
} from '@/../../common/script/libs/onboarding';
|
||||
import notificationsIcon from '@/assets/svg/notifications.svg';
|
||||
import MenuDropdown from '../ui/customMenuDropdown';
|
||||
import MessageCount from './messageCount';
|
||||
import { CONSTANTS, getLocalSetting, setLocalSetting } from '@/libs/userlocalManager';
|
||||
import successImage from '@/assets/svg/success.svg';
|
||||
import starBadge from '@/assets/svg/star-badge.svg';
|
||||
|
||||
// Notifications
|
||||
import NEW_STUFF from './notifications/newStuff';
|
||||
@@ -132,6 +146,8 @@ import VERIFY_USERNAME from './notifications/verifyUsername';
|
||||
import ACHIEVEMENT_JUST_ADD_WATER from './notifications/justAddWater';
|
||||
import ACHIEVEMENT_LOST_MASTERCLASSER from './notifications/lostMasterclasser';
|
||||
import ACHIEVEMENT_MIND_OVER_MATTER from './notifications/mindOverMatter';
|
||||
import ONBOARDING_COMPLETE from './notifications/onboardingComplete';
|
||||
import OnboardingGuide from './onboardingGuide';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -158,18 +174,22 @@ export default {
|
||||
ACHIEVEMENT_MIND_OVER_MATTER,
|
||||
WorldBoss: WORLD_BOSS,
|
||||
VERIFY_USERNAME,
|
||||
OnboardingGuide,
|
||||
ONBOARDING_COMPLETE,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
notifications: notificationsIcon,
|
||||
success: successImage,
|
||||
starBadge,
|
||||
}),
|
||||
hasSpecialBadge: false,
|
||||
quests,
|
||||
openStatus: undefined,
|
||||
actionableNotifications: [
|
||||
'GUILD_INVITATION', 'PARTY_INVITATION', 'CHALLENGE_INVITATION',
|
||||
'QUEST_INVITATION', 'GROUP_TASK_NEEDS_WORK', 'GROUP_TASK_APPROVAL',
|
||||
'QUEST_INVITATION', 'GROUP_TASK_NEEDS_WORK',
|
||||
],
|
||||
// A list of notifications handled by this component,
|
||||
// listed in the order they should appear in the notifications panel.
|
||||
@@ -177,11 +197,11 @@ export default {
|
||||
handledNotifications: [
|
||||
'NEW_STUFF', 'GROUP_TASK_NEEDS_WORK',
|
||||
'GUILD_INVITATION', 'PARTY_INVITATION', 'CHALLENGE_INVITATION',
|
||||
'QUEST_INVITATION', 'GROUP_TASK_ASSIGNED', 'GROUP_TASK_APPROVAL', 'GROUP_TASK_APPROVED', 'GROUP_TASK_CLAIMED',
|
||||
'NEW_MYSTERY_ITEMS', 'CARD_RECEIVED',
|
||||
'QUEST_INVITATION', 'GROUP_TASK_ASSIGNED', 'GROUP_TASK_APPROVAL', 'GROUP_TASK_APPROVED',
|
||||
'GROUP_TASK_CLAIMED', 'NEW_MYSTERY_ITEMS', 'CARD_RECEIVED',
|
||||
'NEW_INBOX_MESSAGE', 'NEW_CHAT_MESSAGE', 'UNALLOCATED_STATS_POINTS',
|
||||
'ACHIEVEMENT_JUST_ADD_WATER', 'ACHIEVEMENT_LOST_MASTERCLASSER', 'ACHIEVEMENT_MIND_OVER_MATTER',
|
||||
'VERIFY_USERNAME',
|
||||
'VERIFY_USERNAME', 'ONBOARDING_COMPLETE',
|
||||
],
|
||||
};
|
||||
},
|
||||
@@ -275,6 +295,20 @@ export default {
|
||||
hasClass () {
|
||||
return this.$store.getters['members:hasClass'](this.user);
|
||||
},
|
||||
showOnboardingGuide () {
|
||||
return hasActiveOnboarding(this.user) && !hasCompletedOnboarding(this.user);
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
const onboardingPanelState = getLocalSetting(CONSTANTS.keyConstants.ONBOARDING_PANEL_STATE);
|
||||
if (
|
||||
onboardingPanelState !== CONSTANTS.onboardingPanelValues.PANEL_OPENED
|
||||
&& this.showOnboardingGuide
|
||||
) {
|
||||
// The first time the onboarding panel is opened a special
|
||||
// badge for notifications should be used
|
||||
this.hasSpecialBadge = true;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
@@ -286,6 +320,18 @@ export default {
|
||||
|
||||
// Mark notifications as seen when the menu is opened
|
||||
if (openStatus) this.markAllAsSeen();
|
||||
|
||||
// Reset the special notification badge as soon as it's opened
|
||||
if (this.hasSpecialBadge) {
|
||||
setLocalSetting(
|
||||
CONSTANTS.keyConstants.ONBOARDING_PANEL_STATE,
|
||||
CONSTANTS.onboardingPanelValues.PANEL_OPENED,
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
this.hasSpecialBadge = false;
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
markAllAsSeen () {
|
||||
const idsToSee = this.notifications.map(notification => {
|
||||
@@ -318,5 +364,6 @@ export default {
|
||||
return this.actionableNotifications.indexOf(notification.type) !== -1;
|
||||
},
|
||||
},
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
<template>
|
||||
<div
|
||||
class="onboarding-guide-panel d-flex align-items-center flex-column p-4 dropdown-separated"
|
||||
@click.stop.prevent="action"
|
||||
>
|
||||
<div
|
||||
class="svg-icon onboarding-toggle"
|
||||
:class="{'onboarding-toggle-open': open}"
|
||||
@click="openPanel"
|
||||
v-html="icons.down"
|
||||
></div>
|
||||
<div
|
||||
class="svg-icon onboarding-guide-banner"
|
||||
v-html="icons.onboardingGuideBanner"
|
||||
></div>
|
||||
<h3 class="getting-started">
|
||||
{{ $t('gettingStarted') }}
|
||||
</h3>
|
||||
<span
|
||||
class="getting-started-desc"
|
||||
v-html="$t('gettingStartedDesc')"
|
||||
></span>
|
||||
<div
|
||||
class="onboarding-progress-box d-flex flex-row justify-content-between small-text mb-2"
|
||||
>
|
||||
<strong>Your Progress</strong>
|
||||
<span :class="{'has-progress': progress > 0}">{{ progressText }}</span>
|
||||
</div>
|
||||
<div class="onboarding-progress-bar mb-3">
|
||||
<div
|
||||
class="onboarding-progress-bar-fill"
|
||||
:style="{width: `${progress}%`}"
|
||||
></div>
|
||||
</div>
|
||||
<b-collapse
|
||||
id="onboardingPanelCollapse"
|
||||
v-model="open"
|
||||
>
|
||||
<div
|
||||
v-for="(achievement, key) in onboardingAchievements"
|
||||
:key="key"
|
||||
:class="{
|
||||
'achievement-earned': achievement.earned
|
||||
}"
|
||||
class="achievement-box d-flex flex-row"
|
||||
>
|
||||
<div class="achievement-icon-wrapper">
|
||||
<div :class="`achievement-icon ${getAchievementIcon(achievement)}`"></div>
|
||||
</div>
|
||||
<div class="achievement-info d-flex flex-column">
|
||||
<strong class="achievement-title">{{ achievement.title }}</strong>
|
||||
<span class="small-text achievement-desc">{{ getAchievementText(key) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</b-collapse>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
.onboarding-guide-panel {
|
||||
white-space: normal;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.onboarding-toggle {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 75px;
|
||||
right: 24px;
|
||||
width: 16px;
|
||||
& ::v-deep svg path {
|
||||
stroke: $gray-200;
|
||||
}
|
||||
|
||||
&:hover ::v-deep svg path {
|
||||
stroke: $gray-100;
|
||||
}
|
||||
|
||||
&-open {
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.onboarding-guide-banner {
|
||||
margin-top: -8px;
|
||||
margin-bottom: 12px;
|
||||
width: 154px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.getting-started {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.getting-started-desc {
|
||||
font-size: 14px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.getting-started-desc ::v-deep .gold-amount {
|
||||
color: $yellow-5;
|
||||
}
|
||||
|
||||
.onboarding-progress-box {
|
||||
width: 100%;
|
||||
font-style: normal;
|
||||
|
||||
strong {
|
||||
color: $gray-50;
|
||||
}
|
||||
|
||||
.has-progress {
|
||||
color: $yellow-5;
|
||||
}
|
||||
}
|
||||
|
||||
.onboarding-progress-bar {
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
border-radius: 2px;
|
||||
background-color: $gray-600;
|
||||
|
||||
.onboarding-progress-bar-fill {
|
||||
height: 100%;
|
||||
border-radius: 2px;
|
||||
background: $yellow-5;
|
||||
}
|
||||
}
|
||||
|
||||
.achievement-box {
|
||||
padding-top: 11px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px dashed $gray-500;
|
||||
width: 100%;
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 0px;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.achievement-earned {
|
||||
.achievement-icon-wrapper {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
color: $gray-200;
|
||||
|
||||
.achievement-title {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
||||
.achievement-icon-wrapper {
|
||||
opacity: 0.5;
|
||||
width: 40px;
|
||||
|
||||
.achievement-icon {
|
||||
margin-left: -12px;
|
||||
transform: scale(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.achievement-info {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.achievement-title {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.achievement-desc {
|
||||
font-style: normal;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import achievs from '@/../../common/script/libs/achievements';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
import onboardingGuideBanner from '@/assets/svg/onboarding-guide-banner.svg';
|
||||
import downIcon from '@/assets/svg/down.svg';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
neverSeen: { // whether it's ever been seen by the user
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
onboardingGuideBanner,
|
||||
down: downIcon,
|
||||
}),
|
||||
open: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
onboardingAchievements () {
|
||||
return achievs.getAchievementsForProfile(this.user).onboarding.achievements;
|
||||
},
|
||||
progress () {
|
||||
const keys = Object.keys(this.onboardingAchievements);
|
||||
let nEarned = 0;
|
||||
|
||||
keys.forEach(key => {
|
||||
if (this.onboardingAchievements[key].earned) nEarned += 1;
|
||||
});
|
||||
|
||||
return (nEarned / keys.length) * 100;
|
||||
},
|
||||
progressText () {
|
||||
if (this.progress === 0) {
|
||||
return this.$t('letsGetStarted');
|
||||
}
|
||||
|
||||
return this.$t('onboardingProgress', { percentage: this.progress });
|
||||
},
|
||||
},
|
||||
created () {
|
||||
// null means it's never been automatically toggled
|
||||
if (this.neverSeen === true) {
|
||||
// The first time the panel should be automatically opened
|
||||
this.open = true;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getAchievementIcon (achievement) {
|
||||
if (achievement.earned) {
|
||||
return `${achievement.icon}2x`;
|
||||
}
|
||||
|
||||
return 'achievement-unearned2x';
|
||||
},
|
||||
getAchievementText (key) {
|
||||
let stringKey = 'achievement';
|
||||
stringKey += key.charAt(0).toUpperCase() + key.slice(1);
|
||||
stringKey += 'ModalText';
|
||||
|
||||
return this.$t(stringKey);
|
||||
},
|
||||
openPanel (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.open = !this.open;
|
||||
},
|
||||
action () {
|
||||
// Do nothing, used to prevent closure on click.
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -341,7 +341,7 @@ export default {
|
||||
const { firstRender } = viewOptions;
|
||||
const { itemsInFirstPosition } = viewOptions;
|
||||
|
||||
// Render selected items in first postion only for the first render
|
||||
// Render selected items in first position only for the first render
|
||||
if (itemsInFirstPosition.indexOf(ownedItem.key) !== -1 && firstRender === false) {
|
||||
gearItemsByType[type].unshift(ownedItem);
|
||||
} else if (isEquipped === true && firstRender === true) {
|
||||
@@ -382,7 +382,7 @@ export default {
|
||||
const { firstRender } = viewOptions;
|
||||
const { itemsInFirstPosition } = viewOptions;
|
||||
|
||||
// Render selected items in first postion only for the first render
|
||||
// Render selected items in first position only for the first render
|
||||
if (itemsInFirstPosition.indexOf(ownedItem.key) !== -1 && firstRender === false) {
|
||||
gearItemsByClass[klass].unshift(ownedItem);
|
||||
} else if (isEquipped === true && firstRender === true) {
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
id="hatchedPet-modal"
|
||||
:hide-header="true"
|
||||
>
|
||||
<span
|
||||
class="close-icon svg-icon inline icon-10"
|
||||
@click="close()"
|
||||
v-html="icons.close"
|
||||
></span>
|
||||
<div
|
||||
v-if="pet != null"
|
||||
class="content"
|
||||
@@ -14,7 +19,7 @@
|
||||
{{ $t('hatchedPetGeneric') }}
|
||||
</div>
|
||||
<div class="inner-content">
|
||||
<div class="pet-background">
|
||||
<div class="pet-background d-flex align-items-center">
|
||||
<div :class="pet.class"></div>
|
||||
</div>
|
||||
<h4 class="title">
|
||||
@@ -51,6 +56,10 @@
|
||||
width: 330px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
.content {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -75,12 +84,25 @@
|
||||
|
||||
.dialog-header {
|
||||
color: $purple-200;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin-bottom: 24px;
|
||||
min-height: 0;
|
||||
|
||||
&.markdown {
|
||||
p {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import markdownDirective from '@/directives/markdown';
|
||||
import svgClose from '@/assets/svg/close.svg';
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
@@ -94,6 +116,9 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
pet: null,
|
||||
icons: Object.freeze({
|
||||
close: svgClose,
|
||||
}),
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
|
||||
@@ -26,10 +26,14 @@
|
||||
<quest-completed />
|
||||
<quest-invitation />
|
||||
<verify-username />
|
||||
<generic-achievement :data="notificationData" />
|
||||
<generic-achievement
|
||||
v-if="notificationData && notificationData.achievement"
|
||||
:data="notificationData"
|
||||
/>
|
||||
<just-add-water />
|
||||
<lost-masterclasser />
|
||||
<mind-over-matter />
|
||||
<onboarding-complete />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -92,6 +96,10 @@
|
||||
background-color: #4f2a93 !important;
|
||||
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12) !important;
|
||||
}
|
||||
|
||||
.introjs-skipbutton.btn-primary, .introjs-donebutton.btn-primary {
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
@@ -99,9 +107,11 @@ import axios from 'axios';
|
||||
import moment from 'moment';
|
||||
import throttle from 'lodash/throttle';
|
||||
import debounce from 'lodash/debounce';
|
||||
import Vue from 'vue';
|
||||
|
||||
import { toNextLevel } from '@/../../common/script/statHelpers';
|
||||
import { shouldDo } from '@/../../common/script/cron';
|
||||
import { onOnboardingComplete } from '@/../../common/script/libs/onboarding';
|
||||
import { mapState } from '@/libs/store';
|
||||
import notifications from '@/mixins/notifications';
|
||||
import guide from '@/mixins/guide';
|
||||
@@ -132,6 +142,7 @@ import justAddWater from './achievements/justAddWater';
|
||||
import lostMasterclasser from './achievements/lostMasterclasser';
|
||||
import mindOverMatter from './achievements/mindOverMatter';
|
||||
import loginIncentives from './achievements/login-incentives';
|
||||
import onboardingComplete from './achievements/onboardingComplete';
|
||||
import verifyUsername from './settings/verifyUsername';
|
||||
|
||||
const NOTIFICATIONS = {
|
||||
@@ -164,21 +175,33 @@ const NOTIFICATIONS = {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementAllYourBase')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'allYourBase', // defined manually until the server sends all the necessary data
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_BACK_TO_BASICS: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementBackToBasics')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'backToBasics', // defined manually until the server sends all the necessary data
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_DUST_DEVIL: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementDustDevil')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'dustDevil', // defined manually until the server sends all the necessary data
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_ARID_AUTHORITY: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementAridAuthority')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'aridAuthority', // defined manually until the server sends all the necessary data
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_PARTY_UP: {
|
||||
achievement: true,
|
||||
@@ -187,6 +210,7 @@ const NOTIFICATIONS = {
|
||||
data: {
|
||||
message: $t => $t('achievement'),
|
||||
modalText: $t => $t('achievementPartyUp'),
|
||||
achievement: 'partyUp', // defined manually until the server sends all the necessary data
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_PARTY_ON: {
|
||||
@@ -196,6 +220,7 @@ const NOTIFICATIONS = {
|
||||
data: {
|
||||
message: $t => $t('achievement'),
|
||||
modalText: $t => $t('achievementPartyOn'),
|
||||
achievement: 'partyOn', // defined manually until the server sends all the necessary data
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_BEAST_MASTER: {
|
||||
@@ -205,6 +230,7 @@ const NOTIFICATIONS = {
|
||||
data: {
|
||||
message: $t => $t('achievement'),
|
||||
modalText: $t => $t('beastAchievement'),
|
||||
achievement: 'beastMaster', // defined manually until the server sends all the necessary data
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_MOUNT_MASTER: {
|
||||
@@ -214,6 +240,7 @@ const NOTIFICATIONS = {
|
||||
data: {
|
||||
message: $t => $t('achievement'),
|
||||
modalText: $t => $t('mountAchievement'),
|
||||
achievement: 'mountMaster', // defined manually until the server sends all the necessary data
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_TRIAD_BINGO: {
|
||||
@@ -223,17 +250,49 @@ const NOTIFICATIONS = {
|
||||
data: {
|
||||
message: $t => $t('achievement'),
|
||||
modalText: $t => $t('triadBingoAchievement'),
|
||||
achievement: 'triadBingo', // defined manually until the server sends all the necessary data
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_MONSTER_MAGUS: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementMonsterMagus')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'monsterMagus', // defined manually until the server sends all the necessary data
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_UNDEAD_UNDERTAKER: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementUndeadUndertaker')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'undeadUndertaker', // defined manually until the server sends all the necessary data
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT: { // data filled in handleUserNotifications
|
||||
achievement: true,
|
||||
modalId: 'generic-achievement',
|
||||
label: null, // data filled in handleUserNotifications
|
||||
data: {
|
||||
message: $t => $t('achievement'),
|
||||
modalText: null, // data filled in handleUserNotifications
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_PRIMED_FOR_PAINTING: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementPrimedForPainting')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'primedForPainting', // defined manually until the server sends all the necessary data
|
||||
},
|
||||
},
|
||||
ACHIEVEMENT_PEARLY_PRO: {
|
||||
achievement: true,
|
||||
label: $t => `${$t('achievement')}: ${$t('achievementPearlyPro')}`,
|
||||
modalId: 'generic-achievement',
|
||||
data: {
|
||||
achievement: 'pearlyPro', // defined manually until the server sends all the necessary data
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -266,6 +325,7 @@ export default {
|
||||
lostMasterclasser,
|
||||
mindOverMatter,
|
||||
justAddWater,
|
||||
onboardingComplete,
|
||||
},
|
||||
mixins: [notifications, guide],
|
||||
data () {
|
||||
@@ -291,7 +351,8 @@ export default {
|
||||
'CRON', 'SCORED_TASK', 'LOGIN_INCENTIVE', 'ACHIEVEMENT_ALL_YOUR_BASE', 'ACHIEVEMENT_BACK_TO_BASICS',
|
||||
'GENERIC_ACHIEVEMENT', 'ACHIEVEMENT_PARTY_UP', 'ACHIEVEMENT_PARTY_ON', 'ACHIEVEMENT_BEAST_MASTER',
|
||||
'ACHIEVEMENT_MOUNT_MASTER', 'ACHIEVEMENT_TRIAD_BINGO', 'ACHIEVEMENT_DUST_DEVIL', 'ACHIEVEMENT_ARID_AUTHORITY',
|
||||
'ACHIEVEMENT_MONSTER_MAGUS', 'ACHIEVEMENT_UNDEAD_UNDERTAKER', 'GENERIC_ACHIEVEMENT',
|
||||
'ACHIEVEMENT_MONSTER_MAGUS', 'ACHIEVEMENT_UNDEAD_UNDERTAKER', 'ACHIEVEMENT_PRIMED_FOR_PAINTING',
|
||||
'ACHIEVEMENT_PEARLY_PRO', 'ACHIEVEMENT', 'ONBOARDING_COMPLETE',
|
||||
].forEach(type => {
|
||||
handledNotifications[type] = true;
|
||||
});
|
||||
@@ -467,7 +528,7 @@ export default {
|
||||
this.text(config.label(this.$t), () => {
|
||||
this.notificationData = data;
|
||||
this.$root.$emit('bv::show::modal', config.modalId);
|
||||
}, false);
|
||||
}, true, 10000);
|
||||
}
|
||||
},
|
||||
debounceCheckUserAchievements: debounce(function debounceCheck () {
|
||||
@@ -697,9 +758,23 @@ export default {
|
||||
case 'ACHIEVEMENT_TRIAD_BINGO':
|
||||
case 'ACHIEVEMENT_MONSTER_MAGUS':
|
||||
case 'ACHIEVEMENT_UNDEAD_UNDERTAKER':
|
||||
case 'ACHIEVEMENT_PRIMED_FOR_PAINTING':
|
||||
case 'ACHIEVEMENT_PEARLY_PRO':
|
||||
case 'GENERIC_ACHIEVEMENT':
|
||||
this.showNotificationWithModal(notification);
|
||||
break;
|
||||
case 'ACHIEVEMENT': { // generic achievement
|
||||
const { achievement } = notification.data;
|
||||
const upperCaseAchievement = achievement.charAt(0).toUpperCase() + achievement.slice(1);
|
||||
const achievementTitleKey = `achievement${upperCaseAchievement}`;
|
||||
NOTIFICATIONS.ACHIEVEMENT.label = $t => `${$t('achievement')}: ${$t(achievementTitleKey)}`;
|
||||
NOTIFICATIONS.ACHIEVEMENT.data.modalText = $t => $t(achievementTitleKey);
|
||||
this.showNotificationWithModal(notification);
|
||||
|
||||
// Set the achievement as it's not defined in the user schema
|
||||
Vue.set(this.user.achievements, achievement, true);
|
||||
break;
|
||||
}
|
||||
case 'CRON':
|
||||
if (notification.data) {
|
||||
if (notification.data.hp) this.hp(notification.data.hp, 'hp');
|
||||
@@ -729,6 +804,20 @@ export default {
|
||||
this.$root.$emit('bv::show::modal', 'login-incentives');
|
||||
}
|
||||
break;
|
||||
case 'ONBOARDING_COMPLETE':
|
||||
// Award rewards
|
||||
onOnboardingComplete(this.user);
|
||||
|
||||
// If the user cronned in the last 3 minutes
|
||||
// Don't show too many modals on app load
|
||||
// Use notification panel
|
||||
if (moment().diff(this.user.lastCron, 'minutes') < 3) {
|
||||
markAsRead = false;
|
||||
} else {
|
||||
// Otherwise use the modal
|
||||
this.$root.$emit('bv::show::modal', 'onboarding-complete');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (markAsRead) notificationsToRead.push(notification.id);
|
||||
|
||||
@@ -28,39 +28,38 @@
|
||||
<small>{{ $t('unsubscribeAllEmailsText') }}</small>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<table class="table"></table>
|
||||
<tr>
|
||||
<td></td>
|
||||
<th>
|
||||
<span>{{ $t('email') }}</span>
|
||||
</th>
|
||||
<th>
|
||||
<span>{{ $t('push') }}</span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr
|
||||
v-for="notification in notificationsIds"
|
||||
:key="notification"
|
||||
>
|
||||
<td>
|
||||
<span>{{ $t(notification) }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
v-model="user.preferences.emailNotifications[notification]"
|
||||
type="checkbox"
|
||||
@change="set('emailNotifications', notification)"
|
||||
>
|
||||
</td>
|
||||
<td v-if="onlyEmailsIds.indexOf(notification) === -1">
|
||||
<input
|
||||
v-model="user.preferences.pushNotifications[notification]"
|
||||
type="checkbox"
|
||||
@change="set('pushNotifications', notification)"
|
||||
>
|
||||
<hr>
|
||||
</td>
|
||||
</tr>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<td></td>
|
||||
<th>
|
||||
<span>{{ $t('email') }}</span>
|
||||
</th>
|
||||
<th>
|
||||
<span>{{ $t('push') }}</span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr v-for="notification in notificationsIds" :key="notification">
|
||||
<td>
|
||||
<span>{{ $t(notification) }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
v-model="user.preferences.emailNotifications[notification]"
|
||||
type="checkbox"
|
||||
@change="set('emailNotifications', notification)"
|
||||
>
|
||||
</td>
|
||||
<td v-if="onlyEmailsIds.indexOf(notification) === -1">
|
||||
<input
|
||||
v-model="user.preferences.pushNotifications[notification]"
|
||||
type="checkbox"
|
||||
@change="set('pushNotifications', notification)"
|
||||
>
|
||||
<td v-else>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -74,6 +73,8 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
notificationsIds: Object.freeze([
|
||||
'majorUpdates',
|
||||
'onboarding',
|
||||
'newPM',
|
||||
'wonChallenge',
|
||||
'giftedGems',
|
||||
@@ -85,8 +86,6 @@ export default {
|
||||
'invitedQuest',
|
||||
'importantAnnouncements',
|
||||
'weeklyRecaps',
|
||||
'onboarding',
|
||||
'majorUpdates',
|
||||
'subscriptionReminders',
|
||||
]),
|
||||
// list of email-only notifications
|
||||
|
||||
@@ -812,7 +812,8 @@ export default {
|
||||
this.localAuth.username = this.user.auth.local.username;
|
||||
this.user.flags.verifiedUsername = true;
|
||||
} else if (attribute === 'email') {
|
||||
this.user.auth.local.email = updates[attribute];
|
||||
this.user.auth.local.email = updates.newEmail;
|
||||
window.alert(this.$t('emailSuccess'));
|
||||
}
|
||||
},
|
||||
async changeDisplayName (newName) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="standard-page">
|
||||
<div class="standard-page pt-0 px-0">
|
||||
<h1>{{ $t('subscription') }}</h1>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
@@ -242,8 +242,10 @@
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
.badge.badge-success {
|
||||
color: #fff;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.subscribe-pay {
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
<div class="how-many-to-buy">
|
||||
<strong>{{ $t('howManyToBuy') }}</strong>
|
||||
</div>
|
||||
<div class="mb-3" v-if="item.addlNotes">
|
||||
{{ item.addlNotes }}
|
||||
</div>
|
||||
<div class="box">
|
||||
<input
|
||||
v-model.number="selectedAmountToBuy"
|
||||
|
||||
@@ -53,9 +53,9 @@
|
||||
<div class="strike">
|
||||
<span>{{ $t('or') }}</span>
|
||||
</div>
|
||||
<div
|
||||
<form
|
||||
class="form"
|
||||
@keyup.enter="register()"
|
||||
@submit.prevent.stop="register()"
|
||||
>
|
||||
<p class="form-text">
|
||||
{{ $t('usernameLimitations') }}
|
||||
@@ -63,7 +63,7 @@
|
||||
<input
|
||||
id="usernameInput"
|
||||
v-model="username"
|
||||
class="form-control"
|
||||
class="form-control input-with-error"
|
||||
type="text"
|
||||
:placeholder="$t('username')"
|
||||
:class="{'input-valid': usernameValid, 'input-invalid': usernameInvalid}"
|
||||
@@ -85,32 +85,49 @@
|
||||
>
|
||||
<input
|
||||
v-model="password"
|
||||
class="form-control"
|
||||
class="form-control input-with-error"
|
||||
type="password"
|
||||
:placeholder="$t('password')"
|
||||
:class="{'input-valid': password.length > 3}"
|
||||
:class="{
|
||||
'input-valid': passwordValid,
|
||||
'input-invalid': passwordInvalid,
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-if="passwordInvalid"
|
||||
class="input-error"
|
||||
>
|
||||
{{ $t('minPasswordLength') }}
|
||||
</div>
|
||||
<input
|
||||
v-model="passwordConfirm"
|
||||
class="form-control"
|
||||
class="form-control input-with-error"
|
||||
type="password"
|
||||
:placeholder="$t('confirmPassword')"
|
||||
:class="{
|
||||
'input-invalid': passwordConfirmInvalid,
|
||||
'input-valid': passwordConfirmValid}"
|
||||
>
|
||||
<div
|
||||
v-if="passwordConfirmInvalid"
|
||||
class="input-error"
|
||||
>
|
||||
{{ $t('passwordConfirmationMatch') }}
|
||||
</div>
|
||||
<p
|
||||
v-once
|
||||
class="form-text"
|
||||
v-html="$t('termsAndAgreement')"
|
||||
></p>
|
||||
<button
|
||||
class="sign-up"
|
||||
class="btn btn-block btn-info sign-up"
|
||||
:disabled="signupFormInvalid"
|
||||
type="submit"
|
||||
@click="register()"
|
||||
>
|
||||
{{ $t('signup') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div
|
||||
@@ -519,7 +536,7 @@
|
||||
transition: border .5s, color .5s;
|
||||
}
|
||||
|
||||
#usernameInput.input-invalid {
|
||||
.input-invalid.input-with-error {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
@@ -537,21 +554,9 @@
|
||||
background-color: #36205d;
|
||||
}
|
||||
|
||||
button.sign-up {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
background-color: #2995cd;
|
||||
font-size: 16px;
|
||||
transition: all .5s ease;
|
||||
}
|
||||
|
||||
.sign-up:hover {
|
||||
background-color: #50b5e9;
|
||||
box-shadow: 0 4px 4px 0 rgba(26, 24, 29, 0.16), 0 1px 8px 0 rgba(26, 24, 29, 0.12);
|
||||
cursor: pointer;
|
||||
.sign-up {
|
||||
padding-top: 11px;
|
||||
padding-bottom: 11px;
|
||||
}
|
||||
|
||||
::-webkit-input-placeholder { /* Chrome/Opera/Safari */
|
||||
@@ -769,7 +774,6 @@
|
||||
color: #fff;
|
||||
font-size: 90%;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
</style>
|
||||
@@ -796,6 +800,7 @@ import lifehacker from '@/assets/images/home/lifehacker.svg';
|
||||
import makeuseof from '@/assets/images/home/make-use-of.svg';
|
||||
import thenewyorktimes from '@/assets/images/home/the-new-york-times.svg';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
import { MINIMUM_PASSWORD_LENGTH } from '@/../../common/script/constants';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
@@ -844,6 +849,14 @@ export default {
|
||||
if (this.username.length < 1) return false;
|
||||
return !this.usernameValid;
|
||||
},
|
||||
passwordValid () {
|
||||
if (this.password.length <= 0) return false;
|
||||
return this.password.length >= MINIMUM_PASSWORD_LENGTH;
|
||||
},
|
||||
passwordInvalid () {
|
||||
if (this.password.length <= 0) return false;
|
||||
return this.password.length < MINIMUM_PASSWORD_LENGTH;
|
||||
},
|
||||
passwordConfirmValid () {
|
||||
if (this.passwordConfirm.length <= 3) return false;
|
||||
return this.passwordConfirm === this.password;
|
||||
@@ -852,6 +865,12 @@ export default {
|
||||
if (this.passwordConfirm.length <= 3) return false;
|
||||
return this.passwordConfirm !== this.password;
|
||||
},
|
||||
signupFormInvalid () {
|
||||
return this.usernameInvalid
|
||||
|| this.emailInvalid
|
||||
|| this.passwordInvalid
|
||||
|| this.passwordConfirmInvalid;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
username () {
|
||||
|
||||