Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f31a82c8f2 | ||
|
|
8bc02e82ee | ||
|
|
9040f9f04e | ||
|
|
ff82c37d5f | ||
|
|
37364b0700 | ||
|
|
11cfb3920a | ||
|
|
f5468d3771 | ||
|
|
99882d09ab | ||
|
|
7034d135d5 | ||
|
|
034c0c9bb5 | ||
|
|
e0711655f0 | ||
|
|
71e162eed5 | ||
|
|
8eac8732c5 | ||
|
|
896a1b74b6 | ||
|
|
3b36046a6a | ||
|
|
07991817e7 | ||
|
|
47b75156fa | ||
|
|
c630486fef | ||
|
|
0a070316b5 | ||
|
|
f6b34e85df | ||
|
|
2946f0df15 | ||
|
|
614d9a920a | ||
|
|
454524fb5b | ||
|
|
abc0777412 | ||
|
|
6972eb8f8f | ||
|
|
535ee2b2a7 | ||
|
|
c9755bee7c | ||
|
|
f810fff6fc | ||
|
|
5bbe59c52d | ||
|
|
3f52401384 | ||
|
|
e7944b3d98 | ||
|
|
08e925e3da | ||
|
|
16c9e42ad8 | ||
|
|
0e1d00c95f | ||
|
|
166f4683ca | ||
|
|
1fcc0d8d3a | ||
|
|
8a4c4e10f1 | ||
|
|
18ed0fe446 | ||
|
|
1f9ebeb629 | ||
|
|
b9d83122d1 | ||
|
|
d1f7e64156 | ||
|
|
0f8e7416f8 | ||
|
|
1c8b0f92df | ||
|
|
75e5b20f93 | ||
|
|
f9db432794 | ||
|
|
6cec7cbba2 | ||
|
|
f76d097313 | ||
|
|
59af471438 | ||
|
|
d0da303b7d | ||
|
|
596383e7a8 | ||
|
|
accba7fc13 | ||
|
|
dfc54f1600 | ||
|
|
b76ab58e3e | ||
|
|
6f093a94c4 | ||
|
|
a9a9e7a4ab | ||
|
|
b2058ec23d | ||
|
|
2461f53cf5 | ||
|
|
73fc288f3b | ||
|
|
ea78b6feb9 | ||
|
|
7c141614ed | ||
|
|
c072935e80 | ||
|
|
54e49ca3b9 | ||
|
|
b16a245d61 | ||
|
|
0a8109e496 | ||
|
|
cdfcc6419f | ||
|
|
9c25c2452f | ||
|
|
573f2e4732 | ||
|
|
81ffcf9c1b | ||
|
|
d8925a8811 | ||
|
|
217c16988b | ||
|
|
de7f953b67 | ||
|
|
8ee2a02e73 | ||
|
|
d9b573b430 | ||
|
|
cbcf5a03e1 | ||
|
|
487523f64b | ||
|
|
74cfc2cf52 | ||
|
|
2d489e870f | ||
|
|
6eb484605a | ||
|
|
8969b755a6 | ||
|
|
0062e5b1f1 | ||
|
|
50b98d8d92 | ||
|
|
7ddf4b1f7b | ||
|
|
c91da86b89 | ||
|
|
d549fea4ed | ||
|
|
a362914f93 | ||
|
|
61001d0e9a | ||
|
|
bd5c4a08e2 | ||
|
|
50ebdd1ece |
47
Dockerfile
@@ -1,18 +1,29 @@
|
||||
FROM node:8
|
||||
|
||||
# Install global packages
|
||||
RUN npm install -g gulp-cli mocha
|
||||
|
||||
# Clone Habitica repo and install dependencies
|
||||
RUN mkdir -p /usr/src/habitrpg
|
||||
WORKDIR /usr/src/habitrpg
|
||||
RUN git clone https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
|
||||
RUN cp config.json.example config.json
|
||||
RUN npm install
|
||||
|
||||
# Create Build dir
|
||||
RUN mkdir -p ./website/build
|
||||
|
||||
# Start Habitica
|
||||
EXPOSE 3000
|
||||
CMD ["npm", "start"]
|
||||
FROM node:8
|
||||
|
||||
ENV ADMIN_EMAIL admin@habitica.com
|
||||
ENV AMAZON_PAYMENTS_CLIENT_ID amzn1.application-oa2-client.68ed9e6904ef438fbc1bf86bf494056e
|
||||
ENV AMAZON_PAYMENTS_SELLER_ID AMQ3SB4SG5E91
|
||||
ENV AMPLITUDE_KEY e8d4c24b3d6ef3ee73eeba715023dd43
|
||||
ENV BASE_URL https://habitica.com
|
||||
ENV FACEBOOK_KEY 128307497299777
|
||||
ENV GA_ID UA-33510635-1
|
||||
ENV GOOGLE_CLIENT_ID 1035232791481-32vtplgnjnd1aufv3mcu1lthf31795fq.apps.googleusercontent.com
|
||||
ENV NODE_ENV production
|
||||
ENV STRIPE_PUB_KEY pk_85fQ0yMECHNfHTSsZoxZXlPSwSNfA
|
||||
|
||||
# Install global packages
|
||||
RUN npm install -g gulp-cli mocha
|
||||
|
||||
# Clone Habitica repo and install dependencies
|
||||
RUN mkdir -p /usr/src/habitrpg
|
||||
WORKDIR /usr/src/habitrpg
|
||||
RUN git clone --branch release https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
|
||||
RUN npm install
|
||||
RUN gulp build:prod --force
|
||||
|
||||
# Create Build dir
|
||||
RUN mkdir -p ./website/build
|
||||
|
||||
# Start Habitica
|
||||
EXPOSE 3000
|
||||
CMD ["node", "./website/transpiled-babel/index.js"]
|
||||
|
||||
18
Dockerfile-Dev
Normal file
@@ -0,0 +1,18 @@
|
||||
FROM node:8
|
||||
|
||||
# Install global packages
|
||||
RUN npm install -g gulp-cli mocha
|
||||
|
||||
# Clone Habitica repo and install dependencies
|
||||
RUN mkdir -p /usr/src/habitrpg
|
||||
WORKDIR /usr/src/habitrpg
|
||||
RUN git clone https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
|
||||
RUN cp config.json.example config.json
|
||||
RUN npm install
|
||||
|
||||
# Create Build dir
|
||||
RUN mkdir -p ./website/build
|
||||
|
||||
# Start Habitica
|
||||
EXPOSE 3000
|
||||
CMD ["npm", "start"]
|
||||
@@ -1,29 +0,0 @@
|
||||
FROM node:8
|
||||
|
||||
ENV ADMIN_EMAIL admin@habitica.com
|
||||
ENV AMAZON_PAYMENTS_CLIENT_ID amzn1.application-oa2-client.68ed9e6904ef438fbc1bf86bf494056e
|
||||
ENV AMAZON_PAYMENTS_SELLER_ID AMQ3SB4SG5E91
|
||||
ENV AMPLITUDE_KEY e8d4c24b3d6ef3ee73eeba715023dd43
|
||||
ENV BASE_URL https://habitica.com
|
||||
ENV FACEBOOK_KEY 128307497299777
|
||||
ENV GA_ID UA-33510635-1
|
||||
ENV GOOGLE_CLIENT_ID 1035232791481-32vtplgnjnd1aufv3mcu1lthf31795fq.apps.googleusercontent.com
|
||||
ENV NODE_ENV production
|
||||
ENV STRIPE_PUB_KEY pk_85fQ0yMECHNfHTSsZoxZXlPSwSNfA
|
||||
|
||||
# Install global packages
|
||||
RUN npm install -g gulp-cli mocha
|
||||
|
||||
# Clone Habitica repo and install dependencies
|
||||
RUN mkdir -p /usr/src/habitrpg
|
||||
WORKDIR /usr/src/habitrpg
|
||||
RUN git clone --branch v4.49.1 https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
|
||||
RUN npm install
|
||||
RUN gulp build:prod --force
|
||||
|
||||
# Create Build dir
|
||||
RUN mkdir -p ./website/build
|
||||
|
||||
# Start Habitica
|
||||
EXPOSE 3000
|
||||
CMD ["node", "./website/transpiled-babel/index.js"]
|
||||
@@ -78,6 +78,9 @@
|
||||
"PUSH_CONFIGS": {
|
||||
"GCM_SERVER_API_KEY": "",
|
||||
"APN_ENABLED": "false",
|
||||
"APN_KEY_ID": "xxxxxxxxxx",
|
||||
"APN_KEY": "xxxxxxxxxx",
|
||||
"APN_TEAM_ID": "aaabbbcccd",
|
||||
"FCM_SERVER_API_KEY": ""
|
||||
},
|
||||
"SITE_HTTP_AUTH": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// const migrationName = 'habits-one-history-entry-per-day';
|
||||
const migrationName = 'habits-one-history-entry-per-day';
|
||||
const authorName = 'paglias'; // in case script author needs to know when their ...
|
||||
const authorUuid = 'ed4c688c-6652-4a92-9d03-a5a79844174a'; // ... own data is done
|
||||
|
||||
@@ -14,7 +14,9 @@ const dbTasks = monk(connectionString).get('tasks', { castIds: false });
|
||||
const dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers (lastId) {
|
||||
let query = {};
|
||||
let query = {
|
||||
migration: {$ne: migrationName},
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
@@ -127,6 +129,11 @@ function updateUser (user) {
|
||||
.then(habits => {
|
||||
return Promise.all(habits.map(habit => updateHabit(habit, timezoneOffset, dayStart)));
|
||||
})
|
||||
.then(() => {
|
||||
return dbUsers.update({_id: user._id}, {
|
||||
$set: {migration: migrationName},
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
return exiting(1, `ERROR! ${ err}`);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
let migrationName = '20180102_takeThis.js'; // Update per month
|
||||
let migrationName = '20180702_takeThis.js'; // Update per month
|
||||
let authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
|
||||
|
||||
@@ -6,15 +6,16 @@ let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
|
||||
* Award Take This ladder items to participants in this month's challenge
|
||||
*/
|
||||
|
||||
let monk = require('monk');
|
||||
let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
let dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
import monk from 'monk';
|
||||
import nconf from 'nconf';
|
||||
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING'); // FOR TEST DATABASE
|
||||
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
|
||||
|
||||
function processUsers (lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
let query = {
|
||||
migration: {$ne: migrationName},
|
||||
challenges: {$in: ['5f70ce5b-2d82-4114-8e44-ca65615aae62']}, // Update per month
|
||||
challenges: {$in: ['f0481f95-1dde-4ae7-a876-d19502a45d61']}, // Update per month
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
155
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"version": "4.50.1",
|
||||
"version": "4.52.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -801,13 +801,25 @@
|
||||
}
|
||||
},
|
||||
"apn": {
|
||||
"version": "1.7.8",
|
||||
"resolved": "https://registry.npmjs.org/apn/-/apn-1.7.8.tgz",
|
||||
"integrity": "sha1-Hp2kKPtXr6lX5UIjvvc0LALCTNo=",
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/apn/-/apn-2.2.0.tgz",
|
||||
"integrity": "sha512-YIypYzPVJA9wzNBLKZ/mq2l1IZX/2FadPvwmSv4ZeR0VH7xdNITQ6Pucgh0Uw6ZZKC+XwheaJ57DFZAhJ0FvPg==",
|
||||
"requires": {
|
||||
"debug": "2.6.9",
|
||||
"node-forge": "0.6.49",
|
||||
"q": "1.5.1"
|
||||
"debug": "3.1.0",
|
||||
"http2": "https://github.com/node-apn/node-http2/archive/apn-2.1.4.tar.gz",
|
||||
"jsonwebtoken": "8.3.0",
|
||||
"node-forge": "0.7.5",
|
||||
"verror": "1.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"append-buffer": {
|
||||
@@ -5036,6 +5048,11 @@
|
||||
"resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz",
|
||||
"integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74="
|
||||
},
|
||||
"buffer-equal-constant-time": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
||||
"integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
|
||||
},
|
||||
"buffer-fill": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
|
||||
@@ -8792,6 +8809,14 @@
|
||||
"jsbn": "0.1.1"
|
||||
}
|
||||
},
|
||||
"ecdsa-sig-formatter": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz",
|
||||
"integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.2"
|
||||
}
|
||||
},
|
||||
"ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
@@ -13292,6 +13317,10 @@
|
||||
"sshpk": "1.14.1"
|
||||
}
|
||||
},
|
||||
"http2": {
|
||||
"version": "https://github.com/node-apn/node-http2/archive/apn-2.1.4.tar.gz",
|
||||
"integrity": "sha512-ad4u4I88X9AcUgxCRW3RLnbh7xHWQ1f5HbrXa7gEy2x4Xgq+rq+auGx5I+nUDE2YYuqteGIlbxrwQXkIaYTfnQ=="
|
||||
},
|
||||
"httpntlm": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.6.1.tgz",
|
||||
@@ -14768,6 +14797,29 @@
|
||||
"resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
|
||||
"integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk="
|
||||
},
|
||||
"jsonwebtoken": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.3.0.tgz",
|
||||
"integrity": "sha512-oge/hvlmeJCH+iIz1DwcO7vKPkNGJHhgkspk8OH3VKlw+mbi42WtD4ig1+VXRln765vxptAv+xT26Fd3cteqag==",
|
||||
"requires": {
|
||||
"jws": "3.1.5",
|
||||
"lodash.includes": "4.3.0",
|
||||
"lodash.isboolean": "3.0.3",
|
||||
"lodash.isinteger": "4.0.4",
|
||||
"lodash.isnumber": "3.0.3",
|
||||
"lodash.isplainobject": "4.0.6",
|
||||
"lodash.isstring": "4.0.1",
|
||||
"lodash.once": "4.1.1",
|
||||
"ms": "2.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ms": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
||||
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
||||
@@ -14806,6 +14858,25 @@
|
||||
"integrity": "sha512-mJVp13Ix6gFo3SBAy9U/kL+oeZqzlYYYLQBwXVBlVzIsZwBqGREnOro24oC/8s8aox+rJhtZ2DiQof++IrkA+g==",
|
||||
"dev": true
|
||||
},
|
||||
"jwa": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.6.tgz",
|
||||
"integrity": "sha512-tBO/cf++BUsJkYql/kBbJroKOgHWEigTKBAjjBEmrMGYd1QMBC74Hr4Wo2zCZw6ZrVhlJPvoMrkcOnlWR/DJfw==",
|
||||
"requires": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.10",
|
||||
"safe-buffer": "5.1.2"
|
||||
}
|
||||
},
|
||||
"jws": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz",
|
||||
"integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==",
|
||||
"requires": {
|
||||
"jwa": "1.1.6",
|
||||
"safe-buffer": "5.1.2"
|
||||
}
|
||||
},
|
||||
"kareem": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.1.0.tgz",
|
||||
@@ -16418,6 +16489,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
|
||||
},
|
||||
"lodash.includes": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||
"integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
|
||||
},
|
||||
"lodash.initial": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.initial/-/lodash.initial-4.1.1.tgz",
|
||||
@@ -16433,16 +16509,36 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
|
||||
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U="
|
||||
},
|
||||
"lodash.isboolean": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
||||
"integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
|
||||
},
|
||||
"lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
|
||||
},
|
||||
"lodash.isinteger": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||
"integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
|
||||
},
|
||||
"lodash.isnumber": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
||||
"integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
|
||||
},
|
||||
"lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
|
||||
},
|
||||
"lodash.isstring": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
||||
"integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
|
||||
},
|
||||
"lodash.istypedarray": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz",
|
||||
@@ -16484,6 +16580,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz",
|
||||
"integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ=="
|
||||
},
|
||||
"lodash.once": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
"integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
|
||||
},
|
||||
"lodash.pairs": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.pairs/-/lodash.pairs-3.0.1.tgz",
|
||||
@@ -18033,11 +18134,6 @@
|
||||
"resolved": "https://registry.npmjs.org/mpath/-/mpath-0.4.1.tgz",
|
||||
"integrity": "sha512-NNY/MpBkALb9jJmjpBlIi6GRoLveLUM0pJzgbp9vY9F7IQEb/HREC/nxrixechcQwd1NevOhJnWWV8QQQRE+OA=="
|
||||
},
|
||||
"mpns": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/mpns/-/mpns-2.1.3.tgz",
|
||||
"integrity": "sha512-gPLNoVqwYoKUmNYZ2shMSdaE2XvHSRxWNzyG4DUi6Av7MSujyeOw/nj61nnQeuV/vke5E0Dni468xn0qxTHIZQ=="
|
||||
},
|
||||
"mquery": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mquery/-/mquery-3.0.0.tgz",
|
||||
@@ -18457,9 +18553,9 @@
|
||||
}
|
||||
},
|
||||
"node-forge": {
|
||||
"version": "0.6.49",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.6.49.tgz",
|
||||
"integrity": "sha1-8e6V1ddGI5OP4Z1piqWibVTS9g8="
|
||||
"version": "0.7.5",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz",
|
||||
"integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ=="
|
||||
},
|
||||
"node-gcm": {
|
||||
"version": "0.14.10",
|
||||
@@ -18655,9 +18751,9 @@
|
||||
}
|
||||
},
|
||||
"node-rdkafka": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/node-rdkafka/-/node-rdkafka-2.3.3.tgz",
|
||||
"integrity": "sha512-2J54zC9+Zj0iRQttmQs1Ubv8aHhmh04XjP3vk39uco7l6tp8BYYHG4XRsoqKOGGKjBLctGpFHr9g97WBE1pTbg==",
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/node-rdkafka/-/node-rdkafka-2.3.4.tgz",
|
||||
"integrity": "sha512-ilaAOrEpDF3TGTlItsxU5pQXG+qjN1gKbhSvs9CoLXZaItt2EN6oU+kEdO6UkRQLKO6/Kv4m296cBrr0JCmiTw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"bindings": "1.3.0",
|
||||
@@ -20335,11 +20431,6 @@
|
||||
"pinkie": "2.0.4"
|
||||
}
|
||||
},
|
||||
"pipe-event": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pipe-event/-/pipe-event-0.1.0.tgz",
|
||||
"integrity": "sha1-pfXgPlqXsrdJPUsqBgzYPazLmmE="
|
||||
},
|
||||
"pixelsmith": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/pixelsmith/-/pixelsmith-2.2.1.tgz",
|
||||
@@ -22718,19 +22809,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"push-notify": {
|
||||
"version": "git://github.com/habitrpg/push-notify.git#6bc2b5fdb1bdc9649b9ec1964d79ca50187fc8a9",
|
||||
"requires": {
|
||||
"apn": "1.7.8",
|
||||
"bluebird": "3.5.1",
|
||||
"lodash": "4.17.10",
|
||||
"mpns": "2.1.3",
|
||||
"node-gcm": "0.14.10",
|
||||
"pipe-event": "0.1.0",
|
||||
"q": "1.5.1",
|
||||
"wns": "0.5.3"
|
||||
}
|
||||
},
|
||||
"pusher": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/pusher/-/pusher-1.5.1.tgz",
|
||||
@@ -27870,11 +27948,6 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"wns": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/wns/-/wns-0.5.3.tgz",
|
||||
"integrity": "sha1-APToXPz44zg9y9gYmJBvH2rUhF8="
|
||||
},
|
||||
"wordwrap": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "4.50.1",
|
||||
"version": "4.52.2",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@slack/client": "^3.8.1",
|
||||
@@ -11,6 +11,7 @@
|
||||
"apidoc": "^0.17.5",
|
||||
"autoprefixer": "^8.5.0",
|
||||
"aws-sdk": "^2.239.1",
|
||||
"apn": "^2.2.0",
|
||||
"axios": "^0.18.0",
|
||||
"axios-progress-bar": "^1.2.0",
|
||||
"babel-core": "^6.26.3",
|
||||
@@ -78,7 +79,6 @@
|
||||
"postcss-easy-import": "^3.0.0",
|
||||
"ps-tree": "^1.0.0",
|
||||
"pug": "^2.0.3",
|
||||
"push-notify": "git://github.com/habitrpg/push-notify.git#6bc2b5fdb1bdc9649b9ec1964d79ca50187fc8a9",
|
||||
"pusher": "^1.3.0",
|
||||
"rimraf": "^2.4.3",
|
||||
"sass-loader": "^7.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { model as User } from '../../../../website/server/models/user';
|
||||
import requireAgain from 'require-again';
|
||||
import pushNotify from 'push-notify';
|
||||
import apn from 'apn/mock';
|
||||
import nconf from 'nconf';
|
||||
import gcmLib from 'node-gcm'; // works with FCM notifications too
|
||||
|
||||
@@ -24,7 +24,7 @@ describe('pushNotifications', () => {
|
||||
|
||||
sandbox.stub(gcmLib.Sender.prototype, 'send').callsFake(fcmSendSpy);
|
||||
|
||||
sandbox.stub(pushNotify, 'apn').returns({
|
||||
sandbox.stub(apn.Provider.prototype, 'send').returns({
|
||||
on: () => null,
|
||||
send: apnSendSpy,
|
||||
});
|
||||
@@ -104,10 +104,7 @@ describe('pushNotifications', () => {
|
||||
},
|
||||
};
|
||||
|
||||
sendPushNotification(user, details);
|
||||
expect(apnSendSpy).to.have.been.calledOnce;
|
||||
expect(apnSendSpy).to.have.been.calledWithMatch({
|
||||
token: '123',
|
||||
const expectedNotification = new apn.Notification({
|
||||
alert: message,
|
||||
sound: 'default',
|
||||
category: 'fun',
|
||||
@@ -117,6 +114,10 @@ describe('pushNotifications', () => {
|
||||
b: true,
|
||||
},
|
||||
});
|
||||
|
||||
sendPushNotification(user, details);
|
||||
expect(apnSendSpy).to.have.been.calledOnce;
|
||||
expect(apnSendSpy).to.have.been.calledWithMatch(expectedNotification, '123');
|
||||
expect(fcmSendSpy).to.not.have.been.called;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -388,6 +388,23 @@ describe('POST /chat', () => {
|
||||
expect(groupMessages[0].id).to.exist;
|
||||
});
|
||||
|
||||
it('creates a chat with a max length of 3000 chars', async () => {
|
||||
const veryLongMessage = `
|
||||
123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789.
|
||||
THIS PART WON'T BE IN THE MESSAGE (over 3000)
|
||||
`;
|
||||
|
||||
const newMessage = await user.post(`/groups/${groupWithChat._id}/chat`, { message: veryLongMessage});
|
||||
const groupMessages = await user.get(`/groups/${groupWithChat._id}/chat`);
|
||||
|
||||
expect(newMessage.message.id).to.exist;
|
||||
expect(groupMessages[0].id).to.exist;
|
||||
|
||||
expect(newMessage.message.text.length).to.eql(3000);
|
||||
expect(newMessage.message.text).to.not.contain('MESSAGE');
|
||||
expect(groupMessages[0].text.length).to.eql(3000);
|
||||
});
|
||||
|
||||
it('creates a chat with user styles', async () => {
|
||||
const mount = 'test-mount';
|
||||
const pet = 'test-pet';
|
||||
|
||||
@@ -23,6 +23,17 @@ describe('GET /export/userdata.xml', () => {
|
||||
|
||||
]);
|
||||
|
||||
// add pinnedItem
|
||||
await user.get('/user/toggle-pinned-item/marketGear/gear.flat.shield_rogue_5');
|
||||
|
||||
// add a private message
|
||||
let receiver = await generateUser();
|
||||
|
||||
user.post('/members/send-private-message', {
|
||||
message: 'Your first message, hi!',
|
||||
toUserId: receiver._id,
|
||||
});
|
||||
|
||||
let response = await user.get('/export/userdata.xml');
|
||||
let {user: res} = await parseStringAsync(response, {explicitArray: false});
|
||||
|
||||
|
||||
@@ -114,6 +114,19 @@ describe('Post /groups/:groupId/invite', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('returns error when recipient has blocked the senders', async () => {
|
||||
const inviterNoBlocks = await inviter.update({'inbox.blocks': []});
|
||||
let userWithBlockedInviter = await generateUser({'inbox.blocks': [inviter._id]});
|
||||
await expect(inviterNoBlocks.post(`/groups/${group._id}/invite`, {
|
||||
uuids: [userWithBlockedInviter._id],
|
||||
}))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('notAuthorizedToSendMessageToThisUser'),
|
||||
});
|
||||
});
|
||||
|
||||
it('invites a user to a group by uuid', async () => {
|
||||
let userToInvite = await generateUser();
|
||||
|
||||
|
||||
23
test/client/unit/specs/components/memberDetails.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import Vue from 'vue';
|
||||
import MemberDetailsComponent from 'client/components/memberDetails.vue';
|
||||
|
||||
describe('Members Details Component', () => {
|
||||
let CTor;
|
||||
let vm;
|
||||
|
||||
beforeEach(() => {
|
||||
CTor = Vue.extend(MemberDetailsComponent);
|
||||
vm = new CTor().$mount();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
});
|
||||
|
||||
xit('prevents flickering by setting a 1px margin-right on elements of class member-stats', () => {
|
||||
const memberstats = vm.$el.querySelector('.member-stats');
|
||||
const style = window.getComputedStyle(memberstats, null);
|
||||
const marginRightProp = style.getPropertyValue('margin-right');
|
||||
expect(marginRightProp).to.equal('1');
|
||||
});
|
||||
});
|
||||
94
test/common/ops/buy/buyQuestGems.js
Normal file
@@ -0,0 +1,94 @@
|
||||
import pinnedGearUtils from '../../../../website/common/script/ops/pinnedGearUtils';
|
||||
import {
|
||||
NotAuthorized,
|
||||
} from '../../../../website/common/script/libs/errors';
|
||||
import i18n from '../../../../website/common/script/i18n';
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../helpers/common.helper';
|
||||
import {BuyQuestWithGemOperation} from '../../../../website/common/script/ops/buy/buyQuestGem';
|
||||
|
||||
describe('shared.ops.buyQuestGems', () => {
|
||||
let user;
|
||||
let goldPoints = 40;
|
||||
let analytics = {track () {}};
|
||||
|
||||
function buyQuest (_user, _req, _analytics) {
|
||||
const buyOp = new BuyQuestWithGemOperation(_user, _req, _analytics);
|
||||
|
||||
return buyOp.purchase();
|
||||
}
|
||||
|
||||
before(() => {
|
||||
user = generateUser({'stats.class': 'rogue'});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(analytics, 'track');
|
||||
sinon.spy(pinnedGearUtils, 'removeItemByPath');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
pinnedGearUtils.removeItemByPath.restore();
|
||||
});
|
||||
|
||||
context('successful purchase', () => {
|
||||
let userGemAmount = 10;
|
||||
|
||||
before(() => {
|
||||
user.balance = userGemAmount;
|
||||
user.stats.gp = goldPoints;
|
||||
user.purchased.plan.gemsBought = 0;
|
||||
user.purchased.plan.customerId = 'customer-id';
|
||||
user.pinnedItems.push({type: 'quests', key: 'gryphon'});
|
||||
});
|
||||
|
||||
it('purchases quests', () => {
|
||||
let key = 'gryphon';
|
||||
|
||||
buyQuest(user, {params: {key}});
|
||||
|
||||
expect(user.items.quests[key]).to.equal(1);
|
||||
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
context('bulk purchase', () => {
|
||||
let userGemAmount = 10;
|
||||
|
||||
beforeEach(() => {
|
||||
user.balance = userGemAmount;
|
||||
user.stats.gp = goldPoints;
|
||||
user.purchased.plan.gemsBought = 0;
|
||||
user.purchased.plan.customerId = 'customer-id';
|
||||
});
|
||||
|
||||
it('errors when user does not have enough gems', (done) => {
|
||||
user.balance = 1;
|
||||
let key = 'gryphon';
|
||||
|
||||
try {
|
||||
buyQuest(user, {
|
||||
params: {key},
|
||||
quantity: 2,
|
||||
});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('makes bulk purchases of quests', () => {
|
||||
let key = 'gryphon';
|
||||
|
||||
buyQuest(user, {
|
||||
params: {key},
|
||||
quantity: 3,
|
||||
});
|
||||
|
||||
expect(user.items.quests[key]).to.equal(4);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -121,7 +121,6 @@ describe('shared.ops.purchase', () => {
|
||||
user.pinnedItems.push({type: 'eggs', key: 'Wolf'});
|
||||
user.pinnedItems.push({type: 'hatchingPotions', key: 'Base'});
|
||||
user.pinnedItems.push({type: 'food', key: SEASONAL_FOOD});
|
||||
user.pinnedItems.push({type: 'quests', key: 'gryphon'});
|
||||
user.pinnedItems.push({type: 'gear', key: 'headAccessory_special_tigerEars'});
|
||||
user.pinnedItems.push({type: 'bundles', key: 'featheredFriends'});
|
||||
});
|
||||
@@ -157,16 +156,6 @@ describe('shared.ops.purchase', () => {
|
||||
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
|
||||
});
|
||||
|
||||
it('purchases quests', () => {
|
||||
let type = 'quests';
|
||||
let key = 'gryphon';
|
||||
|
||||
purchase(user, {params: {type, key}});
|
||||
|
||||
expect(user.items[type][key]).to.equal(1);
|
||||
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
|
||||
});
|
||||
|
||||
it('purchases gear', () => {
|
||||
let type = 'gear';
|
||||
let key = 'headAccessory_special_tigerEars';
|
||||
|
||||
@@ -62,7 +62,11 @@ module.exports = {
|
||||
target: DEV_BASE_URL,
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/logout': {
|
||||
'/logout-server': {
|
||||
target: DEV_BASE_URL,
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/export': {
|
||||
target: DEV_BASE_URL,
|
||||
changeOrigin: true,
|
||||
},
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
# Running
|
||||
For information about installing Habitica locally, see [Setting up Habitica Locally](http://habitica.wikia.com/wiki/Setting_up_Habitica_Locally) and for information about running the local client, refer to the ["Run Habitica" section](http://habitica.wikia.com/wiki/Setting_up_Habitica_Locally#Run_Habitica) in that page.
|
||||
For information about installing and running Habitica locally, see [Setting up Habitica Locally](http://habitica.wikia.com/wiki/Setting_up_Habitica_Locally).
|
||||
|
||||
# Preparation Reading
|
||||
- Vue 2 (https://vuejs.org)
|
||||
|
||||
- Webpack (https://webpack.github.io/) is the build system and it includes plugins for code transformation, right now we have: BabelJS for ES6 transpilation, eslint for code style, less and postcss for css compilation. The code comes from https://github.com/vuejs-templates/webpack which is a Webpack template for Vue, with some small modifications to adapt it to our use case. Docs http://vuejs-templates.github.io/webpack/
|
||||
|
||||
- We’re using `.vue` files that make it possible to have HTML, JS and CSS for each component together in a single location. They’re implemented as a webpack plugin and the docs can be found here http://vue-loader.vuejs.org/en/
|
||||
- We're using `.vue` files that make it possible to have HTML, JS and CSS for each component together in a single location. They're implemented as a webpack plugin and the docs can be found here http://vue-loader.vuejs.org/en/
|
||||
|
||||
- SemanticUI is the UI framework http://semantic-ui.com/. So far I’ve only used the CSS part, it also has JS plugins but I’ve yet to use them. It supports theming so if it’s not too difficult we’ll want to customize the base theme with our own styles instead of writing CSS rules to override the original styling.
|
||||
- SemanticUI is the UI framework http://semantic-ui.com/. So far I've only used the CSS part, it also has JS plugins but I've yet to use them. It supports theming so if it's not too difficult we'll want to customize the base theme with our own styles instead of writing CSS rules to override the original styling.
|
||||
|
||||
The code is in `/website/client`. We’re using something very similar to Vuex (equivalent of React’s Redux) for state management http://vuex.vuejs.org/en/index.html
|
||||
The code is in `/website/client`. We're using something very similar to Vuex (equivalent of React's Redux) for state management http://vuex.vuejs.org/en/index.html
|
||||
|
||||
The API is almost the same except that we don’t use mutations but only actions because it would make it difficult to work with common code
|
||||
The API is almost the same except that we don't use mutations but only actions because it would make it difficult to work with common code
|
||||
|
||||
The project is developed directly in the `develop` branch as long as we’ll be able to avoid splitting it into a different branch.
|
||||
The project is developed directly in the `develop` branch as long as we'll be able to avoid splitting it into a different branch.
|
||||
|
||||
So far most of the work has been on the template, so there’s no complex logic to understand. The only thing I would suggest you to read about is Vuex for data management: it’s basically a Flux implementation: there’s a central store that hold the data for the entire app, and every change to the data must happen through an action, the data cannot be mutated directly.
|
||||
So far most of the work has been on the template, so there's no complex logic to understand. The only thing I would suggest you to read about is Vuex for data management: it's basically a Flux implementation: there's a central store that hold the data for the entire app, and every change to the data must happen through an action, the data cannot be mutated directly.
|
||||
|
||||
For further resources, see [Guidance for Blacksmiths](http://habitica.wikia.com/wiki/Guidance_for_Blacksmiths), and in particular the ["Website Technology Stack" section](http://habitica.wikia.com/wiki/Guidance_for_Blacksmiths#Website_Technology_Stack).
|
||||
|
||||
@@ -116,7 +116,7 @@ div
|
||||
|
||||
/* Push progress bar above modals */
|
||||
#nprogress .bar {
|
||||
z-index: 1090 !important; /* Must stay above nav bar */
|
||||
z-index: 1600 !important; /* Must stay above nav bar */
|
||||
}
|
||||
|
||||
.restingInn {
|
||||
@@ -136,7 +136,7 @@ div
|
||||
background-color: $blue-10;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 1030;
|
||||
z-index: 1300;
|
||||
display: flex;
|
||||
|
||||
.content {
|
||||
@@ -331,9 +331,33 @@ export default {
|
||||
];
|
||||
if (notificationNotFoundMessage.indexOf(errorMessage) !== -1) snackbarTimeout = true;
|
||||
|
||||
let errorsToShow = [];
|
||||
let usernameCheck = false;
|
||||
let emailCheck = false;
|
||||
let passwordCheck = false;
|
||||
// show only the first error for each param
|
||||
if (errorData.errors) {
|
||||
for (let e of errorData.errors) {
|
||||
if (!usernameCheck && e.param === 'username') {
|
||||
errorsToShow.push(e.message);
|
||||
usernameCheck = true;
|
||||
}
|
||||
if (!emailCheck && e.param === 'email') {
|
||||
errorsToShow.push(e.message);
|
||||
emailCheck = true;
|
||||
}
|
||||
if (!passwordCheck && e.param === 'password') {
|
||||
errorsToShow.push(e.message);
|
||||
passwordCheck = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errorsToShow.push(errorMessage);
|
||||
}
|
||||
// dispatch as one snackbar notification
|
||||
this.$store.dispatch('snackbars:add', {
|
||||
title: 'Habitica',
|
||||
text: errorMessage,
|
||||
text: errorsToShow.join(' '),
|
||||
type: 'error',
|
||||
timeout: snackbarTimeout,
|
||||
});
|
||||
|
||||
@@ -1,66 +1,78 @@
|
||||
.promo_aquatic_glass_potions {
|
||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -627px 0px;
|
||||
background-position: -853px 0px;
|
||||
width: 141px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_armoire_backgrounds_201806 {
|
||||
.promo_armoire_backgrounds_201807 {
|
||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -627px -442px;
|
||||
background-position: -853px -442px;
|
||||
width: 141px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_bundle_aquaticAmigos {
|
||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -142px -533px;
|
||||
width: 423px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_ios {
|
||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px 0px;
|
||||
width: 325px;
|
||||
height: 336px;
|
||||
background-position: -477px 0px;
|
||||
width: 375px;
|
||||
height: 361px;
|
||||
}
|
||||
.promo_mystery_201806 {
|
||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -769px -442px;
|
||||
background-position: -995px -442px;
|
||||
width: 121px;
|
||||
height: 114px;
|
||||
}
|
||||
.promo_seafoam {
|
||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -769px 0px;
|
||||
background-position: -995px 0px;
|
||||
width: 141px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_seaserpent {
|
||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px 0px;
|
||||
width: 476px;
|
||||
height: 364px;
|
||||
}
|
||||
.promo_seasonal_shop_summer {
|
||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -142px -681px;
|
||||
background-position: -401px -552px;
|
||||
width: 162px;
|
||||
height: 138px;
|
||||
}
|
||||
.promo_splashy_skins {
|
||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -142px -365px;
|
||||
width: 375px;
|
||||
height: 186px;
|
||||
}
|
||||
.customize-option.promo_splashy_skins {
|
||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -167px -380px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
.promo_summer_splash_2018 {
|
||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: 0px -337px;
|
||||
background-position: 0px -365px;
|
||||
width: 141px;
|
||||
height: 588px;
|
||||
}
|
||||
.promo_take_this {
|
||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -769px -557px;
|
||||
background-position: -995px -557px;
|
||||
width: 96px;
|
||||
height: 69px;
|
||||
}
|
||||
.scene_families {
|
||||
.promo_unconventional_armor {
|
||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -142px -337px;
|
||||
width: 345px;
|
||||
height: 195px;
|
||||
background-position: -518px -365px;
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
}
|
||||
.scene_moderators {
|
||||
.scene_pomodoro {
|
||||
background-image: url('~assets/images/sprites/spritesmith-largeSprites-0.png');
|
||||
background-position: -326px 0px;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
background-position: -142px -552px;
|
||||
width: 258px;
|
||||
height: 258px;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 546 KiB After Width: | Height: | Size: 553 KiB |
|
Before Width: | Height: | Size: 416 KiB After Width: | Height: | Size: 432 KiB |
|
Before Width: | Height: | Size: 350 KiB After Width: | Height: | Size: 342 KiB |
|
Before Width: | Height: | Size: 300 KiB After Width: | Height: | Size: 323 KiB |
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 147 KiB |
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 166 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 153 KiB After Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
@@ -58,7 +58,6 @@
|
||||
<script>
|
||||
import Avatar from '../avatar';
|
||||
import { mapState } from 'client/libs/store';
|
||||
import * as Analytics from 'client/libs/analytics';
|
||||
import percent from '../../../common/script/libs/percent';
|
||||
import {maxHealth} from '../../../common/script/index';
|
||||
|
||||
@@ -82,14 +81,6 @@ export default {
|
||||
return `${Math.ceil(this.user.stats.hp)} / ${this.maxHealth}`;
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
Analytics.track({
|
||||
hitType: 'event',
|
||||
eventCategory: 'button',
|
||||
eventAction: 'click',
|
||||
eventLabel: 'Health Warning',
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'low-health');
|
||||
|
||||
@@ -112,9 +112,10 @@ export default {
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
try {
|
||||
const redirectUrl = `${window.location.protocol}//${window.location.host}`;
|
||||
let auth = await hello(network).login({
|
||||
scope: 'email',
|
||||
redirect_uri: '', // eslint-disable-line camelcase
|
||||
redirect_uri: redirectUrl, // eslint-disable-line camelcase
|
||||
});
|
||||
|
||||
await this.$store.dispatch('auth:socialAuth', {
|
||||
|
||||
13
website/client/components/auth/logout.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script>
|
||||
export default {
|
||||
components: {},
|
||||
methods: {
|
||||
async logout () {
|
||||
return await this.$store.dispatch('auth:logout');
|
||||
},
|
||||
},
|
||||
created () {
|
||||
this.logout();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -402,11 +402,6 @@ export default {
|
||||
window.location.href = redirectTo;
|
||||
},
|
||||
async login () {
|
||||
if (!this.username) {
|
||||
alert('Email is required');
|
||||
return;
|
||||
}
|
||||
|
||||
await this.$store.dispatch('auth:login', {
|
||||
username: this.username,
|
||||
// email: this.email,
|
||||
@@ -433,10 +428,11 @@ export default {
|
||||
await hello(network).logout();
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
const redirectUrl = `${window.location.protocol}//${window.location.host}`;
|
||||
let auth = await hello(network).login({
|
||||
scope: 'email',
|
||||
// explicitly pass the redirect url or it might redirect to /home
|
||||
redirect_uri: '', // eslint-disable-line camelcase
|
||||
redirect_uri: redirectUrl, // eslint-disable-line camelcase
|
||||
});
|
||||
|
||||
await this.$store.dispatch('auth:socialAuth', {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
challenge-modal(v-on:updatedChallenge='updatedChallenge')
|
||||
leave-challenge-modal(:challengeId='challenge._id')
|
||||
close-challenge-modal(:members='members', :challengeId='challenge._id')
|
||||
challenge-member-progress-modal(:memberId='progressMemberId', :challengeId='challenge._id')
|
||||
challenge-member-progress-modal(:challengeId='challenge._id')
|
||||
.col-12.col-md-8.standard-page
|
||||
.row
|
||||
.col-12.col-md-6
|
||||
@@ -231,7 +231,6 @@ export default {
|
||||
creatingTask: {},
|
||||
workingTask: {},
|
||||
taskFormPurpose: 'create',
|
||||
progressMemberId: '',
|
||||
searchTerm: '',
|
||||
memberResults: [],
|
||||
};
|
||||
@@ -345,6 +344,7 @@ export default {
|
||||
this.tasksByType[task.type].splice(index, 1);
|
||||
},
|
||||
showMemberModal () {
|
||||
// @TODO: Change these to options and add a custom event to members modal
|
||||
this.$store.state.memberModalOptions.challengeId = this.challenge._id;
|
||||
this.$store.state.memberModalOptions.groupId = 'challenge'; // @TODO: change these terrible settings
|
||||
this.$store.state.memberModalOptions.group = this.group;
|
||||
@@ -374,8 +374,9 @@ export default {
|
||||
Object.assign(this.challenge, eventData.challenge);
|
||||
},
|
||||
openMemberProgressModal (member) {
|
||||
this.progressMemberId = member._id;
|
||||
this.$root.$emit('bv::show::modal', 'challenge-member-modal');
|
||||
this.$root.$emit('habitica:challenge:member-progress', {
|
||||
progressMemberId: member._id,
|
||||
});
|
||||
},
|
||||
async exportChallengeCsv () {
|
||||
// let response = await this.$store.dispatch('challenges:exportChallengeCsv', {
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
.challenge-description(v-markdown='challenge.summary')
|
||||
.well-wrapper(v-if="fullLayout")
|
||||
.well
|
||||
div(v-for="task in tasksData", :class="{'muted': task.value === 0}", v-once)
|
||||
div(v-for="task in tasksData", :class="{'muted': task.value === 0}")
|
||||
.number
|
||||
.svg-icon(v-html="task.icon", :class="task.label + '-icon'")
|
||||
span.value {{ task.value }}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<template lang="pug">
|
||||
b-modal#challenge-member-modal(title="User Progress", size='lg')
|
||||
.row.award-row
|
||||
.col-12.text-center
|
||||
button.btn.btn-primary(v-once, @click='closeChallenge()') {{ $t('awardWinners') }}
|
||||
.row
|
||||
task-column.col-6(
|
||||
v-for="column in columns",
|
||||
@@ -8,12 +11,19 @@
|
||||
:taskListOverride='tasksByType[column]')
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.award-row {
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import Column from '../tasks/column';
|
||||
|
||||
export default {
|
||||
props: ['challengeId', 'memberId'],
|
||||
props: ['challengeId'],
|
||||
components: {
|
||||
TaskColumn: Column,
|
||||
},
|
||||
@@ -26,8 +36,19 @@ export default {
|
||||
todo: [],
|
||||
reward: [],
|
||||
},
|
||||
memberId: '',
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.$root.$on('habitica:challenge:member-progress', (data) => {
|
||||
if (!data.progressMemberId) return;
|
||||
this.memberId = data.progressMemberId;
|
||||
this.$root.$emit('bv::show::modal', 'challenge-member-modal');
|
||||
});
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$root.$off('habitica:challenge:member-progress');
|
||||
},
|
||||
watch: {
|
||||
async memberId (id) {
|
||||
if (!id) return;
|
||||
@@ -45,5 +66,14 @@ export default {
|
||||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async closeChallenge () {
|
||||
this.challenge = await this.$store.dispatch('challenges:selectChallengeWinner', {
|
||||
challengeId: this.challengeId,
|
||||
winnerId: this.memberId,
|
||||
});
|
||||
this.$router.push('/challenges/myChallenges');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -100,6 +100,7 @@ export default {
|
||||
if (!data.message || !data.groupId) return;
|
||||
this.abuseObject = data.message;
|
||||
this.groupId = data.groupId;
|
||||
this.reportComment = '';
|
||||
this.$root.$emit('bv::show::modal', 'report-flag');
|
||||
});
|
||||
},
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
)
|
||||
.row.tasks-navigation
|
||||
.col-12.col-md-4
|
||||
h1 Group's Tasks
|
||||
h1 {{ $t('groupTasksTitle') }}
|
||||
// @TODO: Abstract to component!
|
||||
.col-12.col-md-4
|
||||
.input-group
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
:class='{"user-entry": newMessage}',
|
||||
@keydown='updateCarretPosition',
|
||||
@keyup.ctrl.enter='sendMessageShortcut()',
|
||||
@paste='disableMessageSendShortcut()'
|
||||
@paste='disableMessageSendShortcut()',
|
||||
maxlength='3000'
|
||||
)
|
||||
span {{ currentLength }} / 3000
|
||||
autocomplete(
|
||||
:text='newMessage',
|
||||
v-on:select="selectedAutocomplete",
|
||||
@@ -62,6 +64,11 @@
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentLength () {
|
||||
return this.newMessage.length;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// https://medium.com/@_jh3y/how-to-where-s-the-caret-getting-the-xy-position-of-the-caret-a24ba372990a
|
||||
getCoord (e, text) {
|
||||
|
||||
@@ -164,12 +164,6 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
shareUserId () {
|
||||
Analytics.track({
|
||||
hitType: 'event',
|
||||
eventCategory: 'button',
|
||||
eventAction: 'click',
|
||||
eventLabel: 'Health Warning',
|
||||
});
|
||||
this.shareUserIdShown = !this.shareUserIdShown;
|
||||
},
|
||||
async createParty () {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#groupPrivateDescription1.icon(:title="$t('privateDescription')")
|
||||
.svg-icon(v-html='icons.information')
|
||||
b-tooltip(
|
||||
:title="$t('privateDescription')",
|
||||
:title="$t('onlyLeaderCreatesChallengesDetail')",
|
||||
target="groupPrivateDescription1",
|
||||
)
|
||||
|
||||
@@ -365,13 +365,6 @@ export default {
|
||||
alert(this.$t('notEnoughGems'));
|
||||
return;
|
||||
// @TODO return $rootScope.openModal('buyGems', {track:"Gems > Gems > Create Group"});
|
||||
// @TODO when modal is implemented, enable analytics
|
||||
/* Analytics.track({
|
||||
hitType: 'event',
|
||||
eventCategory: 'button',
|
||||
eventAction: 'click',
|
||||
eventLabel: 'Health Warning',
|
||||
}); */
|
||||
}
|
||||
|
||||
let errors = [];
|
||||
|
||||
@@ -54,7 +54,6 @@ import { mapState } from 'client/libs/store';
|
||||
import filter from 'lodash/filter';
|
||||
import map from 'lodash/map';
|
||||
import notifications from 'client/mixins/notifications';
|
||||
import * as Analytics from 'client/libs/analytics';
|
||||
|
||||
export default {
|
||||
mixins: [notifications],
|
||||
@@ -65,14 +64,6 @@ export default {
|
||||
emails: [],
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
Analytics.track({
|
||||
hitType: 'event',
|
||||
eventCategory: 'button',
|
||||
eventAction: 'click',
|
||||
eventLabel: 'Invite Friends',
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
inviter () {
|
||||
|
||||
@@ -53,6 +53,9 @@ div
|
||||
span.dropdown-icon-item
|
||||
.svg-icon.inline(v-html="icons.removeIcon")
|
||||
span.text {{$t('removeManager2')}}
|
||||
b-dropdown-item(@click='viewProgress(member)')
|
||||
span.dropdown-icon-item
|
||||
span.text {{ $t('viewProgress') }}
|
||||
.row(v-if='isLoadMoreAvailable')
|
||||
.col-12.text-center
|
||||
button.btn.btn-secondary(@click='loadMoreMembers()') {{ $t('loadMore') }}
|
||||
@@ -475,6 +478,11 @@ export default {
|
||||
groupData.leader = member;
|
||||
this.$root.$emit('updatedGroup', groupData);
|
||||
},
|
||||
viewProgress (member) {
|
||||
this.$root.$emit('habitica:challenge:member-progress', {
|
||||
progressMemberId: member._id,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
drawer(
|
||||
:title="$t('equipment')",
|
||||
:errorMessage="(costume && !user.preferences.costume) ? $t('costumeDisabled') : null",
|
||||
:errorMessage="(costumeMode && !user.preferences.costume) ? $t('costumeDisabled') : null",
|
||||
:openStatus='openStatus',
|
||||
v-on:toggled='drawerToggled'
|
||||
)
|
||||
@@ -44,18 +44,18 @@
|
||||
.drawer-tab-container
|
||||
.drawer-tab.text-right
|
||||
a.drawer-tab-text(
|
||||
@click="costume = false",
|
||||
:class="{'drawer-tab-text-active': costume === false}",
|
||||
@click="selectDrawerTab('equipment')",
|
||||
:class="{'drawer-tab-text-active': !costumeMode}",
|
||||
) {{ $t('equipment') }}
|
||||
.clearfix
|
||||
.drawer-tab.float-left
|
||||
a.drawer-tab-text(
|
||||
@click="costume = true",
|
||||
:class="{'drawer-tab-text-active': costume === true}",
|
||||
@click="selectDrawerTab('costume')",
|
||||
:class="{'drawer-tab-text-active': costumeMode}",
|
||||
) {{ $t('costume') }}
|
||||
|
||||
toggle-switch.float-right.align-with-tab(
|
||||
:label="$t(costume ? 'useCostume' : 'autoEquipBattleGear')",
|
||||
:label="$t(costumeMode ? 'useCostume' : 'autoEquipBattleGear')",
|
||||
:checked="user.preferences[drawerPreference]",
|
||||
@change="changeDrawerPreference",
|
||||
:hoverText="$t(drawerPreference+'PopoverText')",
|
||||
@@ -77,7 +77,7 @@
|
||||
template(slot="itemBadge", slot-scope="context")
|
||||
starBadge(
|
||||
:selected="true",
|
||||
:show="!costume || user.preferences.costume",
|
||||
:show="!costumeMode || user.preferences.costume",
|
||||
@click="equipItem(context.item)",
|
||||
)
|
||||
div(
|
||||
@@ -109,7 +109,7 @@
|
||||
template(slot="itemBadge", slot-scope="context")
|
||||
starBadge(
|
||||
:selected="activeItems[context.item.type] === context.item.key",
|
||||
:show="!costume || user.preferences.costume",
|
||||
:show="!costumeMode || user.preferences.costume",
|
||||
@click="equipItem(context.item)",
|
||||
)
|
||||
template(slot="popoverContent", slot-scope="context")
|
||||
@@ -119,7 +119,7 @@
|
||||
:item="gearToEquip",
|
||||
@equipItem="equipItem($event)",
|
||||
@change="changeModalState($event)",
|
||||
:costumeMode="costume",
|
||||
:costumeMode="costumeMode",
|
||||
:isEquipped="gearToEquip == null ? false : activeItems[gearToEquip.type] === gearToEquip.key"
|
||||
)
|
||||
</template>
|
||||
@@ -185,7 +185,7 @@ export default {
|
||||
itemsPerLine: 9,
|
||||
searchText: null,
|
||||
searchTextThrottled: null,
|
||||
costume: false,
|
||||
costumeMode: false,
|
||||
groupBy: 'type', // or 'class'
|
||||
gearTypesToStrings: Object.freeze({ // TODO use content.itemList?
|
||||
weapon: i18n.t('weaponCapitalized'),
|
||||
@@ -219,11 +219,24 @@ export default {
|
||||
},
|
||||
mounted () {
|
||||
const drawerState = getLocalSetting(CONSTANTS.keyConstants.EQUIPMENT_DRAWER_STATE);
|
||||
if (drawerState === CONSTANTS.valueConstants.DRAWER_CLOSED) {
|
||||
if (drawerState === CONSTANTS.drawerStateValues.DRAWER_CLOSED) {
|
||||
this.$store.state.equipmentDrawerOpen = false;
|
||||
}
|
||||
|
||||
this.costumeMode = getLocalSetting(CONSTANTS.keyConstants.CURRENT_EQUIPMENT_DRAWER_TAB) === CONSTANTS.equipmentDrawerTabValues.COSTUME_TAB ? true : false;
|
||||
},
|
||||
methods: {
|
||||
selectDrawerTab (tabName) {
|
||||
let tabNameValue;
|
||||
if (tabName === 'costume') {
|
||||
tabNameValue = CONSTANTS.equipmentDrawerTabValues.COSTUME_TAB;
|
||||
this.costumeMode = true;
|
||||
} else {
|
||||
tabNameValue = CONSTANTS.equipmentDrawerTabValues.EQUIPMENT_TAB;
|
||||
this.costumeMode = false;
|
||||
}
|
||||
setLocalSetting(CONSTANTS.keyConstants.CURRENT_EQUIPMENT_DRAWER_TAB, tabNameValue);
|
||||
},
|
||||
openEquipDialog (item) {
|
||||
this.gearToEquip = item;
|
||||
},
|
||||
@@ -233,7 +246,7 @@ export default {
|
||||
}
|
||||
},
|
||||
equipItem (item) {
|
||||
this.$store.dispatch('common:equip', {key: item.key, type: this.costume ? 'costume' : 'equipped'});
|
||||
this.$store.dispatch('common:equip', {key: item.key, type: this.costumeMode ? 'costume' : 'equipped'});
|
||||
this.gearToEquip = null;
|
||||
},
|
||||
changeDrawerPreference (newVal) {
|
||||
@@ -260,11 +273,11 @@ export default {
|
||||
this.$store.state.equipmentDrawerOpen = newState;
|
||||
|
||||
if (newState) {
|
||||
setLocalSetting(CONSTANTS.keyConstants.EQUIPMENT_DRAWER_STATE, CONSTANTS.valueConstants.DRAWER_OPEN);
|
||||
setLocalSetting(CONSTANTS.keyConstants.EQUIPMENT_DRAWER_STATE, CONSTANTS.drawerStateValues.DRAWER_OPEN);
|
||||
return;
|
||||
}
|
||||
|
||||
setLocalSetting(CONSTANTS.keyConstants.EQUIPMENT_DRAWER_STATE, CONSTANTS.valueConstants.DRAWER_CLOSED);
|
||||
setLocalSetting(CONSTANTS.keyConstants.EQUIPMENT_DRAWER_STATE, CONSTANTS.drawerStateValues.DRAWER_CLOSED);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
@@ -280,10 +293,10 @@ export default {
|
||||
return this.$store.state.equipmentDrawerOpen ? 1 : 0;
|
||||
},
|
||||
drawerPreference () {
|
||||
return this.costume === true ? 'costume' : 'autoEquip';
|
||||
return this.costumeMode ? 'costume' : 'autoEquip';
|
||||
},
|
||||
activeItems () {
|
||||
return this.costume === true ? this.costumeItems : this.equippedItems;
|
||||
return this.costumeMode ? this.costumeItems : this.equippedItems;
|
||||
},
|
||||
gearItemsByType () {
|
||||
const searchText = this.searchTextThrottled;
|
||||
|
||||
@@ -147,6 +147,7 @@
|
||||
right: 100%;
|
||||
height: calc(100% + 18px);
|
||||
margin-top: -9px;
|
||||
margin-right: 1px;
|
||||
padding-top: 9px;
|
||||
padding-bottom: 24px;
|
||||
padding-right: 16px;
|
||||
|
||||
@@ -211,7 +211,7 @@ export default {
|
||||
// @TODO: {keyboard:false, backdrop:'static'}
|
||||
} else if (after <= 30 && !this.user.flags.warnedLowHealth) {
|
||||
this.$root.$emit('bv::show::modal', 'low-health');
|
||||
// @TODO: {keyboard:false, backdrop:'static', controller:'UserCtrl', track:'Health Warning'}
|
||||
// @TODO: {keyboard:false, backdrop:'static', controller:'UserCtrl'}
|
||||
}
|
||||
if (after === before) return;
|
||||
if (this.user.stats.lvl === 0) return;
|
||||
|
||||
@@ -85,6 +85,12 @@ export default {
|
||||
showApiToken: false,
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
window.addEventListener('message', this.receiveMessage, false);
|
||||
},
|
||||
destroy () {
|
||||
window.removeEventListener('message', this.receiveMessage);
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data', credentials: 'credentials'}),
|
||||
apiToken () {
|
||||
@@ -92,6 +98,15 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
receiveMessage (eventFrom) {
|
||||
if (eventFrom.origin !== 'https://www.spritely.app') return;
|
||||
|
||||
const creds = {
|
||||
userId: this.user._id,
|
||||
apiToken: this.credentials.API_TOKEN,
|
||||
};
|
||||
eventFrom.source.postMessage(creds, eventFrom.origin);
|
||||
},
|
||||
async addWebhook (url) {
|
||||
let webhookInfo = {
|
||||
id: uuid(),
|
||||
|
||||
@@ -1,31 +1,19 @@
|
||||
<template lang="pug">
|
||||
b-modal#delete(:title="$t('deleteAccount')", :hide-footer='true' size='md')
|
||||
.regular-delete(v-if='user.auth.local.email')
|
||||
strong {{ $t('deleteLocalAccountText') }}
|
||||
br
|
||||
.row
|
||||
.col-6
|
||||
input.form-control(type='password', v-model='password')
|
||||
br
|
||||
.row
|
||||
#feedback.col-12.form-group
|
||||
label(for='feedbackTextArea') {{ $t('feedback') }}
|
||||
textarea#feedbackTextArea.form-control(v-model='feedback')
|
||||
.modal-footer
|
||||
button.btn.btn-primary(@click='close()') {{ $t('neverMind') }}
|
||||
button.btn.btn-danger(@click='deleteAccount()', :disabled='!password') {{ $t('deleteDo') }}
|
||||
.modal-header
|
||||
.social-delete(v-if='!user.auth.local.email')
|
||||
h4 {{ $t('deleteAccount') }}
|
||||
.modal-body
|
||||
p {{ $t('deleteSocialAccountText', {magicWord: 'DELETE'}) }}
|
||||
br
|
||||
.row
|
||||
.col-md-6
|
||||
input.form-control(type='text', v-model='password')
|
||||
.modal-footer
|
||||
button.btn.btn-secondary(@click='close()') {{ $t('neverMind') }}
|
||||
button.btn.btn-danger(:disabled='!password', @click='deleteAccount()') {{ $t('deleteDo') }}
|
||||
.modal-body
|
||||
br
|
||||
strong(v-if='user.auth.local.email') {{ $t('deleteLocalAccountText') }}
|
||||
strong(v-if='!user.auth.local.email') {{ $t('deleteSocialAccountText', {magicWord: 'DELETE'}) }}
|
||||
.row.mt-3
|
||||
.col-6
|
||||
input.form-control(type='password', v-model='password')
|
||||
.row.mt-3
|
||||
#feedback.col-12.form-group
|
||||
label(for='feedbackTextArea') {{ $t('feedback') }}
|
||||
textarea#feedbackTextArea.form-control(v-model='feedback')
|
||||
.modal-footer
|
||||
button.btn.btn-primary(@click='close()') {{ $t('neverMind') }}
|
||||
button.btn.btn-danger(@click='deleteAccount()', :disabled='!password') {{ $t('deleteDo') }}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -44,7 +32,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'reset');
|
||||
this.$root.$emit('bv::hide::modal', 'delete');
|
||||
},
|
||||
async deleteAccount () {
|
||||
await axios.delete('/api/v4/user', {
|
||||
@@ -55,7 +43,7 @@ export default {
|
||||
});
|
||||
localStorage.clear();
|
||||
window.location.href = '/static/home';
|
||||
this.$root.$emit('bv::hide::modal', 'reset');
|
||||
this.$root.$emit('bv::hide::modal', 'delete');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
)
|
||||
.standard-page
|
||||
div.featuredItems
|
||||
.background
|
||||
.background(:class="{'background-closed': closed, 'background-open': !closed }")
|
||||
div.npc(:class="{'closed': closed }")
|
||||
div.featured-label
|
||||
span.rectangle
|
||||
@@ -34,8 +34,6 @@
|
||||
span.text(v-once) {{ $t('timeTravelersPopoverNoSubMobile') }}
|
||||
span.rectangle
|
||||
|
||||
h1.mb-4.page-header(v-once) {{ $t('timeTravelers') }}
|
||||
|
||||
.clearfix(v-if="!closed")
|
||||
div.float-right
|
||||
span.dropdown-label {{ $t('sortBy') }}
|
||||
@@ -164,12 +162,9 @@
|
||||
height: 216px;
|
||||
|
||||
.background {
|
||||
background: url('~assets/images/npc/#{$npc_timetravelers_flavor}/time_travelers_background.png');
|
||||
|
||||
background-repeat: repeat-x;
|
||||
|
||||
width: 100%;
|
||||
height: 216px;
|
||||
position: absolute;
|
||||
|
||||
top: 0;
|
||||
@@ -180,6 +175,14 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.background-open {
|
||||
background: url('~assets/images/npc/#{$npc_timetravelers_flavor}/time_travelers_background.png');
|
||||
height: 188px;
|
||||
}
|
||||
.background-closed {
|
||||
background: url('~assets/images/npc/normal/time_travelers_background.png');
|
||||
height: 216px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
|
||||
@@ -50,6 +50,10 @@ transition(name="fade")
|
||||
|
||||
.error {
|
||||
background-color: #f74e52;
|
||||
border-radius: 60px;
|
||||
width: 320px !important;
|
||||
padding: 10px 5px;
|
||||
margin-left: 0;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,36 @@
|
||||
<template lang="pug">
|
||||
.container-fluid
|
||||
.container-fluid(role="tablist")
|
||||
.row
|
||||
.col-12.col-md-6.offset-md-3
|
||||
h1 {{ $t('frequentlyAskedQuestions') }}
|
||||
.faq-question(v-for='(heading, index) in headings')
|
||||
h2.accordion(@click='setActivePage(heading)') {{ $t(`faqQuestion${index}`) }}
|
||||
div(v-if='pageState[heading]', v-markdown="$t('webFaqAnswer' + index, replacements)")
|
||||
h1#faq-heading {{ $t('frequentlyAskedQuestions') }}
|
||||
.faq-question(v-for='(heading, index) in headings', :key="index")
|
||||
h2(role="tab", v-b-toggle="heading", @click="handleClick($event)", variant="info") {{ $t(`faqQuestion${index}`) }}
|
||||
b-collapse(:id="heading", :visible="isVisible(heading)", accordion="faq", role="tabpanel")
|
||||
div.card-body(v-markdown="$t('webFaqAnswer' + index, replacements)")
|
||||
hr
|
||||
p(v-markdown="$t('webFaqStillNeedHelp')")
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.faq-question {
|
||||
margin-bottom: 1em;
|
||||
.card-body {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.faq-question h2 {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.faq-question .card-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.static-wrapper .faq-question h2 {
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
|
||||
.faq-question a {
|
||||
text-decoration: none;
|
||||
color: #4F2A93;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
@@ -25,6 +43,7 @@
|
||||
<script>
|
||||
// @TODO: env.EMAILS.TECH_ASSISTANCE_EMAIL
|
||||
const TECH_ASSISTANCE_EMAIL = 'admin@habitica.com';
|
||||
|
||||
import markdownDirective from 'client/directives/markdown';
|
||||
|
||||
export default {
|
||||
@@ -48,19 +67,15 @@
|
||||
'world-boss',
|
||||
];
|
||||
|
||||
let pageState = {};
|
||||
for (let index in headings) {
|
||||
let heading = headings[index];
|
||||
pageState[heading] = false;
|
||||
}
|
||||
const hash = window.location.hash.replace('#', '');
|
||||
|
||||
return {
|
||||
pageState,
|
||||
headings,
|
||||
replacements: {
|
||||
techAssistanceEmail: TECH_ASSISTANCE_EMAIL,
|
||||
wikiTechAssistanceEmail: `mailto:${TECH_ASSISTANCE_EMAIL}`,
|
||||
},
|
||||
visible: hash && headings.includes(hash) ? hash : null,
|
||||
// @TODO webFaqStillNeedHelp: {
|
||||
// linkStart: '[',
|
||||
// linkEnd: '](/groups/guild/5481ccf3-5d2d-48a9-a871-70a7380cee5a)',
|
||||
@@ -69,8 +84,13 @@
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
setActivePage (page) {
|
||||
this.pageState[page] = !this.pageState[page];
|
||||
isVisible (heading) {
|
||||
return this.visible && this.visible === heading;
|
||||
},
|
||||
handleClick (e) {
|
||||
if (!e) return;
|
||||
const heading = e.target.nextElementSibling.id;
|
||||
history.pushState({}, heading, `#${heading}`);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
.fast-company.svg-icon(v-html='icons.fastCompany')
|
||||
.discover.svg-icon(v-html='icons.discover')
|
||||
.container-fluid
|
||||
.seamless_stars_varied_opacity_repeat
|
||||
.row.seamless_stars_varied_opacity_repeat
|
||||
</template>
|
||||
|
||||
<style lang='scss'>
|
||||
@@ -616,11 +616,6 @@
|
||||
},
|
||||
// @TODO this is totally duplicate from the registerLogin component
|
||||
async register () {
|
||||
if (this.password !== this.passwordConfirm) {
|
||||
alert('Passwords must match');
|
||||
return;
|
||||
}
|
||||
|
||||
let groupInvite = '';
|
||||
if (this.$route.query && this.$route.query.p) {
|
||||
groupInvite = this.$route.query.p;
|
||||
@@ -663,10 +658,11 @@
|
||||
await hello(network).logout();
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
const redirectUrl = `${window.location.protocol}//${window.location.host}`;
|
||||
const auth = await hello(network).login({
|
||||
scope: 'email',
|
||||
// explicitly pass the redirect url or it might redirect to /home
|
||||
redirect_uri: '', // eslint-disable-line camelcase
|
||||
redirect_uri: redirectUrl, // eslint-disable-line camelcase
|
||||
});
|
||||
|
||||
await this.$store.dispatch('auth:socialAuth', {
|
||||
|
||||
@@ -67,7 +67,7 @@ export default {
|
||||
} else if (assignedUsersLength > 1 && !this.userIsAssigned) {
|
||||
return this.$t('assignedToMembers', {userCount: assignedUsersLength});
|
||||
} else if (assignedUsersLength > 1 && this.userIsAssigned) {
|
||||
return this.$t('assignedToYouAndMembers', {userCount: assignedUsersLength});
|
||||
return this.$t('assignedToYouAndMembers', {userCount: assignedUsersLength - 1});
|
||||
} else if (this.userIsAssigned) {
|
||||
return this.$t('youAreAssigned');
|
||||
} else if (assignedUsersLength === 0) {
|
||||
|
||||
@@ -27,9 +27,9 @@ export default {
|
||||
let userIsRequesting = this.task.group.approvals && this.task.group.approvals.indexOf(this.user._id) !== -1;
|
||||
|
||||
if (approvalsLength === 1 && !userIsRequesting) {
|
||||
return this.$t('youAreRequestingApproval', {userName: approvals[0].userId.profile.name});
|
||||
return this.$t('userRequestsApproval', {userName: approvals[0].userId.profile.name});
|
||||
} else if (approvalsLength > 1 && !userIsRequesting) {
|
||||
return this.$t('youAreRequestingApproval', {userCount: approvalsLength});
|
||||
return this.$t('userCountRequestsApproval', {userCount: approvalsLength});
|
||||
} else if (approvalsLength === 1 && userIsRequesting) {
|
||||
return this.$t('youAreRequestingApproval');
|
||||
}
|
||||
|
||||
@@ -81,6 +81,10 @@
|
||||
min-height: 556px;
|
||||
}
|
||||
|
||||
.sortable-tasks {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.sortable-tasks + .reward-items {
|
||||
margin-top: 16px;
|
||||
}
|
||||
@@ -446,7 +450,7 @@ export default {
|
||||
|
||||
if (this.type !== 'todo') return;
|
||||
this.$root.$on('habitica::resync-requested', () => {
|
||||
if (this.activeFilters.todo.label !== 'complete2') return;
|
||||
if (this.activeFilter.label !== 'complete2') return;
|
||||
this.loadCompletedTodos(true);
|
||||
});
|
||||
},
|
||||
@@ -544,6 +548,7 @@ export default {
|
||||
this.quickAddText = '';
|
||||
this.quickAddRows = 1;
|
||||
this.createTask(tasks);
|
||||
this.$refs.quickAdd.blur();
|
||||
},
|
||||
editTask (task) {
|
||||
this.$emit('editTask', task);
|
||||
|
||||
@@ -190,7 +190,7 @@ export default {
|
||||
mounted () {
|
||||
// @TODO: should we abstract the drawer state/local store to a library and mixing combo? We use a similar pattern in equipment
|
||||
const spellDrawerState = getLocalSetting(CONSTANTS.keyConstants.SPELL_DRAWER_STATE);
|
||||
if (spellDrawerState === CONSTANTS.valueConstants.DRAWER_CLOSED) {
|
||||
if (spellDrawerState === CONSTANTS.drawerStateValues.DRAWER_CLOSED) {
|
||||
this.$store.state.spellOptions.spellDrawOpen = false;
|
||||
}
|
||||
},
|
||||
@@ -205,11 +205,11 @@ export default {
|
||||
this.$store.state.spellOptions.spellDrawOpen = newState;
|
||||
|
||||
if (newState) {
|
||||
setLocalSetting(CONSTANTS.keyConstants.SPELL_DRAWER_STATE, CONSTANTS.valueConstants.DRAWER_OPEN);
|
||||
setLocalSetting(CONSTANTS.keyConstants.SPELL_DRAWER_STATE, CONSTANTS.drawerStateValues.DRAWER_OPEN);
|
||||
return;
|
||||
}
|
||||
|
||||
setLocalSetting(CONSTANTS.keyConstants.SPELL_DRAWER_STATE, CONSTANTS.valueConstants.DRAWER_CLOSED);
|
||||
setLocalSetting(CONSTANTS.keyConstants.SPELL_DRAWER_STATE, CONSTANTS.drawerStateValues.DRAWER_CLOSED);
|
||||
},
|
||||
spellDisabled (skill) {
|
||||
if (skill === 'frost' && this.user.stats.buffs.streaks) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template lang="pug">
|
||||
form(v-if="task", @submit.stop.prevent="submit()")
|
||||
b-modal#task-modal(size="sm", @hidden="onClose()", @shown="focusInput()")
|
||||
b-modal#task-modal(size="sm", @hidden="onClose()", @show="handleOpen()", @shown="focusInput()")
|
||||
.task-modal-header(slot="modal-header", :class="cssClass('bg')")
|
||||
.clearfix
|
||||
h1.float-left {{ title }}
|
||||
@@ -159,11 +159,11 @@
|
||||
.option.group-options(v-if='groupId')
|
||||
.form-group.row
|
||||
label.col-12(v-once) {{ $t('assignedTo') }}
|
||||
.col-12
|
||||
.col-12.mt-2
|
||||
.category-wrap(@click="showAssignedSelect = !showAssignedSelect")
|
||||
span.category-select(v-if='assignedMembers && assignedMembers.length === 0') {{$t('none')}}
|
||||
span.category-select(v-else)
|
||||
span(v-for='memberId in assignedMembers') {{memberNamesById[memberId]}}
|
||||
span.mr-1(v-for='memberId in assignedMembers') {{memberNamesById[memberId]}}
|
||||
.category-box(v-if="showAssignedSelect")
|
||||
.container
|
||||
.row
|
||||
@@ -176,7 +176,7 @@
|
||||
label.custom-control-label(v-once, :for="`assigned-${member._id}`") {{ member.profile.name }}
|
||||
|
||||
.row
|
||||
button.btn.btn-primary(@click="showAssignedSelect = !showAssignedSelect") {{$t('close')}}
|
||||
button.btn.btn-primary(@click.stop.prevent="showAssignedSelect = !showAssignedSelect") {{$t('close')}}
|
||||
|
||||
.option.group-options(v-if='groupId')
|
||||
.form-group
|
||||
@@ -705,26 +705,8 @@ export default {
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
async task () {
|
||||
if (this.groupId && this.task.group && this.task.group.approval && this.task.group.approval.required) {
|
||||
this.requiresApproval = true;
|
||||
}
|
||||
|
||||
if (this.groupId) {
|
||||
let members = await this.$store.dispatch('members:getGroupMembers', {
|
||||
groupId: this.groupId,
|
||||
includeAllPublicFields: true,
|
||||
});
|
||||
this.members = members;
|
||||
this.members.forEach(member => {
|
||||
this.memberNamesById[member._id] = member.profile.name;
|
||||
});
|
||||
this.assignedMembers = [];
|
||||
if (this.task.group && this.task.group.assignedUsers) this.assignedMembers = this.task.group.assignedUsers;
|
||||
}
|
||||
|
||||
// @TODO: This whole component is mutating a prop and that causes issues. We need to not copy the prop similar to group modals
|
||||
if (this.task) this.checklist = clone(this.task.checklist);
|
||||
task () {
|
||||
this.syncTask();
|
||||
},
|
||||
'task.startDate' () {
|
||||
this.calculateMonthlyRepeatDays();
|
||||
@@ -813,6 +795,30 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
...mapActions({saveTask: 'tasks:save', destroyTask: 'tasks:destroy', createTask: 'tasks:create'}),
|
||||
async syncTask () {
|
||||
if (this.groupId && this.task.group && this.task.group.approval) {
|
||||
this.requiresApproval = this.task.group.approval.required;
|
||||
}
|
||||
|
||||
if (this.groupId) {
|
||||
let members = await this.$store.dispatch('members:getGroupMembers', {
|
||||
groupId: this.groupId,
|
||||
includeAllPublicFields: true,
|
||||
});
|
||||
this.members = members;
|
||||
this.members.forEach(member => {
|
||||
this.memberNamesById[member._id] = member.profile.name;
|
||||
});
|
||||
this.assignedMembers = [];
|
||||
if (this.task.group && this.task.group.assignedUsers) this.assignedMembers = this.task.group.assignedUsers;
|
||||
}
|
||||
|
||||
// @TODO: This whole component is mutating a prop and that causes issues. We need to not copy the prop similar to group modals
|
||||
if (this.task) this.checklist = clone(this.task.checklist);
|
||||
},
|
||||
async handleOpen () {
|
||||
this.syncTask();
|
||||
},
|
||||
cssClass (suffix) {
|
||||
return this.getTaskClasses(this.task, `${this.purpose === 'edit' ? 'edit' : 'create'}-modal-${suffix}`);
|
||||
},
|
||||
@@ -886,6 +892,12 @@ export default {
|
||||
async submit () {
|
||||
if (this.newChecklistItem) this.addChecklistItem();
|
||||
|
||||
if (this.groupId) {
|
||||
this.task.group.assignedUsers = this.assignedMembers;
|
||||
this.task.requiresApproval = this.requiresApproval;
|
||||
this.task.group.approval.required = this.requiresApproval;
|
||||
}
|
||||
|
||||
if (this.purpose === 'create') {
|
||||
if (this.challengeId) {
|
||||
this.$store.dispatch('tasks:createChallengeTasks', {
|
||||
@@ -906,19 +918,11 @@ export default {
|
||||
});
|
||||
});
|
||||
Promise.all(promises);
|
||||
|
||||
this.task.group.assignedUsers = this.assignedMembers;
|
||||
|
||||
this.$emit('taskCreated', this.task);
|
||||
} else {
|
||||
this.createTask(this.task);
|
||||
}
|
||||
} else {
|
||||
if (this.groupId) {
|
||||
this.task.group.assignedUsers = this.assignedMembers;
|
||||
this.task.requiresApproval = this.requiresApproval;
|
||||
}
|
||||
|
||||
this.saveTask(this.task);
|
||||
this.$emit('taskEdited', this.task);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
// @TODO: Implement new message header here when we fix the above
|
||||
|
||||
.new-message-row(v-if='selectedConversation.key && !user.flags.chatRevoked')
|
||||
textarea(v-model='newMessage')
|
||||
textarea(v-model='newMessage', @keyup.ctrl.enter='sendPrivateMessage()')
|
||||
button.btn.btn-secondary(@click='sendPrivateMessage()') Send
|
||||
</template>
|
||||
|
||||
@@ -337,12 +337,12 @@ export default {
|
||||
optTextSet () {
|
||||
if (!this.user.inbox.optOut) {
|
||||
return {
|
||||
switchDescription: this.$t('PMDisable'),
|
||||
switchDescription: this.$t('PMReceive'),
|
||||
popoverText: this.$t('PMEnabledOptPopoverText'),
|
||||
};
|
||||
}
|
||||
return {
|
||||
switchDescription: this.$t('PMEnable'),
|
||||
switchDescription: this.$t('PMReceive'),
|
||||
popoverText: this.$t('PMDisabledOptPopoverText'),
|
||||
};
|
||||
},
|
||||
|
||||
@@ -3,11 +3,16 @@ const CONSTANTS = {
|
||||
keyConstants: {
|
||||
SPELL_DRAWER_STATE: 'spell-drawer-state',
|
||||
EQUIPMENT_DRAWER_STATE: 'equipment-drawer-state',
|
||||
CURRENT_EQUIPMENT_DRAWER_TAB: 'current-equipment-drawer-tab',
|
||||
},
|
||||
valueConstants: {
|
||||
drawerStateValues: {
|
||||
DRAWER_CLOSED: 'drawer-closed',
|
||||
DRAWER_OPEN: 'drawer-open',
|
||||
},
|
||||
equipmentDrawerTabValues: {
|
||||
COSTUME_TAB: 'costume-tab',
|
||||
EQUIPMENT_TAB: 'equipment-tab',
|
||||
},
|
||||
};
|
||||
|
||||
function setLocalSetting (key, value) {
|
||||
|
||||
@@ -27,6 +27,7 @@ const PrivacyPage = () => import(/* webpackChunkName: "static" */'./components/s
|
||||
const TermsPage = () => import(/* webpackChunkName: "static" */'./components/static/terms');
|
||||
|
||||
const RegisterLoginReset = () => import(/* webpackChunkName: "auth" */'./components/auth/registerLoginReset');
|
||||
const Logout = () => import(/* webpackChunkName: "auth" */'./components/auth/logout');
|
||||
|
||||
// User Pages
|
||||
// const StatsPage = () => import(/* webpackChunkName: "user" */'./components/userMenu/stats');
|
||||
@@ -105,6 +106,7 @@ const router = new VueRouter({
|
||||
routes: [
|
||||
{ name: 'register', path: '/register', component: RegisterLoginReset, meta: {requiresLogin: false} },
|
||||
{ name: 'login', path: '/login', component: RegisterLoginReset, meta: {requiresLogin: false} },
|
||||
{ name: 'logout', path: '/logout', component: Logout },
|
||||
{ name: 'resetPassword', path: '/reset-password', component: RegisterLoginReset, meta: {requiresLogin: false} },
|
||||
{ name: 'tasks', path: '/', component: UserTasks },
|
||||
{
|
||||
@@ -297,7 +299,7 @@ router.beforeEach(function routerGuard (to, from, next) {
|
||||
name: redirectTo,
|
||||
query: redirectTo === 'login' ? {
|
||||
redirectTo: to.path,
|
||||
} : null,
|
||||
} : to.query,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||