Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4d006e5cd | ||
|
|
801b53857f | ||
|
|
f8571ec5d5 | ||
|
|
78ba596504 | ||
|
|
fe9521a63f | ||
|
|
c4348d8e47 | ||
|
|
9b0ee6a726 | ||
|
|
c23fad3077 | ||
|
|
6850a1fcb4 | ||
|
|
bc477455bb | ||
|
|
88c56c9877 | ||
|
|
a4ee3aaa7e | ||
|
|
052c653cd3 | ||
|
|
3aa7b4b631 | ||
|
|
045378b820 | ||
|
|
dd29c60d87 | ||
|
|
78fd79931e | ||
|
|
19ba1290f6 | ||
|
|
c509c8e04f | ||
|
|
00d4393024 | ||
|
|
c502b1997b | ||
|
|
4435862ff2 | ||
|
|
e901850a6f | ||
|
|
48bbc22fb4 | ||
|
|
c1e5d8b573 | ||
|
|
6951b79b95 | ||
|
|
1c3a12f37d | ||
|
|
3c71748a1b | ||
|
|
8e56222fc7 | ||
|
|
6bc6c09c75 | ||
|
|
0cbf9354cc | ||
|
|
06c53677f6 | ||
|
|
aa91c5dbae | ||
|
|
5b7c7b77c8 | ||
|
|
d3d221dccb | ||
|
|
53c610236b |
36
Dockerfile
@@ -1,18 +1,18 @@
|
||||
FROM node:boron
|
||||
|
||||
# Install global packages
|
||||
RUN npm install -g gulp grunt-cli bower 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 npm install
|
||||
RUN bower install --allow-root
|
||||
|
||||
# Create Build dir
|
||||
RUN mkdir -p ./website/build
|
||||
|
||||
# Start Habitica
|
||||
EXPOSE 3000
|
||||
CMD ["npm", "start"]
|
||||
FROM node:boron
|
||||
|
||||
# Install global packages
|
||||
RUN npm install -g gulp grunt-cli bower 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 npm install
|
||||
RUN bower install --allow-root
|
||||
|
||||
# Create Build dir
|
||||
RUN mkdir -p ./website/build
|
||||
|
||||
# Start Habitica
|
||||
EXPOSE 3000
|
||||
CMD ["npm", "start"]
|
||||
|
||||
@@ -6,13 +6,14 @@ RUN npm install -g gulp grunt-cli bower mocha
|
||||
# Clone Habitica repo and install dependencies
|
||||
RUN mkdir -p /usr/src/habitrpg
|
||||
WORKDIR /usr/src/habitrpg
|
||||
RUN git clone --branch v3.99.0 https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
|
||||
RUN git clone --branch v3.102.2 https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
|
||||
RUN npm install
|
||||
RUN bower install --allow-root
|
||||
RUN gulp build:prod --force
|
||||
|
||||
# Create Build dir
|
||||
RUN mkdir -p ./website/build
|
||||
|
||||
# Start Habitica
|
||||
EXPOSE 3000
|
||||
CMD ["npm", "start"]
|
||||
CMD ["node", "./website/transpiled-babel/index.js"]
|
||||
@@ -66,7 +66,8 @@
|
||||
},
|
||||
"mode":"sandbox",
|
||||
"client_id":"client_id",
|
||||
"client_secret":"client_secret"
|
||||
"client_secret":"client_secret",
|
||||
"experience_profile_id": ""
|
||||
},
|
||||
"IAP_GOOGLE_KEYDIR": "/path/to/google/public/key/dir/",
|
||||
"LOGGLY_TOKEN": "token",
|
||||
|
||||
60
kubernetes/README.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Habitica in Kubernetes
|
||||
This is a set of sample Kubernetes configuration files to launch Habitica under AWS, both as a single-node web frontend as well as a multi-node web frontend.
|
||||
|
||||
## Prerequisites
|
||||
* An AWS account.
|
||||
* A working Kubernetes installation.
|
||||
* A basic understanding of how to use Kubernetes. https://kubernetes.io/
|
||||
* A persistent volume for MongoDB data.
|
||||
* Docker images of Habitica.
|
||||
+ You can use your own, or use the one included in the YAML files.
|
||||
+ If you use your own, you'll need a fork of the Habitica GitHub repo and your own Docker Hub repo, both of which are free.
|
||||
|
||||
## Before you begin
|
||||
1. Set up Kubernetes.
|
||||
2. Create an EBS volume for MongoDB data.
|
||||
+ Make a note of the name, you'll need it later.
|
||||
|
||||
## Starting MongoDB
|
||||
1. Edit mongo.yaml
|
||||
+ Find the volumeID line.
|
||||
+ Change the volume to the one created in the section above.
|
||||
2. Run the following commands:
|
||||
+ `kubectl.sh create -f mongo.yaml`
|
||||
+ `kubectl.sh create -f mongo-service.yaml`
|
||||
3. Wait for the MongoDB pod to start up.
|
||||
|
||||
## Starting a Single Web Frontend
|
||||
|
||||
1. Run the following commands:
|
||||
+ `kubectl.sh create -f habitica.yaml`
|
||||
+ `kubectl.sh create -f habitica-service.yaml`
|
||||
2. Wait for the frontend to start up.
|
||||
|
||||
## Starting Multi-node Web Frontend
|
||||
1. Run the following commands :
|
||||
+ `kubectl.sh create -f habitica-rc.yaml`
|
||||
+ `kubectl.sh create -f habitica-service.yaml`
|
||||
2. Wait for the frontend to start up.
|
||||
|
||||
## Accessing Your Habitica web interface
|
||||
Using `kubectl describe svc habiticaweb` get the hostname generated for the Habitica service. Open a browser and go to http://hostname:3000 to access the web front-end for the installations above.
|
||||
|
||||
## Shutting down
|
||||
Shutting down is basically done by reversing the steps above:
|
||||
+ `kubectl.sh delete -f habitica-service.yaml`
|
||||
+ `kubectl.sh delete -f habitica.yaml (or habitica-rc.yaml)`
|
||||
+ `kubectl.sh delete -f mongo-service.yaml`
|
||||
+ `kubectl.sh delete -f mongo.yaml`
|
||||
|
||||
You can also just shut down all of Kubernetes as well.
|
||||
|
||||
## Notes
|
||||
+ MongoDB data will be persistent! If you need to start with a fresh database, you'll need to remove the volume and re-create it.
|
||||
+ On AWS, you probably want to use at least t2.medium minion nodes for Kubernetes. The default t2.small is too small for more than two Habitica nodes.
|
||||
|
||||
## Future Plans
|
||||
+ Multi-node MongoDB.
|
||||
+ Monitoring
|
||||
+ Instructions for a better hostname. The default generated ones stink.
|
||||
+ More to come....
|
||||
25
kubernetes/habitica-rc.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
apiVersion: v1
|
||||
kind: ReplicationController
|
||||
metadata:
|
||||
name: habitica
|
||||
labels:
|
||||
name: habitica
|
||||
spec:
|
||||
replicas: 4
|
||||
selector:
|
||||
name: habitica
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
name: habitica
|
||||
spec:
|
||||
containers:
|
||||
- name: habitica
|
||||
image: ksonney/habitrpg:latest
|
||||
env:
|
||||
- name: NODE_DB_URI
|
||||
value: mongodb://mongosvc/habitrpg
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
hostPort: 3000
|
||||
name: habitica
|
||||
14
kubernetes/habitica-service.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
name: habiticaweb
|
||||
name: habiticaweb
|
||||
spec:
|
||||
ports:
|
||||
# the port that this service should serve on
|
||||
- port: 3000
|
||||
# label keys and values that must match in order to receive traffic for this service
|
||||
selector:
|
||||
name: habitica
|
||||
type: LoadBalancer
|
||||
22
kubernetes/habitica.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: habitica
|
||||
labels:
|
||||
name: habitica
|
||||
spec:
|
||||
containers:
|
||||
# - image: mongo:latest
|
||||
# name: mongo
|
||||
# ports:
|
||||
# - containerPort: 27017
|
||||
# name: mongo
|
||||
- image: ksonney/habitrpg:latest
|
||||
name: habitica
|
||||
env:
|
||||
- name: NODE_DB_URI
|
||||
value: mongodb://mongosvc/habitrpg
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
hostPort: 3000
|
||||
name: habitica
|
||||
13
kubernetes/mongo-service.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
name: mongosvc
|
||||
name: mongosvc
|
||||
spec:
|
||||
ports:
|
||||
# the port that this service should serve on
|
||||
- port: 27017
|
||||
# label keys and values that must match in order to receive traffic for this service
|
||||
selector:
|
||||
name: mongodb
|
||||
28
kubernetes/mongo.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: mongodb
|
||||
labels:
|
||||
name: mongodb
|
||||
spec:
|
||||
containers:
|
||||
- resources:
|
||||
limits :
|
||||
cpu: 0.5
|
||||
image: mongo
|
||||
name: mongodb
|
||||
ports:
|
||||
- containerPort: 27017
|
||||
hostPort: 27017
|
||||
name: mongo
|
||||
volumeMounts:
|
||||
# # name must match the volume name below
|
||||
- name: mongo-persistent-storage
|
||||
# # mount path within the container
|
||||
mountPath: /data/db
|
||||
volumes:
|
||||
- name: mongo-persistent-storage
|
||||
awsElasticBlockStore:
|
||||
volumeID: aws://YOUR-REGION/YOUR-VOLNAME
|
||||
fsType: ext3
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "3.102.1",
|
||||
"version": "3.103.0",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@slack/client": "^3.8.1",
|
||||
@@ -31,7 +31,7 @@
|
||||
"bluebird": "^3.3.5",
|
||||
"body-parser": "^1.15.0",
|
||||
"bootstrap": "^4.0.0-alpha.6",
|
||||
"bootstrap-vue": "^0.15.8",
|
||||
"bootstrap-vue": "^0.16.1",
|
||||
"bower": "~1.3.12",
|
||||
"browserify": "~12.0.1",
|
||||
"compression": "^1.6.1",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require("babel-register");
|
||||
require("babel-polyfill");
|
||||
// This file is used for creating paypal billing plans. PayPal doesn't have a web interface for setting up recurring
|
||||
// payment plan definitions, instead you have to create it via their REST SDK and keep it updated the same way. So this
|
||||
// file will be used once for initing your billing plan (then you get the resultant plan.id to store in config.json),
|
||||
@@ -7,10 +9,10 @@ var path = require('path');
|
||||
var nconf = require('nconf');
|
||||
var _ = require('lodash');
|
||||
var paypal = require('paypal-rest-sdk');
|
||||
var blocks = require('../../../../common').content.subscriptionBlocks;
|
||||
var blocks = require('../website/common').content.subscriptionBlocks;
|
||||
var live = nconf.get('PAYPAL:mode')=='live';
|
||||
|
||||
nconf.argv().env().file('user', path.join(path.resolve(__dirname, '../../../config.json')));
|
||||
nconf.argv().env().file('user', path.join(path.resolve(__dirname, '../config.json')));
|
||||
|
||||
var OP = 'create'; // list create update remove
|
||||
|
||||
@@ -49,6 +51,8 @@ _.each(blocks, function(block){
|
||||
});
|
||||
})
|
||||
|
||||
// @TODO: Add cli library for this
|
||||
|
||||
switch(OP) {
|
||||
case "list":
|
||||
paypal.billingPlan.list({status: 'ACTIVE'}, function(err, plans){
|
||||
@@ -91,4 +95,17 @@ switch(OP) {
|
||||
});
|
||||
break;
|
||||
case "remove": break;
|
||||
|
||||
case 'create-webprofile':
|
||||
let webexpinfo = {
|
||||
"name": "HabiticaProfile",
|
||||
"input_fields": {
|
||||
"no_shipping": 1,
|
||||
},
|
||||
};
|
||||
|
||||
paypal.webProfile.create(webexpinfo, (error, result) => {
|
||||
console.log(error, result)
|
||||
})
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -114,6 +114,26 @@ describe('POST /challenges/:challengeId/winner/:winnerId', () => {
|
||||
await expect(winningUser.sync()).to.eventually.have.property('balance', oldBalance + challenge.prize / 4);
|
||||
});
|
||||
|
||||
it('doesn\'t gives winner gems if group policy prevents it', async () => {
|
||||
let oldBalance = winningUser.balance;
|
||||
let oldLeaderBalance = (await groupLeader.sync()).balance;
|
||||
|
||||
await winningUser.update({
|
||||
'purchased.plan.customerId': 'group-plan',
|
||||
});
|
||||
await group.update({
|
||||
'leaderOnly.getGems': true,
|
||||
'purchased.plan.customerId': 123,
|
||||
});
|
||||
|
||||
await groupLeader.post(`/challenges/${challenge._id}/selectWinner/${winningUser._id}`);
|
||||
|
||||
await sleep(0.5);
|
||||
|
||||
await expect(winningUser.sync()).to.eventually.have.property('balance', oldBalance);
|
||||
await expect(groupLeader.sync()).to.eventually.have.property('balance', oldLeaderBalance + challenge.prize / 4);
|
||||
});
|
||||
|
||||
it('doesn\'t refund gems to group leader', async () => {
|
||||
let oldBalance = (await groupLeader.sync()).balance;
|
||||
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import {
|
||||
generateUser,
|
||||
generateGroup,
|
||||
generateChallenge,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('POST /tasks/unlink-all/:challengeId', () => {
|
||||
let user;
|
||||
let guild;
|
||||
let challenge;
|
||||
let tasksToTest = {
|
||||
habit: {
|
||||
text: 'test habit',
|
||||
type: 'habit',
|
||||
up: false,
|
||||
down: true,
|
||||
},
|
||||
todo: {
|
||||
text: 'test todo',
|
||||
type: 'todo',
|
||||
},
|
||||
daily: {
|
||||
text: 'test daily',
|
||||
type: 'daily',
|
||||
frequency: 'daily',
|
||||
everyX: 5,
|
||||
startDate: new Date(),
|
||||
},
|
||||
reward: {
|
||||
text: 'test reward',
|
||||
type: 'reward',
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
guild = await generateGroup(user);
|
||||
challenge = await generateChallenge(user, guild);
|
||||
});
|
||||
|
||||
it('fails if no keep query', async () => {
|
||||
await expect(user.post(`/tasks/unlink-all/${challenge._id}`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('fails if invalid challenge id', async () => {
|
||||
await expect(user.post('/tasks/unlink-all/123?keep=remove-all'))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on an unbroken challenge', async () => {
|
||||
await user.post(`/tasks/challenge/${challenge._id}`, tasksToTest.daily);
|
||||
await expect(user.post(`/tasks/unlink-all/${challenge._id}?keep=remove-all`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('cantOnlyUnlinkChalTask'),
|
||||
});
|
||||
});
|
||||
|
||||
it('unlinks all tasks from a challenge and deletes them on keep=remove-all', async () => {
|
||||
const daily = await user.post(`/tasks/challenge/${challenge._id}`, tasksToTest.daily);
|
||||
await user.post(`/tasks/challenge/${challenge._id}`, tasksToTest.habit);
|
||||
await user.post(`/tasks/challenge/${challenge._id}`, tasksToTest.reward);
|
||||
await user.post(`/tasks/challenge/${challenge._id}`, tasksToTest.todo);
|
||||
await user.del(`/challenges/${challenge._id}`);
|
||||
const response = await user.post(`/tasks/unlink-all/${challenge._id}?keep=remove-all`);
|
||||
expect(response).to.eql({});
|
||||
|
||||
await expect(user.get(`/tasks/${daily._id}`)).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('taskNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
it('unlinks a task from a challenge on keep=keep-all', async () => {
|
||||
const daily = await user.post(`/tasks/challenge/${challenge._id}`, tasksToTest.daily);
|
||||
const anotherUser = await generateUser();
|
||||
await user.post(`/groups/${guild._id}/invite`, {
|
||||
uuids: [anotherUser._id],
|
||||
});
|
||||
// Have the second user join the group and challenge
|
||||
await anotherUser.post(`/groups/${guild._id}/join`);
|
||||
await anotherUser.post(`/challenges/${challenge._id}/join`);
|
||||
// Have the leader delete the challenge and unlink the tasks
|
||||
await user.del(`/challenges/${challenge._id}`);
|
||||
await user.post(`/tasks/unlink-all/${challenge._id}?keep=keep-all`);
|
||||
// Get the second task for the second user
|
||||
const [, anotherUserTask] = await anotherUser.get('/tasks/user');
|
||||
// Expect the second user to still have the task, but unlinked
|
||||
expect(anotherUserTask.challenge).to.eql({
|
||||
taskId: daily._id,
|
||||
id: challenge._id,
|
||||
shortName: challenge.shortName,
|
||||
broken: 'CHALLENGE_DELETED',
|
||||
winner: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,110 @@
|
||||
import {
|
||||
generateUser,
|
||||
generateGroup,
|
||||
generateChallenge,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
describe('POST /tasks/unlink-one/:taskId', () => {
|
||||
let user;
|
||||
let guild;
|
||||
let challenge;
|
||||
let tasksToTest = {
|
||||
habit: {
|
||||
text: 'test habit',
|
||||
type: 'habit',
|
||||
up: false,
|
||||
down: true,
|
||||
},
|
||||
todo: {
|
||||
text: 'test todo',
|
||||
type: 'todo',
|
||||
},
|
||||
daily: {
|
||||
text: 'test daily',
|
||||
type: 'daily',
|
||||
frequency: 'daily',
|
||||
everyX: 5,
|
||||
startDate: new Date(),
|
||||
},
|
||||
reward: {
|
||||
text: 'test reward',
|
||||
type: 'reward',
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
guild = await generateGroup(user);
|
||||
challenge = await generateChallenge(user, guild);
|
||||
});
|
||||
|
||||
it('fails if no keep query', async () => {
|
||||
const daily = await user.post(`/tasks/challenge/${challenge._id}`, tasksToTest.daily);
|
||||
await expect(user.post(`/tasks/unlink-one/${daily._id}`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('fails if invalid task id', async () => {
|
||||
await expect(user.post('/tasks/unlink-one/123?keep=remove'))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on task not found', async () => {
|
||||
await expect(user.post(`/tasks/unlink-one/${generateUUID()}?keep=keep`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('taskNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on task unlinked to challenge', async () => {
|
||||
let daily = await user.post('/tasks/user', tasksToTest.daily);
|
||||
await expect(user.post(`/tasks/unlink-one/${daily._id}?keep=keep`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('cantOnlyUnlinkChalTask'),
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on unbroken challenge', async () => {
|
||||
await user.post(`/tasks/challenge/${challenge._id}`, tasksToTest.daily);
|
||||
let [daily] = await user.get('/tasks/user');
|
||||
await expect(user.post(`/tasks/unlink-one/${daily._id}?keep=keep`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('cantOnlyUnlinkChalTask'),
|
||||
});
|
||||
});
|
||||
|
||||
it('unlinks a task from a challenge and saves it on keep=keep', async () => {
|
||||
await user.post(`/tasks/challenge/${challenge._id}`, tasksToTest.daily);
|
||||
let [, daily] = await user.get('/tasks/user');
|
||||
await user.del(`/challenges/${challenge._id}`);
|
||||
await user.post(`/tasks/unlink-one/${daily._id}?keep=keep`);
|
||||
[, daily] = await user.get('/tasks/user');
|
||||
expect(daily.challenge).to.eql({});
|
||||
});
|
||||
|
||||
it('unlinks a task from a challenge and deletes it on keep=remove', async () => {
|
||||
await user.post(`/tasks/challenge/${challenge._id}`, tasksToTest.daily);
|
||||
let [, daily] = await user.get('/tasks/user');
|
||||
await user.del(`/challenges/${challenge._id}`);
|
||||
await user.post(`/tasks/unlink-one/${daily._id}?keep=remove`);
|
||||
const tasks = await user.get('/tasks/user');
|
||||
// Only the default task should remain
|
||||
expect(tasks.length).to.eql(1);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
generateUser,
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
@@ -31,4 +32,70 @@ describe('POST /user/purchase/:type/:key', () => {
|
||||
|
||||
expect(user.items[type][key]).to.equal(1);
|
||||
});
|
||||
|
||||
it('can convert gold to gems if subscribed', async () => {
|
||||
let oldBalance = user.balance;
|
||||
await user.update({
|
||||
'purchased.plan.customerId': 'group-plan',
|
||||
'stats.gp': 1000,
|
||||
});
|
||||
await user.post('/user/purchase/gems/gem');
|
||||
await user.sync();
|
||||
expect(user.balance).to.equal(oldBalance + 0.25);
|
||||
});
|
||||
|
||||
it('leader can convert gold to gems even if the group plan prevents it', async () => {
|
||||
let { group, groupLeader } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'test',
|
||||
type: 'guild',
|
||||
privacy: 'private',
|
||||
},
|
||||
});
|
||||
await group.update({
|
||||
'leaderOnly.getGems': true,
|
||||
'purchased.plan.customerId': 123,
|
||||
});
|
||||
await groupLeader.sync();
|
||||
let oldBalance = groupLeader.balance;
|
||||
|
||||
await groupLeader.update({
|
||||
'purchased.plan.customerId': 'group-plan',
|
||||
'stats.gp': 1000,
|
||||
});
|
||||
await groupLeader.post('/user/purchase/gems/gem');
|
||||
|
||||
await groupLeader.sync();
|
||||
expect(groupLeader.balance).to.equal(oldBalance + 0.25);
|
||||
});
|
||||
|
||||
it('cannot convert gold to gems if the group plan prevents it', async () => {
|
||||
let { group, members } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'test',
|
||||
type: 'guild',
|
||||
privacy: 'private',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
await group.update({
|
||||
'leaderOnly.getGems': true,
|
||||
'purchased.plan.customerId': 123,
|
||||
});
|
||||
let oldBalance = members[0].balance;
|
||||
|
||||
await members[0].update({
|
||||
'purchased.plan.customerId': 'group-plan',
|
||||
'stats.gp': 1000,
|
||||
});
|
||||
await expect(members[0].post('/user/purchase/gems/gem'))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('groupPolicyCannotGetGems'),
|
||||
});
|
||||
|
||||
await members[0].sync();
|
||||
expect(members[0].balance).to.equal(oldBalance);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -102,6 +102,7 @@ describe('Amazon Payments', () => {
|
||||
});
|
||||
|
||||
it('should purchase gems', async () => {
|
||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(true);
|
||||
await amzLib.checkout({user, orderReferenceId, headers});
|
||||
|
||||
expect(paymentBuyGemsStub).to.be.calledOnce;
|
||||
@@ -111,22 +112,52 @@ describe('Amazon Payments', () => {
|
||||
headers,
|
||||
});
|
||||
expectAmazonStubs();
|
||||
expect(user.canGetGems).to.be.calledOnce;
|
||||
user.canGetGems.restore();
|
||||
});
|
||||
|
||||
it('should gift gems', async () => {
|
||||
it('should error if gem amount is too low', async () => {
|
||||
let receivingUser = new User();
|
||||
receivingUser.save();
|
||||
let gift = {
|
||||
type: 'gems',
|
||||
gems: {
|
||||
amount: 16,
|
||||
amount: 0,
|
||||
uuid: receivingUser._id,
|
||||
},
|
||||
};
|
||||
|
||||
await expect(amzLib.checkout({gift, user, orderReferenceId, headers}))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 400,
|
||||
message: 'Amount must be at least 1.',
|
||||
name: 'BadRequest',
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if user cannot get gems gems', async () => {
|
||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(false);
|
||||
await expect(amzLib.checkout({user, orderReferenceId, headers})).to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
message: i18n.t('groupPolicyCannotGetGems'),
|
||||
name: 'NotAuthorized',
|
||||
});
|
||||
user.canGetGems.restore();
|
||||
});
|
||||
|
||||
it('should gift gems', async () => {
|
||||
let receivingUser = new User();
|
||||
await receivingUser.save();
|
||||
let gift = {
|
||||
type: 'gems',
|
||||
uuid: receivingUser._id,
|
||||
gems: {
|
||||
amount: 16,
|
||||
},
|
||||
};
|
||||
amount = 16 / 4;
|
||||
await amzLib.checkout({gift, user, orderReferenceId, headers});
|
||||
|
||||
gift.member = receivingUser;
|
||||
expect(paymentBuyGemsStub).to.be.calledOnce;
|
||||
expect(paymentBuyGemsStub).to.be.calledWith({
|
||||
user,
|
||||
|
||||
@@ -57,7 +57,20 @@ describe('Apple Payments', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('errors if the user cannot purchase gems', async () => {
|
||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(false);
|
||||
await expect(applePayments.verifyGemPurchase(user, receipt, headers))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
message: i18n.t('groupPolicyCannotGetGems'),
|
||||
});
|
||||
|
||||
user.canGetGems.restore();
|
||||
});
|
||||
|
||||
it('purchases gems', async () => {
|
||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(true);
|
||||
await applePayments.verifyGemPurchase(user, receipt, headers);
|
||||
|
||||
expect(iapSetupStub).to.be.calledOnce;
|
||||
@@ -74,6 +87,8 @@ describe('Apple Payments', () => {
|
||||
amount: 5.25,
|
||||
headers,
|
||||
});
|
||||
expect(user.canGetGems).to.be.calledOnce;
|
||||
user.canGetGems.restore();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -63,7 +63,21 @@ describe('Google Payments', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error if user cannot purchase gems', async () => {
|
||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(false);
|
||||
|
||||
await expect(googlePayments.verifyGemPurchase(user, receipt, signature, headers))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
message: i18n.t('groupPolicyCannotGetGems'),
|
||||
});
|
||||
|
||||
user.canGetGems.restore();
|
||||
});
|
||||
|
||||
it('purchases gems', async () => {
|
||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(true);
|
||||
await googlePayments.verifyGemPurchase(user, receipt, signature, headers);
|
||||
|
||||
expect(iapSetupStub).to.be.calledOnce;
|
||||
@@ -82,6 +96,8 @@ describe('Google Payments', () => {
|
||||
amount: 5.25,
|
||||
headers,
|
||||
});
|
||||
expect(user.canGetGems).to.be.calledOnce;
|
||||
user.canGetGems.restore();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -61,23 +61,54 @@ describe('Paypal Payments', () => {
|
||||
});
|
||||
|
||||
it('creates a link for gem purchases', async () => {
|
||||
let link = await paypalPayments.checkout();
|
||||
let link = await paypalPayments.checkout({user: new User()});
|
||||
|
||||
expect(paypalPaymentCreateStub).to.be.calledOnce;
|
||||
expect(paypalPaymentCreateStub).to.be.calledWith(getPaypalCreateOptions('Habitica Gems', 5.00));
|
||||
expect(link).to.eql(approvalHerf);
|
||||
});
|
||||
|
||||
it('creates a link for gifting gems', async () => {
|
||||
it('should error if gem amount is too low', async () => {
|
||||
let receivingUser = new User();
|
||||
receivingUser.save();
|
||||
let gift = {
|
||||
type: 'gems',
|
||||
gems: {
|
||||
amount: 16,
|
||||
amount: 0,
|
||||
uuid: receivingUser._id,
|
||||
},
|
||||
};
|
||||
|
||||
await expect(paypalPayments.checkout({gift}))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 400,
|
||||
message: 'Amount must be at least 1.',
|
||||
name: 'BadRequest',
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if the user cannot get gems', async () => {
|
||||
let user = new User();
|
||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(false);
|
||||
|
||||
await expect(paypalPayments.checkout({user})).to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
message: i18n.t('groupPolicyCannotGetGems'),
|
||||
name: 'NotAuthorized',
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a link for gifting gems', async () => {
|
||||
let receivingUser = new User();
|
||||
await receivingUser.save();
|
||||
let gift = {
|
||||
type: 'gems',
|
||||
uuid: receivingUser._id,
|
||||
gems: {
|
||||
amount: 16,
|
||||
},
|
||||
};
|
||||
|
||||
let link = await paypalPayments.checkout({gift});
|
||||
|
||||
expect(paypalPaymentCreateStub).to.be.calledOnce;
|
||||
|
||||
@@ -48,7 +48,57 @@ describe('Stripe Payments', () => {
|
||||
payments.createSubscription.restore();
|
||||
});
|
||||
|
||||
it('should error if gem amount is too low', async () => {
|
||||
let receivingUser = new User();
|
||||
receivingUser.save();
|
||||
gift = {
|
||||
type: 'gems',
|
||||
gems: {
|
||||
amount: 0,
|
||||
uuid: receivingUser._id,
|
||||
},
|
||||
};
|
||||
|
||||
await expect(stripePayments.checkout({
|
||||
token,
|
||||
user,
|
||||
gift,
|
||||
groupId,
|
||||
email,
|
||||
headers,
|
||||
coupon,
|
||||
}, stripe))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 400,
|
||||
message: 'Amount must be at least 1.',
|
||||
name: 'BadRequest',
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should error if user cannot get gems', async () => {
|
||||
gift = undefined;
|
||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(false);
|
||||
|
||||
await expect(stripePayments.checkout({
|
||||
token,
|
||||
user,
|
||||
gift,
|
||||
groupId,
|
||||
email,
|
||||
headers,
|
||||
coupon,
|
||||
}, stripe)).to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
message: i18n.t('groupPolicyCannotGetGems'),
|
||||
name: 'NotAuthorized',
|
||||
});
|
||||
});
|
||||
|
||||
it('should purchase gems', async () => {
|
||||
gift = undefined;
|
||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(true);
|
||||
|
||||
await stripePayments.checkout({
|
||||
token,
|
||||
user,
|
||||
@@ -73,16 +123,18 @@ describe('Stripe Payments', () => {
|
||||
paymentMethod: 'Stripe',
|
||||
gift,
|
||||
});
|
||||
expect(user.canGetGems).to.be.calledOnce;
|
||||
user.canGetGems.restore();
|
||||
});
|
||||
|
||||
it('should gift gems', async () => {
|
||||
let receivingUser = new User();
|
||||
receivingUser.save();
|
||||
await receivingUser.save();
|
||||
gift = {
|
||||
type: 'gems',
|
||||
uuid: receivingUser._id,
|
||||
gems: {
|
||||
amount: 16,
|
||||
uuid: receivingUser._id,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -96,7 +148,6 @@ describe('Stripe Payments', () => {
|
||||
coupon,
|
||||
}, stripe);
|
||||
|
||||
gift.member = receivingUser;
|
||||
expect(stripeChargeStub).to.be.calledOnce;
|
||||
expect(stripeChargeStub).to.be.calledWith({
|
||||
amount: '400',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Bluebird from 'bluebird';
|
||||
import moment from 'moment';
|
||||
import { model as User } from '../../../../../website/server/models/user';
|
||||
import { model as Group } from '../../../../../website/server/models/group';
|
||||
import common from '../../../../../website/common';
|
||||
|
||||
describe('User Model', () => {
|
||||
@@ -179,6 +180,75 @@ describe('User Model', () => {
|
||||
});
|
||||
});
|
||||
|
||||
context('canGetGems', () => {
|
||||
let user;
|
||||
let group;
|
||||
beforeEach(() => {
|
||||
user = new User();
|
||||
let leader = new User();
|
||||
group = new Group({
|
||||
name: 'test',
|
||||
type: 'guild',
|
||||
privacy: 'private',
|
||||
leader: leader._id,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns true if user is not subscribed', async () => {
|
||||
expect(await user.canGetGems()).to.equal(true);
|
||||
});
|
||||
|
||||
it('returns true if user is not subscribed with a group plan', async () => {
|
||||
user.purchased.plan.customerId = 123;
|
||||
expect(await user.canGetGems()).to.equal(true);
|
||||
});
|
||||
|
||||
it('returns true if user is subscribed with a group plan', async () => {
|
||||
user.purchased.plan.customerId = 'group-plan';
|
||||
expect(await user.canGetGems()).to.equal(true);
|
||||
});
|
||||
|
||||
it('returns true if user is part of a group', async () => {
|
||||
user.guilds.push(group._id);
|
||||
expect(await user.canGetGems()).to.equal(true);
|
||||
});
|
||||
|
||||
it('returns true if user is part of a group with a subscription', async () => {
|
||||
user.guilds.push(group._id);
|
||||
user.purchased.plan.customerId = 'group-plan';
|
||||
group.purchased.plan.customerId = 123;
|
||||
await group.save();
|
||||
expect(await user.canGetGems()).to.equal(true);
|
||||
});
|
||||
|
||||
it('returns true if leader is part of a group with a subscription and canGetGems: false', async () => {
|
||||
user.guilds.push(group._id);
|
||||
user.purchased.plan.customerId = 'group-plan';
|
||||
group.purchased.plan.customerId = 123;
|
||||
group.leader = user._id;
|
||||
group.leaderOnly.getGems = true;
|
||||
await group.save();
|
||||
expect(await user.canGetGems()).to.equal(true);
|
||||
});
|
||||
|
||||
it('returns true if user is part of a group with no subscription but canGetGems: false', async () => {
|
||||
user.guilds.push(group._id);
|
||||
user.purchased.plan.customerId = 'group-plan';
|
||||
group.leaderOnly.getGems = true;
|
||||
await group.save();
|
||||
expect(await user.canGetGems()).to.equal(true);
|
||||
});
|
||||
|
||||
it('returns false if user is part of a group with a subscription and canGetGems: false', async () => {
|
||||
user.guilds.push(group._id);
|
||||
user.purchased.plan.customerId = 'group-plan';
|
||||
group.purchased.plan.customerId = 123;
|
||||
group.leaderOnly.getGems = true;
|
||||
await group.save();
|
||||
expect(await user.canGetGems()).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
context('hasNotCancelled', () => {
|
||||
let user;
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -6,6 +6,7 @@ describe('Notification Controller', function() {
|
||||
beforeEach(function() {
|
||||
user = specHelper.newUser();
|
||||
user._id = "unique-user-id";
|
||||
user.needsCron = false;
|
||||
|
||||
var userSync = sinon.stub().returns({
|
||||
then: function then (f) { f(); }
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
.promo_android {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1748px -176px;
|
||||
background-position: -1748px 0px;
|
||||
width: 175px;
|
||||
height: 175px;
|
||||
}
|
||||
.promo_aquatic_potions {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -447px -148px;
|
||||
background-position: -589px -148px;
|
||||
width: 141px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201602 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1606px 0px;
|
||||
background-position: -1606px -295px;
|
||||
width: 141px;
|
||||
height: 294px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201603 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1606px -295px;
|
||||
background-position: -1606px -590px;
|
||||
width: 141px;
|
||||
height: 294px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201604 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: 0px -894px;
|
||||
background-position: -1325px -441px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201605 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -705px -894px;
|
||||
background-position: -987px -894px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
@@ -60,25 +60,25 @@
|
||||
}
|
||||
.promo_backgrounds_armoire_201610 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -141px -894px;
|
||||
background-position: -282px -894px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201611 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -564px -894px;
|
||||
background-position: -141px -894px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201612 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -423px -894px;
|
||||
background-position: 0px -894px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201701 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -846px -894px;
|
||||
background-position: -705px -894px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
@@ -102,31 +102,31 @@
|
||||
}
|
||||
.promo_backgrounds_armoire_201705 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1183px -442px;
|
||||
background-position: -447px -148px;
|
||||
width: 141px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201706 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -899px -442px;
|
||||
background-position: -1183px -442px;
|
||||
width: 141px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201707 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -899px 0px;
|
||||
background-position: -899px -442px;
|
||||
width: 141px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_bees {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -757px -442px;
|
||||
background-position: -899px 0px;
|
||||
width: 141px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_bundle_feathered {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -140px -305px;
|
||||
background-position: -757px -442px;
|
||||
width: 141px;
|
||||
height: 441px;
|
||||
}
|
||||
@@ -150,7 +150,7 @@
|
||||
}
|
||||
.promo_checkin_incentives {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1606px -590px;
|
||||
background-position: -1606px 0px;
|
||||
width: 141px;
|
||||
height: 294px;
|
||||
}
|
||||
@@ -162,7 +162,7 @@
|
||||
}
|
||||
.promo_classes_fall_2015 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -558px -1336px;
|
||||
background-position: -823px -1336px;
|
||||
width: 377px;
|
||||
height: 99px;
|
||||
}
|
||||
@@ -228,7 +228,7 @@
|
||||
}
|
||||
.promo_cow {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -282px -894px;
|
||||
background-position: -564px -894px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
@@ -252,7 +252,7 @@
|
||||
}
|
||||
.promo_enchanted_armoire {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -936px -1336px;
|
||||
background-position: -1201px -1336px;
|
||||
width: 374px;
|
||||
height: 76px;
|
||||
}
|
||||
@@ -270,7 +270,7 @@
|
||||
}
|
||||
.promo_enchanted_armoire_201509 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1543px -1484px;
|
||||
background-position: -1088px -1484px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -282,13 +282,13 @@
|
||||
}
|
||||
.promo_enchanted_armoire_201601 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1634px -1484px;
|
||||
background-position: -906px -1484px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.promo_fairy_potions {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -589px -148px;
|
||||
background-position: -140px -305px;
|
||||
width: 141px;
|
||||
height: 441px;
|
||||
}
|
||||
@@ -300,13 +300,13 @@
|
||||
}
|
||||
.promo_ghost_potions {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -987px -894px;
|
||||
background-position: -846px -894px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_habitica {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1748px 0px;
|
||||
background-position: -1748px -176px;
|
||||
width: 175px;
|
||||
height: 175px;
|
||||
}
|
||||
@@ -318,7 +318,7 @@
|
||||
}
|
||||
.promo_habitoween_2016 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1325px -441px;
|
||||
background-position: -423px -894px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
@@ -346,6 +346,12 @@
|
||||
width: 276px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_king_manta {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -558px -1336px;
|
||||
width: 264px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_more_checkin_incentives {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -306px 0px;
|
||||
@@ -354,7 +360,7 @@
|
||||
}
|
||||
.promo_mystery_201405 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -906px -1484px;
|
||||
background-position: -1543px -1484px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -378,7 +384,7 @@
|
||||
}
|
||||
.promo_mystery_201409 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -341px -1585px;
|
||||
background-position: -1634px -1484px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -390,7 +396,7 @@
|
||||
}
|
||||
.promo_mystery_201411 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -815px -1484px;
|
||||
background-position: -614px -1585px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -408,7 +414,7 @@
|
||||
}
|
||||
.promo_mystery_201502 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1088px -1484px;
|
||||
background-position: -815px -1484px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -438,13 +444,13 @@
|
||||
}
|
||||
.promo_mystery_201507 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1467px -879px;
|
||||
background-position: -1467px -773px;
|
||||
width: 90px;
|
||||
height: 105px;
|
||||
}
|
||||
.promo_mystery_201508 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1467px -1167px;
|
||||
background-position: -140px -747px;
|
||||
width: 93px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -456,7 +462,7 @@
|
||||
}
|
||||
.promo_mystery_201510 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -140px -747px;
|
||||
background-position: -721px -1484px;
|
||||
width: 93px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -504,13 +510,13 @@
|
||||
}
|
||||
.promo_mystery_201606 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1467px -773px;
|
||||
background-position: -1467px -879px;
|
||||
width: 90px;
|
||||
height: 105px;
|
||||
}
|
||||
.promo_mystery_201607 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -614px -1585px;
|
||||
background-position: -341px -1585px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -522,7 +528,7 @@
|
||||
}
|
||||
.promo_mystery_201609 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -721px -1484px;
|
||||
background-position: -1467px -1167px;
|
||||
width: 93px;
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 936 KiB After Width: | Height: | Size: 928 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
@@ -646,7 +646,7 @@ form
|
||||
// Task filters
|
||||
// --------
|
||||
.task-filter
|
||||
margin: 1.5em 0 1em 0
|
||||
margin: 1em 0 1em 0
|
||||
hrpg-button-bar-mixin($color-tasks)
|
||||
@extend $hrpg-button-bar
|
||||
width: 100%
|
||||
|
||||
@@ -146,6 +146,13 @@ window.habitrpg = angular.module('habitrpg',
|
||||
title: env.t('groupPlansTitle')
|
||||
})
|
||||
|
||||
.state('options.social.newGroup', {
|
||||
url: '/new-group',
|
||||
templateUrl: "partials/options.social.newGroup.html",
|
||||
controller: 'NewGroupCtrl',
|
||||
title: env.t('newGroupTitle')
|
||||
})
|
||||
|
||||
.state('options.social.guilds', {
|
||||
url: '/guilds',
|
||||
templateUrl: "partials/options.social.guilds.html",
|
||||
|
||||
@@ -9,7 +9,7 @@ habitrpg.controller('GroupApprovalsCtrl', ['$scope', 'Tasks',
|
||||
};
|
||||
|
||||
$scope.approvalTitle = function (approval) {
|
||||
return env.t('approvalTitle', {text: approval.text, userName: approval.userId.profile.name});
|
||||
return env.t('approvalTitle', {type: approval.type, text: approval.text, userName: approval.userId.profile.name});
|
||||
};
|
||||
|
||||
$scope.refreshApprovals = function () {
|
||||
|
||||
@@ -9,35 +9,11 @@ angular.module('habitrpg')
|
||||
function($scope, $window, Groups, Payments) {
|
||||
$scope.PAGES = {
|
||||
BENEFITS: 'benefits',
|
||||
CREATE_GROUP: 'create-group',
|
||||
UPGRADE_GROUP: 'upgrade-group',
|
||||
};
|
||||
$scope.activePage = $scope.PAGES.BENEFITS;
|
||||
$scope.newGroup = {
|
||||
type: 'guild',
|
||||
privacy: 'private',
|
||||
};
|
||||
$scope.PAYMENTS = {
|
||||
AMAZON: 'amazon',
|
||||
STRIPE: 'stripe',
|
||||
};
|
||||
|
||||
$scope.changePage = function (page) {
|
||||
$scope.activePage = page;
|
||||
$window.scrollTo(0, 0);
|
||||
};
|
||||
|
||||
$scope.newGroupIsReady = function () {
|
||||
return !!$scope.newGroup.name;
|
||||
};
|
||||
|
||||
$scope.createGroup = function () {
|
||||
$scope.changePage($scope.PAGES.UPGRADE_GROUP);
|
||||
};
|
||||
|
||||
$scope.upgradeGroup = function (paymentType) {
|
||||
var subscriptionKey = 'group_monthly'; // @TODO: Get from content API?
|
||||
if (paymentType === $scope.PAYMENTS.STRIPE) Payments.showStripe({subscription: subscriptionKey, coupon: null, groupToCreate: $scope.newGroup});
|
||||
if (paymentType === $scope.PAYMENTS.AMAZON) Payments.amazonPayments.init({type: 'subscription', subscription: subscriptionKey, coupon: null, groupToCreate: $scope.newGroup});
|
||||
};
|
||||
}]);
|
||||
|
||||
53
website/client-old/js/controllers/newGroupCtrl.js
Normal file
@@ -0,0 +1,53 @@
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
A controller to manage the New Group page
|
||||
*/
|
||||
|
||||
angular.module('habitrpg')
|
||||
.controller("NewGroupCtrl", ['$scope', '$window', 'Groups', 'Payments',
|
||||
function ($scope, $window, Groups, Payments) {
|
||||
$scope.PAGES = {
|
||||
CREATE_GROUP: 'create-group',
|
||||
UPGRADE_GROUP: 'upgrade-group',
|
||||
};
|
||||
$scope.activePage = $scope.PAGES.CREATE_GROUP;
|
||||
$scope.changePage = function (page) {
|
||||
$scope.activePage = page;
|
||||
$window.scrollTo(0, 0);
|
||||
};
|
||||
|
||||
$scope.newGroup = {
|
||||
type: 'guild',
|
||||
privacy: 'private',
|
||||
};
|
||||
$scope.PAYMENTS = {
|
||||
AMAZON: 'amazon',
|
||||
STRIPE: 'stripe',
|
||||
};
|
||||
|
||||
$scope.newGroupIsReady = function () {
|
||||
return !!$scope.newGroup.name;
|
||||
};
|
||||
|
||||
$scope.createGroup = function () {
|
||||
$scope.changePage($scope.PAGES.UPGRADE_GROUP);
|
||||
};
|
||||
|
||||
$scope.upgradeGroup = function (paymentType) {
|
||||
var subscriptionKey = 'group_monthly'; // @TODO: Get from content API?
|
||||
if (paymentType === $scope.PAYMENTS.STRIPE) Payments.showStripe({
|
||||
subscription: subscriptionKey,
|
||||
coupon: null,
|
||||
groupToCreate: $scope.newGroup
|
||||
});
|
||||
if (paymentType === $scope.PAYMENTS.AMAZON) Payments.amazonPayments.init({
|
||||
type: 'subscription',
|
||||
subscription: subscriptionKey,
|
||||
coupon: null,
|
||||
groupToCreate: $scope.newGroup
|
||||
});
|
||||
};
|
||||
}]);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
habitrpg.controller('NotificationCtrl',
|
||||
['$scope', '$rootScope', 'Shared', 'Content', 'User', 'Guide', 'Notification', 'Analytics', 'Achievement', 'Social', 'Tasks',
|
||||
function ($scope, $rootScope, Shared, Content, User, Guide, Notification, Analytics, Achievement, Social, Tasks) {
|
||||
['$scope', '$rootScope', 'Shared', 'Content', 'User', 'Guide', 'Notification', 'Analytics', 'Achievement', 'Social', 'Tasks', '$modal',
|
||||
function ($scope, $rootScope, Shared, Content, User, Guide, Notification, Analytics, Achievement, Social, Tasks, $modal) {
|
||||
var isRunningYesterdailies = false;
|
||||
|
||||
$rootScope.$watch('user', function (after, before) {
|
||||
@@ -39,6 +39,7 @@ habitrpg.controller('NotificationCtrl',
|
||||
if (yesterDailies.length === 0) {
|
||||
User.runCron().then(function () {
|
||||
isRunningYesterdailies = false;
|
||||
handleUserNotifications(User.user);
|
||||
});
|
||||
return;
|
||||
};
|
||||
@@ -53,7 +54,8 @@ habitrpg.controller('NotificationCtrl',
|
||||
modalScope.processingYesterdailies = true;
|
||||
|
||||
$scope.yesterDailiesModalOpen = true;
|
||||
$rootScope.openModal('yesterDailies', {
|
||||
$modal.open({
|
||||
templateUrl: 'modals/yesterDailies.html',
|
||||
scope: modalScope,
|
||||
backdrop: 'static',
|
||||
controller: ['$scope', 'Tasks', 'User', '$rootScope', function ($scope, Tasks, User, $rootScope) {
|
||||
@@ -72,6 +74,7 @@ habitrpg.controller('NotificationCtrl',
|
||||
User.runCron()
|
||||
.then(function () {
|
||||
isRunningYesterdailies = false;
|
||||
handleUserNotifications(User.user);
|
||||
});
|
||||
};
|
||||
}],
|
||||
@@ -312,13 +315,14 @@ habitrpg.controller('NotificationCtrl',
|
||||
// are now stored in user.notifications.
|
||||
$rootScope.$watchCollection('userNotifications', function (after) {
|
||||
if (!User.user._wrapped) return;
|
||||
if (User.user.needsCron) return;
|
||||
handleUserNotifications(after);
|
||||
});
|
||||
|
||||
var handleUserNotificationsOnFirstSync = _.once(function () {
|
||||
handleUserNotifications($rootScope.userNotifications);
|
||||
});
|
||||
$rootScope.$on('userUpdated', handleUserNotificationsOnFirstSync);
|
||||
// var handleUserNotificationsOnFirstSync = _.once(function () {
|
||||
// handleUserNotifications($rootScope.userNotifications);
|
||||
// });
|
||||
// $rootScope.$on('userUpdated', handleUserNotificationsOnFirstSync);
|
||||
|
||||
// TODO what about this?
|
||||
$rootScope.$watch('user.achievements', function(){
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('habitrpg').factory('Payments',
|
||||
['$rootScope', 'User', '$http', 'Content',
|
||||
function($rootScope, User, $http, Content) {
|
||||
['$rootScope', 'User', '$http', 'Content', 'Notification',
|
||||
function($rootScope, User, $http, Content, Notification) {
|
||||
var Payments = {};
|
||||
var isAmazonReady = false;
|
||||
Payments.amazonButtonEnabled = true;
|
||||
@@ -21,7 +21,18 @@ function($rootScope, User, $http, Content) {
|
||||
amazon.Login.setClientId(window.env.AMAZON_PAYMENTS.CLIENT_ID);
|
||||
};
|
||||
|
||||
Payments.checkGemAmount = function(data) {
|
||||
if (data && data.gift && data.gift.type === "gems" && (!data.gift.gems.amount || data.gift.gems.amount === 0)) {
|
||||
Notification.error(window.env.t('badAmountOfGemsToPurchase'), true);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Payments.showStripe = function(data) {
|
||||
|
||||
if(!Payments.checkGemAmount(data)) return;
|
||||
|
||||
var sub = false;
|
||||
|
||||
if (data.subscription) {
|
||||
@@ -121,6 +132,7 @@ function($rootScope, User, $http, Content) {
|
||||
// Needs to be called everytime the modal/router is accessed
|
||||
Payments.amazonPayments.init = function(data) {
|
||||
if(!isAmazonReady) return;
|
||||
if(!Payments.checkGemAmount(data)) return;
|
||||
if(data.type !== 'single' && data.type !== 'subscription') return;
|
||||
|
||||
if (data.gift) {
|
||||
@@ -345,6 +357,14 @@ function($rootScope, User, $http, Content) {
|
||||
});
|
||||
}
|
||||
|
||||
Payments.payPalPayment = function(data){
|
||||
if(!Payments.checkGemAmount(data)) return;
|
||||
|
||||
var gift = Payments.encodeGift(data.giftedTo, data.gift);
|
||||
var url = '/paypal/checkout?_id=' + User.user._id + '&apiToken=' + User.settings.auth.apiToken + '&gift=' + gift;
|
||||
$http.get(url);
|
||||
}
|
||||
|
||||
Payments.encodeGift = function(uuid, gift) {
|
||||
gift.uuid = uuid;
|
||||
var encodedString = JSON.stringify(gift);
|
||||
|
||||
@@ -357,6 +357,9 @@ angular.module('habitrpg')
|
||||
var numberToShortDay = Shared.DAY_MAPPING;
|
||||
|
||||
function generateSummary(task) {
|
||||
if (task._edit.everyX === 0)
|
||||
return window.env.t('repeatZero');
|
||||
|
||||
var frequencyPlural = frequencyMap[task._edit.frequency];
|
||||
|
||||
var repeatDays = '';
|
||||
@@ -409,8 +412,8 @@ angular.module('habitrpg')
|
||||
|
||||
var dateFormat = 'MM-DD-YYYY';
|
||||
if (user.preferences.dateFormat) dateFormat = user.preferences.dateFormat.toUpperCase();
|
||||
|
||||
var nextDue = nextDueDates.map(function (date) {
|
||||
var nextDueDatesArr = angular.isArray(nextDueDates) ? nextDueDates : [];
|
||||
var nextDue = nextDueDatesArr.map(function (date) {
|
||||
return date.format(dateFormat);
|
||||
});
|
||||
|
||||
|
||||
@@ -423,7 +423,7 @@ angular.module('habitrpg')
|
||||
url: 'api/v3/cron',
|
||||
})
|
||||
.then(function (response) {
|
||||
sync();
|
||||
return sync();
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
@@ -26,11 +26,20 @@ window.habitrpg = angular.module('habitrpg', ['chieffancypants.loadingBar', 'ui.
|
||||
$http.defaults.headers.common['x-client'] = 'habitica-web';
|
||||
}])
|
||||
|
||||
.controller("PlansCtrl", ['$rootScope','Analytics',
|
||||
function($rootScope,Analytics) {
|
||||
.controller("PlansCtrl", ['$rootScope','Analytics','$location','User','$scope',
|
||||
function($rootScope,Analytics,$location,User,$scope) {
|
||||
$rootScope.clickContact = function(){
|
||||
Analytics.track({'hitType':'event','eventCategory':'button','eventAction':'click','eventLabel':'Contact Us (Plans)'})
|
||||
}
|
||||
};
|
||||
$scope.goToNewGroupPage = function () {
|
||||
if (User.authenticated()) {
|
||||
window.location.href="/#/options/groups/new-group";
|
||||
} else {
|
||||
// There is no authenticated user, so redirect to the login page,
|
||||
// taking the hash with it to effectively redirect after login.
|
||||
window.location.href = "/static/login#/options/groups/new-group";
|
||||
}
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@
|
||||
"js/controllers/tasksCtrl.js",
|
||||
"js/controllers/userCtrl.js",
|
||||
"js/controllers/groupPlansCtrl.js",
|
||||
"js/controllers/newGroupCtrl.js",
|
||||
|
||||
"js/components/groupTasks/groupTasksController.js",
|
||||
"js/components/groupTasks/groupTasksDirective.js",
|
||||
|
||||
BIN
website/client/assets/creator/arrow.png
Normal file
|
After Width: | Height: | Size: 194 B |
BIN
website/client/assets/creator/arrow@2x.png
Normal file
|
After Width: | Height: | Size: 330 B |
BIN
website/client/assets/creator/arrow@3x.png
Normal file
|
After Width: | Height: | Size: 582 B |
BIN
website/client/assets/creator/creator-hills-bg.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
website/client/assets/creator/creator-hills-bg@2x.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
website/client/assets/creator/creator-hills-bg@3x.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
website/client/assets/creator/prev.png
Normal file
|
After Width: | Height: | Size: 191 B |
BIN
website/client/assets/creator/prev@2x.png
Normal file
|
After Width: | Height: | Size: 334 B |
BIN
website/client/assets/creator/prev@3x.png
Normal file
|
After Width: | Height: | Size: 614 B |
@@ -1,36 +1,36 @@
|
||||
.promo_android {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1748px -176px;
|
||||
background-position: -1748px 0px;
|
||||
width: 175px;
|
||||
height: 175px;
|
||||
}
|
||||
.promo_aquatic_potions {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -447px -148px;
|
||||
background-position: -589px -148px;
|
||||
width: 141px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201602 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1606px 0px;
|
||||
background-position: -1606px -295px;
|
||||
width: 141px;
|
||||
height: 294px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201603 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1606px -295px;
|
||||
background-position: -1606px -590px;
|
||||
width: 141px;
|
||||
height: 294px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201604 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: 0px -894px;
|
||||
background-position: -1325px -441px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201605 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -705px -894px;
|
||||
background-position: -987px -894px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
@@ -60,25 +60,25 @@
|
||||
}
|
||||
.promo_backgrounds_armoire_201610 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -141px -894px;
|
||||
background-position: -282px -894px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201611 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -564px -894px;
|
||||
background-position: -141px -894px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201612 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -423px -894px;
|
||||
background-position: 0px -894px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201701 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -846px -894px;
|
||||
background-position: -705px -894px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
@@ -102,31 +102,31 @@
|
||||
}
|
||||
.promo_backgrounds_armoire_201705 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1183px -442px;
|
||||
background-position: -447px -148px;
|
||||
width: 141px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201706 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -899px -442px;
|
||||
background-position: -1183px -442px;
|
||||
width: 141px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_backgrounds_armoire_201707 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -899px 0px;
|
||||
background-position: -899px -442px;
|
||||
width: 141px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_bees {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -757px -442px;
|
||||
background-position: -899px 0px;
|
||||
width: 141px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_bundle_feathered {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -140px -305px;
|
||||
background-position: -757px -442px;
|
||||
width: 141px;
|
||||
height: 441px;
|
||||
}
|
||||
@@ -150,7 +150,7 @@
|
||||
}
|
||||
.promo_checkin_incentives {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1606px -590px;
|
||||
background-position: -1606px 0px;
|
||||
width: 141px;
|
||||
height: 294px;
|
||||
}
|
||||
@@ -162,7 +162,7 @@
|
||||
}
|
||||
.promo_classes_fall_2015 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -558px -1336px;
|
||||
background-position: -823px -1336px;
|
||||
width: 377px;
|
||||
height: 99px;
|
||||
}
|
||||
@@ -228,7 +228,7 @@
|
||||
}
|
||||
.promo_cow {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -282px -894px;
|
||||
background-position: -564px -894px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
@@ -252,7 +252,7 @@
|
||||
}
|
||||
.promo_enchanted_armoire {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -936px -1336px;
|
||||
background-position: -1201px -1336px;
|
||||
width: 374px;
|
||||
height: 76px;
|
||||
}
|
||||
@@ -270,7 +270,7 @@
|
||||
}
|
||||
.promo_enchanted_armoire_201509 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1543px -1484px;
|
||||
background-position: -1088px -1484px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -282,13 +282,13 @@
|
||||
}
|
||||
.promo_enchanted_armoire_201601 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1634px -1484px;
|
||||
background-position: -906px -1484px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.promo_fairy_potions {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -589px -148px;
|
||||
background-position: -140px -305px;
|
||||
width: 141px;
|
||||
height: 441px;
|
||||
}
|
||||
@@ -300,13 +300,13 @@
|
||||
}
|
||||
.promo_ghost_potions {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -987px -894px;
|
||||
background-position: -846px -894px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
.promo_habitica {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1748px 0px;
|
||||
background-position: -1748px -176px;
|
||||
width: 175px;
|
||||
height: 175px;
|
||||
}
|
||||
@@ -318,7 +318,7 @@
|
||||
}
|
||||
.promo_habitoween_2016 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1325px -441px;
|
||||
background-position: -423px -894px;
|
||||
width: 140px;
|
||||
height: 441px;
|
||||
}
|
||||
@@ -346,6 +346,12 @@
|
||||
width: 276px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_king_manta {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -558px -1336px;
|
||||
width: 264px;
|
||||
height: 147px;
|
||||
}
|
||||
.promo_more_checkin_incentives {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -306px 0px;
|
||||
@@ -354,7 +360,7 @@
|
||||
}
|
||||
.promo_mystery_201405 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -906px -1484px;
|
||||
background-position: -1543px -1484px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -378,7 +384,7 @@
|
||||
}
|
||||
.promo_mystery_201409 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -341px -1585px;
|
||||
background-position: -1634px -1484px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -390,7 +396,7 @@
|
||||
}
|
||||
.promo_mystery_201411 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -815px -1484px;
|
||||
background-position: -614px -1585px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -408,7 +414,7 @@
|
||||
}
|
||||
.promo_mystery_201502 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1088px -1484px;
|
||||
background-position: -815px -1484px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -438,13 +444,13 @@
|
||||
}
|
||||
.promo_mystery_201507 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1467px -879px;
|
||||
background-position: -1467px -773px;
|
||||
width: 90px;
|
||||
height: 105px;
|
||||
}
|
||||
.promo_mystery_201508 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1467px -1167px;
|
||||
background-position: -140px -747px;
|
||||
width: 93px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -456,7 +462,7 @@
|
||||
}
|
||||
.promo_mystery_201510 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -140px -747px;
|
||||
background-position: -721px -1484px;
|
||||
width: 93px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -504,13 +510,13 @@
|
||||
}
|
||||
.promo_mystery_201606 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -1467px -773px;
|
||||
background-position: -1467px -879px;
|
||||
width: 90px;
|
||||
height: 105px;
|
||||
}
|
||||
.promo_mystery_201607 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -614px -1585px;
|
||||
background-position: -341px -1585px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
@@ -522,7 +528,7 @@
|
||||
}
|
||||
.promo_mystery_201609 {
|
||||
background-image: url(/static/sprites/spritesmith-largeSprites-0.png);
|
||||
background-position: -721px -1484px;
|
||||
background-position: -1467px -1167px;
|
||||
width: 93px;
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
BIN
website/client/assets/images/groups/grassy-meadow-backdrop.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
@@ -14,18 +14,18 @@
|
||||
box-shadow: 0 2px 2px 0 rgba($black, 0.16), 0 1px 4px 0 rgba($black, 0.12);
|
||||
color: $white;
|
||||
|
||||
&:focus {
|
||||
&:focus:not(.btn-flat) {
|
||||
outline: none;
|
||||
border-color: $purple-400;
|
||||
@include btn-focus-hover-shadow();
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:hover:not(.btn-flat) {
|
||||
@include btn-focus-hover-shadow();
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
&:active, &.active {
|
||||
&:active:not(.btn-flat), &.active:not(.btn-flat) {
|
||||
box-shadow: none;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
@@ -124,3 +124,8 @@
|
||||
color: inherit !important;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-flat {
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@@ -38,9 +38,10 @@
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
&:active, &:hover, &:focus, &.active {
|
||||
&:active, &:hover, &.active {
|
||||
background-color: rgba(#d5c8ff, 0.32);
|
||||
color: $purple-200;
|
||||
}
|
||||
|
||||
@@ -12,4 +12,18 @@
|
||||
* {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon-16 {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.icon-10 {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.inline {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@@ -34,10 +34,18 @@
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
&-active {
|
||||
background: $purple-500;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 4px 4px 0 rgba($black, 0.16), 0 1px 8px 0 rgba($black, 0.12);
|
||||
border-color: $purple-500;
|
||||
}
|
||||
|
||||
&.highlight {
|
||||
box-shadow: 0 0 8px 8px rgba($black, 0.16), 0 5px 10px 0 rgba($black, 0.12) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-content .item:hover {
|
||||
|
||||
@@ -12,10 +12,12 @@ html, body {
|
||||
padding: 24px;
|
||||
font-size: 14px;
|
||||
line-height: 1.43;
|
||||
width: 236px;
|
||||
}
|
||||
|
||||
.standard-page {
|
||||
padding: 24px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
|
||||
6
website/client/assets/svg/accessories.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="23" viewBox="0 0 40 23">
|
||||
<g fill="none" fill-rule="evenodd" stroke="#A5A1AC" stroke-width="2.4">
|
||||
<path d="M23.324 10.53h14.621M2.248 10.53h21.946M16.804 15.667s1.501-.742 3.293-.742a7.57 7.57 0 0 1 3.197.742"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M16.838 10.53v3.878c0 3.879-3.22 7.052-7.154 7.052H8.359c-3.497 0-6.36-2.822-6.36-6.269v-4.968l8.289-7.205c2.02-1.756 4.63-1.113 6.482.958M23.26 10.53v3.878c0 3.879 3.219 7.052 7.154 7.052h1.325c3.497 0 6.359-2.822 6.359-6.269v-4.968L29.81 3.018c-2.02-1.756-4.63-1.113-6.482.958"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 644 B |
8
website/client/assets/svg/body.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="34" viewBox="0 0 32 34">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path class='path' stroke="#6133B4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.4" d="M2.124 20.456S2.854 2 15.734 2c12.88 0 13.61 18.456 13.61 18.456"/>
|
||||
<path class='path' stroke="#6133B4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.4" d="M10.29 32.388s-1.725-7.96-1.725-14.681c0-6.722 2.382-14.682 2.382-14.682M21.064 32.388s1.724-7.96 1.724-14.681c0-6.722-2.382-14.682-2.382-14.682"/>
|
||||
<path class='path' stroke="#6133B4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.4" d="M8.957 22.65s3.372-.64 6.933-.64c3.563 0 6.54.64 6.54.64M7.003 32.388h3.29M21.144 32.388h3.289"/>
|
||||
<path fill="#6133B4" d="M3.784 21.533a1.893 1.893 0 1 1-3.785 0 1.893 1.893 0 0 1 3.785 0M31.237 21.533a1.893 1.893 0 1 1-3.786 0 1.893 1.893 0 0 1 3.786 0"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 960 B |
5
website/client/assets/svg/close.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
|
||||
<g fill="none" fill-rule="evenodd" stroke="#A5A1AC" stroke-width="2">
|
||||
<path d="M1 11L11 1M11 11L1 1"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 215 B |
36
website/client/assets/svg/for-css/tutorial-border.svg
Normal file
@@ -0,0 +1,36 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="144" viewBox="0 0 400 144">
|
||||
<defs>
|
||||
<path id="a" d="M0 0h400v144H0z"/>
|
||||
<path id="b" d="M0 0h10v10H0z"/>
|
||||
<path id="c" d="M0 0h10v10H0z"/>
|
||||
<path id="d" d="M0 0h10v10H0z"/>
|
||||
<path id="e" d="M0 0h10v10H0z"/>
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<g>
|
||||
<use fill="#FFF" xlink:href="#a"/>
|
||||
<path stroke="#FFA623" stroke-width="4" d="M2 2h396v140H2z"/>
|
||||
<path stroke="#B36213" stroke-width="2" d="M1 1h398v142H1z"/>
|
||||
</g>
|
||||
<g transform="translate(0 134)">
|
||||
<use fill="#FFBE5D" xlink:href="#b"/>
|
||||
<path stroke="#FFA623" stroke-width="4" d="M2 2h6v6H2z"/>
|
||||
<path stroke="#B36213" stroke-width="2" d="M1 1h8v8H1z"/>
|
||||
</g>
|
||||
<g transform="translate(390 134)">
|
||||
<use fill="#FFBE5D" xlink:href="#c"/>
|
||||
<path stroke="#FFA623" stroke-width="4" d="M2 2h6v6H2z"/>
|
||||
<path stroke="#B36213" stroke-width="2" d="M1 1h8v8H1z"/>
|
||||
</g>
|
||||
<g transform="translate(390)">
|
||||
<use fill="#FFBE5D" xlink:href="#d"/>
|
||||
<path stroke="#FFA623" stroke-width="4" d="M2 2h6v6H2z"/>
|
||||
<path stroke="#B36213" stroke-width="2" d="M1 1h8v8H1z"/>
|
||||
</g>
|
||||
<g>
|
||||
<use fill="#FFBE5D" xlink:href="#e"/>
|
||||
<path stroke="#FFA623" stroke-width="4" d="M2 2h6v6H2z"/>
|
||||
<path stroke="#B36213" stroke-width="2" d="M1 1h8v8H1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
6
website/client/assets/svg/hair.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="35" height="35" viewBox="0 0 35 35">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path fill="#A5A1AC" d="M22.688 0a7.222 7.222 0 0 0-5.12 2.12L2.12 17.569a7.241 7.241 0 0 0 0 10.24l2.538 2.538L30.346 4.66 27.809 2.12A7.222 7.222 0 0 0 22.688 0m0 2.414c1.289 0 2.502.502 3.413 1.414l.832.83L4.659 26.934l-.831-.831a4.793 4.793 0 0 1-1.415-3.414c0-1.29.502-2.501 1.415-3.414L19.275 3.828a4.793 4.793 0 0 1 3.413-1.414"/>
|
||||
<path stroke="#A5A1AC" stroke-width="2.4" d="M4.385 28.385l5.746 5.747M28.36 4.41l5.746 5.746M16.372 16.398l5.746 5.746M7.382 25.389l5.746 5.746M10.379 22.392l5.746 5.746M13.376 19.395l5.746 5.746M19.37 13.4l5.745 5.747M22.366 10.404l5.746 5.746M25.363 7.407l5.746 5.746"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 768 B |
49
website/client/assets/svg/logo-purple.svg
Normal file
@@ -0,0 +1,49 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="191" height="42" viewBox="0 0 191 42">
|
||||
<defs>
|
||||
<path id="a" d="M.666 28.718h22.448V.606H.666v28.112z"/>
|
||||
<path id="c" d="M.558 40.74h24.35V.077H.557z"/>
|
||||
<path id="e" d="M0 40.74h24.03V.077H0V40.74z"/>
|
||||
<path id="g" d="M19.379.077H.279v40.662h19.1z"/>
|
||||
<path id="i" d="M6.59.077H.558v6.01H6.59V.077z"/>
|
||||
<path id="k" d="M6.59.077H.558v6.01H6.59V.077z"/>
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<g transform="translate(138.726 12.613)">
|
||||
<mask id="b" fill="#fff">
|
||||
<use xlink:href="#a"/>
|
||||
</mask>
|
||||
<path fill="#4F2A93" d="M21.94 19.774a3.025 3.025 0 0 0-4.23.545 6.115 6.115 0 0 1-4.87 2.389c-3.249 0-5.934-2.519-6.131-5.743-.007-.465-.012-3.793-.012-4.226 0-3.376 2.756-6.122 6.144-6.122a6.1 6.1 0 0 1 4.456 1.907 3.023 3.023 0 0 0 4.263.11 2.997 2.997 0 0 0 .11-4.249A12.248 12.248 0 0 0 12.84.606C6.128.606.667 6.048.667 12.739c0 .041.001 4.206.018 4.512.35 6.43 5.69 11.467 12.157 11.467a12.11 12.11 0 0 0 9.645-4.728 2.999 2.999 0 0 0-.546-4.216" mask="url(#b)"/>
|
||||
</g>
|
||||
<path fill="#4F2A93" d="M178.606 35.32c-3.249 0-5.933-2.518-6.131-5.74-.007-.467-.012-3.81-.012-4.229 0-3.375 2.756-6.122 6.143-6.122 3.388 0 6.144 2.747 6.144 6.122 0 .434-.005 3.763-.012 4.223-.195 3.226-2.88 5.746-6.132 5.746m9.16-22.621a3.007 3.007 0 0 0-2.911 2.259 12.12 12.12 0 0 0-6.249-1.74c-6.713 0-12.174 5.444-12.174 12.133 0 .043.002 4.205.017 4.513.35 6.43 5.69 11.467 12.157 11.467 2.341 0 4.53-.667 6.392-1.815a3.014 3.014 0 0 0 2.768 1.815 3.01 3.01 0 0 0 3.015-3.006V15.704a3.01 3.01 0 0 0-3.015-3.005M40.6 35.32c-3.25 0-5.934-2.519-6.13-5.742-.008-.464-.013-3.793-.013-4.227 0-3.375 2.756-6.122 6.144-6.122 3.387 0 6.143 2.747 6.143 6.122 0 .434-.005 3.763-.012 4.223-.195 3.226-2.88 5.746-6.131 5.746m9.159-22.621a3.007 3.007 0 0 0-2.91 2.259 12.12 12.12 0 0 0-6.25-1.74c-6.713 0-12.175 5.444-12.175 12.133 0 .043.002 4.206.019 4.513.35 6.43 5.69 11.467 12.157 11.467 2.34 0 4.53-.667 6.392-1.815a3.014 3.014 0 0 0 2.767 1.815 3.01 3.01 0 0 0 3.016-3.006V15.704A3.01 3.01 0 0 0 49.76 12.7M94.159 12.699a3.01 3.01 0 0 0-3.016 3.005v22.621a3.01 3.01 0 0 0 3.016 3.006 3.01 3.01 0 0 0 3.016-3.006v-22.62a3.01 3.01 0 0 0-3.016-3.006M129.678 12.699a3.01 3.01 0 0 0-3.016 3.005v22.621a3.01 3.01 0 0 0 3.016 3.006 3.01 3.01 0 0 0 3.016-3.006v-22.62a3.01 3.01 0 0 0-3.016-3.006"/>
|
||||
<g transform="translate(59.646 .591)">
|
||||
<mask id="d" fill="#fff">
|
||||
<use xlink:href="#c"/>
|
||||
</mask>
|
||||
<path fill="#4F2A93" d="M18.864 28.99c-.198 3.221-2.883 5.739-6.131 5.739-3.25 0-5.935-2.519-6.132-5.746-.008-.464-.012-3.79-.012-4.223 0-3.376 2.756-6.122 6.144-6.122 3.386 0 6.143 2.746 6.143 6.122 0 .419-.005 3.762-.012 4.23m-6.131-16.363c-2.242 0-4.338.618-6.144 1.676V3.083A3.01 3.01 0 0 0 3.574.076 3.01 3.01 0 0 0 .558 3.082v34.653a3.01 3.01 0 0 0 3.016 3.005c1.24 0 2.304-.748 2.767-1.815a12.149 12.149 0 0 0 6.392 1.815c6.466 0 11.806-5.037 12.157-11.478.015-.297.017-4.46.017-4.502 0-6.69-5.462-12.133-12.174-12.133" mask="url(#d)"/>
|
||||
</g>
|
||||
<g transform="translate(0 .591)">
|
||||
<mask id="f" fill="#fff">
|
||||
<use xlink:href="#e"/>
|
||||
</mask>
|
||||
<path fill="#4F2A93" d="M11.855 12.627c-2.08 0-4.07.52-5.823 1.47V3.082A3.01 3.01 0 0 0 3.016.077 3.01 3.01 0 0 0 0 3.082v34.652a3.01 3.01 0 0 0 3.016 3.006 3.01 3.01 0 0 0 3.016-3.006V22.201c.23-.196.442-.416.612-.684a6.12 6.12 0 0 1 5.211-2.879c3.387 0 6.143 2.746 6.143 6.122 0 .55-.005 12.23-.013 12.9a3.01 3.01 0 0 0 3.018 3.08 3.012 3.012 0 0 0 3.01-2.853c.014-.297.016-12.999.016-13.127 0-6.69-5.46-12.133-12.174-12.133" mask="url(#f)"/>
|
||||
</g>
|
||||
<g transform="translate(101.867 .591)">
|
||||
<mask id="h" fill="#fff">
|
||||
<use xlink:href="#g"/>
|
||||
</mask>
|
||||
<path fill="#4F2A93" d="M16.363 12.108h-3.518V3.082A3.01 3.01 0 0 0 9.829.077a3.01 3.01 0 0 0-3.016 3.005v9.026H3.295a3.01 3.01 0 0 0-3.016 3.005 3.01 3.01 0 0 0 3.016 3.005h3.518v19.616a3.01 3.01 0 0 0 3.016 3.005 3.01 3.01 0 0 0 3.016-3.005V18.118h3.518a3.01 3.01 0 0 0 3.016-3.005 3.01 3.01 0 0 0-3.016-3.005" mask="url(#h)"/>
|
||||
</g>
|
||||
<g transform="translate(90.474 .591)">
|
||||
<mask id="j" fill="#fff">
|
||||
<use xlink:href="#i"/>
|
||||
</mask>
|
||||
<path fill="#FF6066" d="M6.59 3.082a3.01 3.01 0 0 1-3.016 3.005A3.01 3.01 0 0 1 .558 3.082 3.01 3.01 0 0 1 3.574.077 3.01 3.01 0 0 1 6.59 3.082" mask="url(#j)"/>
|
||||
</g>
|
||||
<g transform="translate(125.993 .591)">
|
||||
<mask id="l" fill="#fff">
|
||||
<use xlink:href="#k"/>
|
||||
</mask>
|
||||
<path fill="#4FB5E8" d="M6.59 3.082a3.01 3.01 0 0 1-3.016 3.005A3.01 3.01 0 0 1 .558 3.082 3.01 3.01 0 0 1 3.574.077 3.01 3.01 0 0 1 6.59 3.082" mask="url(#l)"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
@@ -1,3 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="12" viewBox="0 0 16 12">
|
||||
<path fill="#4F2A93" fill-rule="evenodd" d="M14 10H2V2l6 5 6-5v8zm0-10H2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z"/>
|
||||
<path fill-rule="evenodd" d="M14 10H2V2l6 5 6-5v8zm0-10H2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 235 B After Width: | Height: | Size: 220 B |
6
website/client/assets/svg/skin.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="37" height="33" viewBox="0 0 37 33">
|
||||
<g fill="none" fill-rule="evenodd" stroke="#A5A1AC" stroke-linejoin="round" stroke-width="2.4">
|
||||
<path stroke-linecap="round" d="M13.782 21.846s1.794 3.682 0 5.454c-1.793 1.773-5.957 0-8.327.938C3.085 29.175 2 31.65 2 31.65M23.744 21.846s-1.794 3.682 0 5.454c1.793 1.773 5.957 0 8.327.938 2.37.937 3.455 3.412 3.455 3.412"/>
|
||||
<path d="M27.939 12.642c0 6.321-5.755 11.445-9.277 11.445-2.98 0-9.277-5.124-9.277-11.445C9.385 6.32 13.089 2 18.662 2c5.572 0 9.277 4.32 9.277 10.642z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 599 B |
@@ -1,54 +1,56 @@
|
||||
<template lang="pug">
|
||||
nav.navbar.navbar-inverse.fixed-top.navbar-toggleable-sm
|
||||
.navbar-header
|
||||
.logo.svg-icon(v-html="icons.logo")
|
||||
.collapse.navbar-collapse
|
||||
ul.navbar-nav.mr-auto
|
||||
router-link.nav-item(tag="li", :to="{name: 'tasks'}", exact)
|
||||
a.nav-link(v-once) {{ $t('tasks') }}
|
||||
router-link.nav-item.dropdown(tag="li", :to="{name: 'items'}", :class="{'active': $route.path.startsWith('/inventory')}")
|
||||
a.nav-link(v-once) {{ $t('inventory') }}
|
||||
.dropdown-menu
|
||||
router-link.dropdown-item(:to="{name: 'items'}", exact) {{ $t('items') }}
|
||||
router-link.dropdown-item(:to="{name: 'equipment'}") {{ $t('equipment') }}
|
||||
router-link.dropdown-item(:to="{name: 'stable'}") {{ $t('stable') }}
|
||||
router-link.nav-item(tag="li", :to="{name: 'shops'}", exact)
|
||||
a.nav-link(v-once) {{ $t('shops') }}
|
||||
router-link.nav-item(tag="li", :to="{name: 'party'}")
|
||||
a.nav-link(v-once) {{ $t('party') }}
|
||||
router-link.nav-item.dropdown(tag="li", :to="{name: 'tavern'}", :class="{'active': $route.path.startsWith('/guilds')}")
|
||||
a.nav-link(v-once) {{ $t('guilds') }}
|
||||
.dropdown-menu
|
||||
router-link.dropdown-item(:to="{name: 'tavern'}") {{ $t('tavern') }}
|
||||
router-link.dropdown-item(:to="{name: 'myGuilds'}") {{ $t('myGuilds') }}
|
||||
router-link.dropdown-item(:to="{name: 'guildsDiscovery'}") {{ $t('guildsDiscovery') }}
|
||||
router-link.nav-item(tag="li", :to="{name: 'challenges'}", exact)
|
||||
a.nav-link(v-once) {{ $t('challenges') }}
|
||||
router-link.nav-item.dropdown(tag="li", to="/help", :class="{'active': $route.path.startsWith('/help')}")
|
||||
a.nav-link(v-once) {{ $t('help') }}
|
||||
.dropdown-menu
|
||||
router-link.dropdown-item(to="/help/faq") {{ $t('faq') }}
|
||||
router-link.dropdown-item(to="/help/report-bug") {{ $t('reportBug') }}
|
||||
router-link.dropdown-item(to="/help/request-feature") {{ $t('requestAF') }}
|
||||
.item-with-icon
|
||||
.svg-icon(v-html="icons.gem")
|
||||
span {{userGems | roundBigNumber}}
|
||||
.item-with-icon
|
||||
.svg-icon(v-html="icons.gold")
|
||||
span {{user.stats.gp | roundBigNumber}}
|
||||
.item-with-icon.item-notifications
|
||||
.svg-icon(v-html="icons.notifications")
|
||||
router-link.dropdown.item-with-icon.item-user(:to="{name: 'avatar'}")
|
||||
.svg-icon(v-html="icons.user")
|
||||
.dropdown-menu.dropdown-menu-right.user-dropdown
|
||||
router-link.dropdown-item.edit-avatar(:to="{name: 'avatar'}")
|
||||
h3 {{ user.profile.name }}
|
||||
span.small-text {{ $t('editAvatar') }}
|
||||
router-link.dropdown-item(:to="{name: 'inbox'}") {{ $t('inbox') }}
|
||||
router-link.dropdown-item(:to="{name: 'stats'}") {{ $t('stats') }}
|
||||
router-link.dropdown-item(:to="{name: 'achievements'}") {{ $t('achievements') }}
|
||||
router-link.dropdown-item(:to="{name: 'settings'}") {{ $t('settings') }}
|
||||
a.nav-link.dropdown-item(to="/", @click.prevent='logout()') {{ $t('logout') }}
|
||||
div
|
||||
inbox-modal
|
||||
nav.navbar.navbar-inverse.fixed-top.navbar-toggleable-sm
|
||||
.navbar-header
|
||||
.logo.svg-icon(v-html="icons.logo")
|
||||
.collapse.navbar-collapse
|
||||
ul.navbar-nav.mr-auto
|
||||
router-link.nav-item(tag="li", :to="{name: 'tasks'}", exact)
|
||||
a.nav-link(v-once) {{ $t('tasks') }}
|
||||
router-link.nav-item.dropdown(tag="li", :to="{name: 'items'}", :class="{'active': $route.path.startsWith('/inventory')}")
|
||||
a.nav-link(v-once) {{ $t('inventory') }}
|
||||
.dropdown-menu
|
||||
router-link.dropdown-item(:to="{name: 'items'}", exact) {{ $t('items') }}
|
||||
router-link.dropdown-item(:to="{name: 'equipment'}") {{ $t('equipment') }}
|
||||
router-link.dropdown-item(:to="{name: 'stable'}") {{ $t('stable') }}
|
||||
router-link.nav-item(tag="li", :to="{name: 'shops'}", exact)
|
||||
a.nav-link(v-once) {{ $t('shops') }}
|
||||
router-link.nav-item(tag="li", :to="{name: 'party'}")
|
||||
a.nav-link(v-once) {{ $t('party') }}
|
||||
router-link.nav-item.dropdown(tag="li", :to="{name: 'tavern'}", :class="{'active': $route.path.startsWith('/guilds')}")
|
||||
a.nav-link(v-once) {{ $t('guilds') }}
|
||||
.dropdown-menu
|
||||
router-link.dropdown-item(:to="{name: 'tavern'}") {{ $t('tavern') }}
|
||||
router-link.dropdown-item(:to="{name: 'myGuilds'}") {{ $t('myGuilds') }}
|
||||
router-link.dropdown-item(:to="{name: 'guildsDiscovery'}") {{ $t('guildsDiscovery') }}
|
||||
router-link.nav-item(tag="li", :to="{name: 'challenges'}", exact)
|
||||
a.nav-link(v-once) {{ $t('challenges') }}
|
||||
router-link.nav-item.dropdown(tag="li", to="/help", :class="{'active': $route.path.startsWith('/help')}")
|
||||
a.nav-link(v-once) {{ $t('help') }}
|
||||
.dropdown-menu
|
||||
router-link.dropdown-item(to="/help/faq") {{ $t('faq') }}
|
||||
router-link.dropdown-item(to="/help/report-bug") {{ $t('reportBug') }}
|
||||
router-link.dropdown-item(to="/help/request-feature") {{ $t('requestAF') }}
|
||||
.item-with-icon
|
||||
.svg-icon(v-html="icons.gem")
|
||||
span {{userGems | roundBigNumber}}
|
||||
.item-with-icon
|
||||
.svg-icon(v-html="icons.gold")
|
||||
span {{user.stats.gp | roundBigNumber}}
|
||||
.item-with-icon.item-notifications
|
||||
.svg-icon(v-html="icons.notifications")
|
||||
router-link.dropdown.item-with-icon.item-user(:to="{name: 'avatar'}")
|
||||
.svg-icon(v-html="icons.user")
|
||||
.dropdown-menu.dropdown-menu-right.user-dropdown
|
||||
router-link.dropdown-item.edit-avatar(:to="{name: 'avatar'}")
|
||||
h3 {{ user.profile.name }}
|
||||
span.small-text {{ $t('editAvatar') }}
|
||||
a.nav-link.dropdown-item(@click.prevent='showInbox()') {{ $t('inbox') }}
|
||||
router-link.dropdown-item(:to="{name: 'stats'}") {{ $t('stats') }}
|
||||
router-link.dropdown-item(:to="{name: 'achievements'}") {{ $t('achievements') }}
|
||||
router-link.dropdown-item(:to="{name: 'settings'}") {{ $t('settings') }}
|
||||
a.nav-link.dropdown-item(to="/", @click.prevent='logout()') {{ $t('logout') }}
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -198,8 +200,12 @@ import goldIcon from 'assets/svg/gold.svg';
|
||||
import notificationsIcon from 'assets/svg/notifications.svg';
|
||||
import userIcon from 'assets/svg/user.svg';
|
||||
import logo from 'assets/svg/logo.svg';
|
||||
import InboxModal from './userMenu/inbox.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
InboxModal,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
@@ -222,6 +228,9 @@ export default {
|
||||
localStorage.removeItem('habit-mobile-settings');
|
||||
this.$router.go('/');
|
||||
},
|
||||
showInbox () {
|
||||
this.$root.$emit('show::modal', 'inbox-modal');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template lang="pug">
|
||||
.avatar(:style="{width, height, paddingTop}", :class="backgroundClass")
|
||||
.character-sprites
|
||||
template(v-if="!avatarOnly" v-once)
|
||||
template(v-if="!avatarOnly")
|
||||
// Mount Body
|
||||
span(v-if="member.items.currentMount", :class="'Mount_Body_' + member.items.currentMount")
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
// Resting
|
||||
span.zzz(v-if="member.preferences.sleep")
|
||||
|
||||
template(v-if="!avatarOnly" v-once)
|
||||
template(v-if="!avatarOnly")
|
||||
// Mount Head
|
||||
span(v-if="member.items.currentMount", :class="'Mount_Head_' + member.items.currentMount")
|
||||
// Pet
|
||||
@@ -168,4 +168,4 @@ export default {
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
</script>
|
||||
|
||||
451
website/client/components/creatorIntro.vue
Normal file
@@ -0,0 +1,451 @@
|
||||
<template lang="pug">
|
||||
#creator-background
|
||||
#creator-modal
|
||||
.section.row.welcome-section(v-if='modalPage == 1')
|
||||
.col-6.offset-3.text-center
|
||||
h3(v-once) {{$t('welcomeTo')}}
|
||||
.svg-icon.logo(v-html='icons.logoPurple')
|
||||
|
||||
.section.row
|
||||
.col-6.offset-3
|
||||
.user-creation-bg
|
||||
avatar(:member='user')
|
||||
|
||||
div(v-if='modalPage == 2')
|
||||
.section.row
|
||||
.col-12.text-center
|
||||
button.btn.btn-secondary(v-once) {{$t('randomize')}}
|
||||
.section.row.text-center.customize-menu
|
||||
.col-3
|
||||
.menu-item(@click='changeTopPage("body", "size")')
|
||||
.svg-icon(v-html='icons.bodyIcon')
|
||||
strong(v-once) {{$t('body')}}
|
||||
.col-3
|
||||
.menu-item(@click='changeTopPage("skin", "color")')
|
||||
.svg-icon(v-html='icons.skinIcon')
|
||||
strong(v-once) {{$t('skin')}}
|
||||
.col-3
|
||||
.menu-item(@click='changeTopPage("hair", "color")')
|
||||
.svg-icon(v-html='icons.hairIcon')
|
||||
strong(v-once) {{$t('hair')}}
|
||||
.col-3
|
||||
.menu-item(@click='changeTopPage("extra", "glasses")')
|
||||
.svg-icon(v-html='icons.accessoriesIcon')
|
||||
strong(v-once) {{$t('extra')}}
|
||||
.section.customize-section(v-if='activeTopPage === "body"')
|
||||
.row.sub-menu
|
||||
.col-2.offset-4.sub-menu-item(@click='changeSubPage("size")', :class='{active: activeSubPage === "size"}')
|
||||
strong(v-once) {{$t('size')}}
|
||||
.col-2.sub-menu-item(@click='changeSubPage("shirt")', :class='{active: activeSubPage === "shirt"}')
|
||||
strong(v-once) {{$t('shirt')}}
|
||||
.row(v-if='activeSubPage === "size"')
|
||||
.col-12.customize-options.size-options
|
||||
.slim_shirt_black.option(@click='set({"preferences.size":"slim"})', :class='{active: user.preferences.size === "slim"}')
|
||||
.broad_shirt_black.option(@click='set({"preferences.size":"broad"})', :class='{active: user.preferences.size === "broad"}')
|
||||
.row(v-if='activeSubPage === "shirt"')
|
||||
.col-12.customize-options
|
||||
.slim_shirt_black.option(@click='set({"preferences.shirt":"black"})', :class='{active: user.preferences.shirt === "black"}')
|
||||
.slim_shirt_blue.option(@click='set({"preferences.shirt":"blue"})', :class='{active: user.preferences.shirt === "blue"}')
|
||||
.slim_shirt_green.option(@click='set({"preferences.shirt":"green"})', :class='{active: user.preferences.shirt === "green"}')
|
||||
.slim_shirt_pink.option(@click='set({"preferences.shirt":"pink"})', :class='{active: user.preferences.shirt === "pink"}')
|
||||
.slim_shirt_white.option(@click='set({"preferences.shirt":"white"})', :class='{active: user.preferences.shirt === "white"}')
|
||||
.slim_shirt_yellow.option(@click='set({"preferences.shirt":"yellow"})', :class='{active: user.preferences.shirt === "yellow"}')
|
||||
|
||||
.section.customize-section(v-if='activeTopPage === "skin"')
|
||||
.row.sub-menu
|
||||
.col-6.offset-3.text-center.sub-menu-item(:class='{active: activeSubPage === "color"}')
|
||||
strong(v-once) {{$t('color')}}
|
||||
.row
|
||||
.col-12.customize-options
|
||||
.skin_ddc994.option(@click='set({"preferences.skin":"ddc994"})', :class='{active: user.preferences.skin === "ddc994"}')
|
||||
.skin_f5a76e.option(@click='set({"preferences.skin":"f5a76e"})', :class='{active: user.preferences.skin === "f5a76e"}')
|
||||
.skin_ea8349.option(@click='set({"preferences.skin":"ea8349"})', :class='{active: user.preferences.skin === "ea8349"}')
|
||||
.skin_c06534.option(@click='set({"preferences.skin":"c06534"})', :class='{active: user.preferences.skin === "c06534"}')
|
||||
.skin_98461a.option(@click='set({"preferences.skin":"98461a"})', :class='{active: user.preferences.skin === "98461a"}')
|
||||
.skin_915533.option(@click='set({"preferences.skin":"915533"})', :class='{active: user.preferences.skin === "915533"}')
|
||||
.skin_c3e1dc.option(@click='set({"preferences.skin":"c3e1dc"})', :class='{active: user.preferences.skin === "c3e1dc"}')
|
||||
.skin_6bd049.option(@click='set({"preferences.skin":"6bd049"})', :class='{active: user.preferences.skin === "6bd049"}')
|
||||
|
||||
.section.customize-section(v-if='activeTopPage === "hair"')
|
||||
.row.sub-menu
|
||||
.col-2.offset-3.text-center.sub-menu-item(@click='changeSubPage("color")', :class='{active: activeSubPage === "color"}')
|
||||
strong(v-once) {{$t('color')}}
|
||||
.col-2.text-center.sub-menu-item(@click='changeSubPage("bangs")', :class='{active: activeSubPage === "bangs"}')
|
||||
strong(v-once) {{$t('bangs')}}
|
||||
.col-2.text-center.sub-menu-item(@click='changeSubPage("ponytail")', :class='{active: activeSubPage === "ponytail"}')
|
||||
strong(v-once) {{$t('ponytail')}}
|
||||
.row(v-if='activeSubPage === "color"')
|
||||
.col-12.customize-options
|
||||
.hair_bangs_1_white.option(@click='set({"preferences.hair.color": "white"})', :class='{active: user.preferences.hair.color === "white"}')
|
||||
.hair_bangs_1_brown.option(@click='set({"preferences.hair.color": "brown"})', :class='{active: user.preferences.hair.color === "brown"}')
|
||||
.hair_bangs_1_blond.option(@click='set({"preferences.hair.color": "blond"})', :class='{active: user.preferences.hair.color === "blond"}')
|
||||
.hair_bangs_1_red.option(@click='set({"preferences.hair.color": "red"})', :class='{active: user.preferences.hair.color === "red"}')
|
||||
.hair_bangs_1_black.option(@click='set({"preferences.hair.color": "black"})', :class='{active: user.preferences.hair.color === "black"}')
|
||||
.row(v-if='activeSubPage === "bangs"')
|
||||
.col-12.customize-options
|
||||
.head_0.option(@click='set({"preferences.hair.bangs": 0})', :class="[{ active: user.preferences.hair.bangs === 0 }, 'hair_bangs_0_' + user.preferences.hair.color]")
|
||||
.option(@click='set({"preferences.hair.bangs": 1})', :class="[{ active: user.preferences.hair.bangs === 1 }, 'hair_bangs_1_' + user.preferences.hair.color]")
|
||||
.option(@click='set({"preferences.hair.bangs": 2})',:class="[{ active: user.preferences.hair.bangs === 2 }, 'hair_bangs_2_' + user.preferences.hair.color]")
|
||||
.option(@click='set({"preferences.hair.bangs": 3})', :class="[{ active: user.preferences.hair.bangs === 3 }, 'hair_bangs_3_' + user.preferences.hair.color]")
|
||||
.option(@click='set({"preferences.hair.bangs": 4})', :class="[{ active: user.preferences.hair.bangs === 4 }, 'hair_bangs_4_' + user.preferences.hair.color]")
|
||||
.row(v-if='activeSubPage === "ponytail"')
|
||||
.col-12.customize-options
|
||||
.head_0.option(@click='set({"preferences.hair.base": 0})', :class="[{ active: user.preferences.hair.base === 0 }, 'hair_base_0_' + user.preferences.hair.color]")
|
||||
.hair_base_1_blond.option(@click='set({"preferences.hair.base": 1})', :class="[{ active: user.preferences.hair.base === 1 }, 'hair_base_1_' + user.preferences.hair.color]")
|
||||
.hair_base_3_blond.option(@click='set({"preferences.hair.base": 3})', :class="[{ active: user.preferences.hair.base === 3 }, 'hair_base_3_' + user.preferences.hair.color]")
|
||||
|
||||
.section.container.customize-section(v-if='activeTopPage === "extra"')
|
||||
.row.sub-menu
|
||||
.col-4.text-center.sub-menu-item(@click='changeSubPage("glasses")', :class='{active: activeSubPage === "glasses"}')
|
||||
strong(v-once) {{$t('glasses')}}
|
||||
.col-4.text-center.sub-menu-item(@click='changeSubPage("wheelchair")', :class='{active: activeSubPage === "wheelchair"}')
|
||||
strong(v-once) {{$t('wheelchair')}}
|
||||
.col-4.text-center.sub-menu-item(@click='changeSubPage("flower")', :class='{active: activeSubPage === "flower"}')
|
||||
strong(v-once) {{$t('flower')}}
|
||||
.row(v-if='activeSubPage === "glasses"')
|
||||
.col-12.customize-options
|
||||
.eyewear_special_blackTopFrame.option(@click='equip("eyewear_special_blackTopFrame")', :class='{active: user.preferences.costume ? user.items.gear.costume.eyewear === "eyewear_special_blackTopFrame" : user.items.gear.equipped.eyewear === "eyewear_special_blackTopFrame"}')
|
||||
.eyewear_special_blueTopFrame.option(@click='equip("eyewear_special_blueTopFrame")', :class='{active: user.preferences.costume ? user.items.gear.costume.eyewear === "eyewear_special_blueTopFrame" : user.items.gear.equipped.eyewear === "eyewear_special_blueTopFrame"}')
|
||||
.eyewear_special_greenTopFrame.option(@click='equip("eyewear_special_greenTopFrame")', :class='{active: user.preferences.costume ? user.items.gear.costume.eyewear === "eyewear_special_greenTopFrame" : user.items.gear.equipped.eyewear === "eyewear_special_greenTopFrame"}')
|
||||
.eyewear_special_pinkTopFrame.option(@click='equip("eyewear_special_pinkTopFrame")', :class='{active: user.preferences.costume ? user.items.gear.costume.eyewear === "eyewear_special_pinkTopFrame" : user.items.gear.equipped.eyewear === "eyewear_special_pinkTopFrame"}')
|
||||
.eyewear_special_redTopFrame.option(@click='equip("eyewear_special_redTopFrame")', :class='{active: user.preferences.costume ? user.items.gear.costume.eyewear === "eyewear_special_redTopFrame" : user.items.gear.equipped.eyewear === "eyewear_special_redTopFrame"}')
|
||||
.eyewear_special_whiteTopFrame.option(@click='equip("eyewear_special_whiteTopFrame")', :class='{active: user.preferences.costume ? user.items.gear.costume.eyewear === "eyewear_special_whiteTopFrame" : user.items.gear.equipped.eyewear === "eyewear_special_whiteTopFrame"}')
|
||||
.eyewear_special_yellowTopFrame.option(@click='equip("eyewear_special_yellowTopFrame")', :class='{active: user.preferences.costume ? user.items.gear.costume.eyewear === "eyewear_special_yellowTopFrame" : user.items.gear.equipped.eyewear === "eyewear_special_yellowTopFrame"}')
|
||||
.row(v-if='activeSubPage === "wheelchair"')
|
||||
.col-12.customize-options.weelchairs
|
||||
.option(@click='set({"preferences.chair": "none"})', :class='{active: user.preferences.chair === "none"}')
|
||||
| None
|
||||
.option(@click='set({"preferences.chair": "black"})', :class='{active: user.preferences.chair === "black"}')
|
||||
.button_chair_black
|
||||
.option(@click='set({"preferences.chair": "blue"})', :class='{active: user.preferences.chair === "blue"}')
|
||||
.button_chair_blue
|
||||
.option(@click='set({"preferences.chair": "green"})', :class='{active: user.preferences.chair === "green"}')
|
||||
.button_chair_green
|
||||
.option(@click='set({"preferences.chair": "pink"})', :class='{active: user.preferences.chair === "pink"}')
|
||||
.button_chair_pink
|
||||
.option(@click='set({"preferences.chair": "red"})', :class='{active: user.preferences.chair === "red"}')
|
||||
.button_chair_red
|
||||
.option(@click='set({"preferences.chair": "yellow"})', :class='{active: user.preferences.chair === "yellow"}')
|
||||
.button_chair_yellow
|
||||
.row(v-if='activeSubPage === "flower"')
|
||||
.col-12.customize-options
|
||||
.head_0.option(@click='set({"preferences.hair.flower":0})', :class='{active: user.preferences.hair.flower === 0}')
|
||||
.hair_flower_1.option(@click='set({"preferences.hair.flower":1})', :class='{active: user.preferences.hair.flower === 1}')
|
||||
.hair_flower_2.option(@click='set({"preferences.hair.flower":2})', :class='{active: user.preferences.hair.flower === 2}')
|
||||
.hair_flower_3.option(@click='set({"preferences.hair.flower":3})', :class='{active: user.preferences.hair.flower === 3}')
|
||||
.hair_flower_4.option(@click='set({"preferences.hair.flower":4})', :class='{active: user.preferences.hair.flower === 4}')
|
||||
.hair_flower_5.option(@click='set({"preferences.hair.flower":5})', :class='{active: user.preferences.hair.flower === 5}')
|
||||
.hair_flower_6.option(@click='set({"preferences.hair.flower":6})', :class='{active: user.preferences.hair.flower === 6}')
|
||||
|
||||
.container.interests-section(v-if='modalPage == 3')
|
||||
.section.row
|
||||
.col-12.text-center
|
||||
h2 I want to work on:
|
||||
.section.row
|
||||
.col-4.offset-2
|
||||
div
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ $t('work') }}
|
||||
div
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ $t('excercise') }}
|
||||
div
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ $t('health') }}
|
||||
div
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ $t('school') }}
|
||||
.col-4
|
||||
div
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ $t('chores') }}
|
||||
div
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ $t('creativity') }}
|
||||
div
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ $t('budgeting') }}
|
||||
|
||||
.section.row.justin-message-section(:class='{top: modalPage > 1}')
|
||||
.col-9
|
||||
.justin-message(v-if='modalPage == 1')
|
||||
p(v-once) {{$t('justinIntroMessage1')}}
|
||||
p(v-once) {{$t('justinIntroMessage2')}}
|
||||
.justin-message(v-if='modalPage > 1')
|
||||
p(v-once) {{$t('justinIntroMessage3')}}
|
||||
|
||||
.section.container.footer
|
||||
.row
|
||||
.col-3.offset-1.text-center
|
||||
div(v-if='modalPage > 1', @click='prev()')
|
||||
.prev-arrow
|
||||
.prev(v-once) {{$t('prev')}}
|
||||
.col-4.text-center.circles
|
||||
.circle(:class="{active: modalPage === 1}")
|
||||
.circle(:class="{active: modalPage === 2}")
|
||||
.circle(:class="{active: modalPage === 3}")
|
||||
.col-3.text-center
|
||||
div(v-if='modalPage < 3', @click='next()')
|
||||
.next(v-once) {{$t('next')}}
|
||||
.next-arrow
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
#creator-background {
|
||||
background-color: $purple-200;
|
||||
}
|
||||
|
||||
#creator-modal {
|
||||
width: 448px;
|
||||
height: 670px;
|
||||
border-radius: 8px;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 2px 16px 0 rgba(26, 24, 29, 0.32);
|
||||
margin: 0 auto;
|
||||
padding-top: 1em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
.welcome-section {
|
||||
margin-top: 5em;
|
||||
margin-bottom: 5em;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 190px;
|
||||
}
|
||||
|
||||
.user-creation-bg {
|
||||
background-image: url('~client/assets/creator/creator-hills-bg.png');
|
||||
height: 105px;
|
||||
width: 219px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
position: absolute;
|
||||
top: -23px;
|
||||
left: 48px;
|
||||
}
|
||||
|
||||
.justin-message {
|
||||
background-image: url('~client/assets/svg/for-css/tutorial-border.svg');
|
||||
height: 144px;
|
||||
width: 400px;
|
||||
padding: 2em;
|
||||
margin-left: 1.5em;
|
||||
}
|
||||
|
||||
.justin-message-section {
|
||||
margin-top: 6em;
|
||||
margin-bottom: 6em;
|
||||
}
|
||||
|
||||
.justin-message-section.top {
|
||||
position: absolute;
|
||||
top: -15em;
|
||||
}
|
||||
|
||||
.circles {
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
.circle {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: #d8d8d8;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.circle.active {
|
||||
background-color: #bda8ff;
|
||||
}
|
||||
|
||||
.customize-menu {
|
||||
.menu-item .svg-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
cursor: pointer;
|
||||
svg path, strong {
|
||||
stroke: purple !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sub-menu:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sub-menu-item {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sub-menu .sub-menu-item:hover, .sub-menu .sub-menu-item.active {
|
||||
color: $purple-200;
|
||||
border-bottom: 2px solid $purple-200;
|
||||
}
|
||||
|
||||
.customize-options .option {
|
||||
display: inline-block;
|
||||
padding: 2em;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.size-options {
|
||||
padding-left: 9em;
|
||||
}
|
||||
|
||||
.weelchairs .option {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
.option.active {
|
||||
border: 4px solid $purple-200;
|
||||
border-radius: 4px;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.option:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.customize-section {
|
||||
background-color: #f9f9f9;
|
||||
padding-top: 1em;
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
.interests-section {
|
||||
margin-top: 7em;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
padding-bottom: 1em;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
|
||||
.prev {
|
||||
color: #a5a1ac;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
padding: 0.4em;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.prev:hover, .prev-arrow:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.prev-arrow {
|
||||
background-image: url('~client/assets/creator/prev.png');
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.next {
|
||||
color: #6133b4;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
padding: 0.4em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.next:hover, .next-arrow:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.next-arrow {
|
||||
background-image: url('~client/assets/creator/arrow.png');
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// @TODO: Wait for my other PR (login/register) to fix the background and hiding the header
|
||||
|
||||
import { mapState } from 'client/libs/store';
|
||||
import avatar from './avatar';
|
||||
|
||||
import logoPurple from 'assets/svg/logo-purple.svg';
|
||||
import bodyIcon from 'assets/svg/body.svg';
|
||||
import accessoriesIcon from 'assets/svg/accessories.svg';
|
||||
import skinIcon from 'assets/svg/skin.svg';
|
||||
import hairIcon from 'assets/svg/hair.svg';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
avatar,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
logoPurple,
|
||||
bodyIcon,
|
||||
accessoriesIcon,
|
||||
skinIcon,
|
||||
hairIcon,
|
||||
}),
|
||||
modalPage: 1,
|
||||
activeTopPage: 'body',
|
||||
activeSubPage: 'size',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
},
|
||||
methods: {
|
||||
prev () {
|
||||
this.modalPage -= 1;
|
||||
},
|
||||
next () {
|
||||
this.modalPage += 1;
|
||||
},
|
||||
changeTopPage (page, subpage) {
|
||||
this.activeTopPage = page;
|
||||
if (subpage) this.activeSubPage = subpage;
|
||||
},
|
||||
changeSubPage (page) {
|
||||
this.activeSubPage = page;
|
||||
},
|
||||
set (settings) {
|
||||
this.$store.dispatch('user:set', settings);
|
||||
},
|
||||
equip (key) {
|
||||
this.$store.dispatch('common:equip', {key, type: 'costume'});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -2,7 +2,7 @@
|
||||
.row
|
||||
sidebar(@search="updateSearch", @filter="updateFilters")
|
||||
|
||||
.col-10.standard-page
|
||||
.standard-page
|
||||
.clearfix
|
||||
h1.page-header.float-left(v-once) {{ $t('publicGuilds') }}
|
||||
.float-right
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
.row
|
||||
sidebar(v-on:search="updateSearch", v-on:filter="updateFilters")
|
||||
|
||||
.col-10.no-guilds.standard-page(v-if='filteredGuilds.length === 0')
|
||||
.no-guilds.standard-page(v-if='filteredGuilds.length === 0')
|
||||
.no-guilds-wrapper
|
||||
.svg-icon(v-html='icons.greyBadge')
|
||||
h2 {{$t('noGuildsTitle')}}
|
||||
@@ -10,7 +10,7 @@
|
||||
p {{$t('noGuildsParagraph2')}}
|
||||
span(v-if='loading') {{ $t('loading') }}
|
||||
|
||||
.col-10.standard-page(v-if='filteredGuilds.length > 0')
|
||||
.standard-page(v-if='filteredGuilds.length > 0')
|
||||
.row
|
||||
.col-md-12
|
||||
h1.page-header.float-left(v-once) {{ $t('myGuilds') }}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template lang="pug">
|
||||
.col-2.standard-sidebar
|
||||
.standard-sidebar
|
||||
.form-group
|
||||
input.form-control.search(type="text", :placeholder="$t('search')", v-model='searchTerm')
|
||||
|
||||
|
||||
@@ -1,209 +1,446 @@
|
||||
<template lang="pug">
|
||||
.row
|
||||
.col-md-4
|
||||
h2.ui.dividing.header SideMenu
|
||||
.clearfix.col-8.standard-page
|
||||
.row
|
||||
.col-6.title-details
|
||||
h1(v-once) {{ $t('welcomeToTavern') }}
|
||||
|
||||
.ui.card
|
||||
.content
|
||||
.header Daniel
|
||||
.content
|
||||
h4.ui.sub.header Activity
|
||||
| Description
|
||||
.extra.content
|
||||
button.ui.button Rest at the Inn
|
||||
.row.chat-row
|
||||
.col-12
|
||||
h3(v-once) {{ $t('welcomeToTavern') }}
|
||||
|
||||
.ui.card
|
||||
.content
|
||||
.header Resources
|
||||
.content
|
||||
textarea(:placeholder="$t('chatPlaceHolder')")
|
||||
button.btn.btn-secondary.send-chat.float-right(v-once) {{ $t('send') }}
|
||||
|
||||
.container.community-guidelines(v-if='communityGuidelinesAccepted')
|
||||
.row
|
||||
div.col-8(v-once) {{ $t('communityGuidelinesIntro') }}
|
||||
div.col-4
|
||||
button.btn.btn-info(@click='acceptCommunityGuidelines()', v-once) {{ $t('acceptCommunityGuidelines') }}
|
||||
|
||||
.hr
|
||||
.hr-middle(v-once) {{ $t('today') }}
|
||||
|
||||
.row
|
||||
.col-md-2
|
||||
.svg-icon(v-html="icons.like")
|
||||
.col-md-10
|
||||
.card(v-for="msg in group.chat", :key="msg.id")
|
||||
.card-block
|
||||
h3.leader Character name
|
||||
span 2 hours ago
|
||||
.clearfix
|
||||
strong.float-left {{msg.user}}
|
||||
.float-right {{msg.timestamp}}
|
||||
.text {{msg.text}}
|
||||
hr
|
||||
span.action(v-once)
|
||||
.svg-icon(v-html="icons.like")
|
||||
| {{$t('like')}}
|
||||
span.action(v-once)
|
||||
.svg-icon(v-html="icons.copy")
|
||||
| {{$t('copyAsTodo')}}
|
||||
span.action(v-once)
|
||||
.svg-icon(v-html="icons.report")
|
||||
| {{$t('report')}}
|
||||
span.action(v-once)
|
||||
.svg-icon(v-html="icons.delete")
|
||||
| {{$t('delete')}}
|
||||
span.action.float-right
|
||||
.svg-icon(v-html="icons.liked")
|
||||
| +3
|
||||
|
||||
.col-md-4.sidebar
|
||||
.section
|
||||
.grassy-meadow-backdrop
|
||||
|
||||
.sleep
|
||||
strong(v-once) {{ $t('sleepDescription') }}
|
||||
ul
|
||||
li Community Guidelines
|
||||
li Looking for Group (Party Wanted) Posts
|
||||
li FAQ
|
||||
li Glossary
|
||||
li Wiki
|
||||
li Data Display Tool
|
||||
li Report a Problem
|
||||
li Request a Feature
|
||||
li Community Forum
|
||||
li Ask a Question (Habitica Help guild)
|
||||
li(v-once) {{ $t('sleepBullet1') }}
|
||||
li(v-once) {{ $t('sleepBullet2') }}
|
||||
li(v-once) {{ $t('sleepBullet3') }}
|
||||
li(v-once) {{ $t('sleepBullet4') }}
|
||||
button.btn.btn-secondary.pause-button(v-if='!user.preferences.sleep', @click='toggleSleep()', v-once) {{ $t('pauseDailies') }}
|
||||
button.btn.btn-secondary.pause-button(v-if='user.preferences.sleep', @click='toggleSleep()', v-once) {{ $t('unpauseDailies') }}
|
||||
|
||||
.ui.card
|
||||
.content
|
||||
.header Resources
|
||||
.content
|
||||
small
|
||||
a(href='/#/options/groups/hall') Visit the Hall of Heroes (contributors and backers)
|
||||
br
|
||||
a(href='http://habitica.wikia.com/wiki/Contributor_Rewards', target='_blank') Learn more about contributor rewards
|
||||
br
|
||||
a(href='http://habitica.wikia.com/wiki/Contributing_to_Habitica', target='_blank') Learn how to contribute to Habitica
|
||||
table.table.table-striped.panel-tiers
|
||||
tbody
|
||||
tr
|
||||
td
|
||||
a.label.label-contributor-1(ng-click='toggleUserTier($event)') Tier 1 (Friend)
|
||||
div
|
||||
p
|
||||
span.achievement.achievement-boot
|
||||
| When your
|
||||
strong first
|
||||
| set of submissions is deployed, you will receive the Habitica Contributor's badge. Your name in Tavern chat will proudly display that you are a contributor. As a bounty for your work, you will also receive
|
||||
strong 3 Gems
|
||||
| .
|
||||
tr
|
||||
td
|
||||
a.label.label-contributor-2(ng-click='toggleUserTier($event)') Tier 2 (Friend)
|
||||
div
|
||||
p
|
||||
span.shop-sprite.item-img.shop_armor_special_1
|
||||
| When your
|
||||
strong second
|
||||
| set of submissions is deployed, the
|
||||
strong Crystal Armor
|
||||
| will be available for purchase in the Rewards shop. As a bounty for your continued work, you will also receive
|
||||
strong 3 Gems.
|
||||
tr
|
||||
td
|
||||
a.label.label-contributor-3(ng-click='toggleUserTier($event)') Tier 3 (Elite)
|
||||
div
|
||||
p
|
||||
span.shop-sprite.item-img.shop_head_special_1
|
||||
| When your
|
||||
strong third
|
||||
| set of submissions is deployed, the
|
||||
strong Crystal Helmet
|
||||
| will be available for purchase in the Rewards shop. As a bounty for your continued work, you will also receive
|
||||
strong 3 Gems
|
||||
| .
|
||||
tr
|
||||
td
|
||||
a.label.label-contributor-4(ng-click='toggleUserTier($event)') Tier 4 (Elite)
|
||||
div
|
||||
p
|
||||
span.shop-sprite.item-img.shop_weapon_special_1
|
||||
| When your
|
||||
strong fourth
|
||||
| set of submissions is deployed, the
|
||||
strong Crystal Sword
|
||||
| will be available for purchase in the Rewards shop. As a bounty for your continued work, you will also receive
|
||||
strong 4 Gems
|
||||
| .
|
||||
tr
|
||||
td
|
||||
a.label.label-contributor-5(ng-click='toggleUserTier($event)') Tier 5 (Champion)
|
||||
div
|
||||
p
|
||||
span.shop-sprite.item-img.shop_shield_special_1
|
||||
| When your
|
||||
strong fifth
|
||||
| set of submissions is deployed, the
|
||||
strong Crystal Shield
|
||||
| will be available for purchase in the Rewards shop. As a bounty for your continued work, you will also receive
|
||||
strong 4 Gems
|
||||
| .
|
||||
tr
|
||||
td
|
||||
a.label.label-contributor-6(ng-click='toggleUserTier($event)') Tier 6 (Champion)
|
||||
div
|
||||
p
|
||||
span.shop-sprite.item-img.Pet-Dragon-Hydra
|
||||
| When your
|
||||
strong sixth
|
||||
| set of submissions is deployed, you will receive a
|
||||
strong Hydra Pet
|
||||
| . You will also receive
|
||||
strong 4 Gems
|
||||
| .
|
||||
tr
|
||||
td
|
||||
a.label.label-contributor-7(ng-click='toggleUserTier($event)') Tier 7 (Legendary)
|
||||
div
|
||||
p
|
||||
| When your
|
||||
strong seventh
|
||||
| set of submissions is deployed, you will receive
|
||||
strong 4 Gems
|
||||
| and become a member of the honored Contributor's Guild and be privy to the behind-the-scenes details of Habitica! Further contributions do not increase your tier, but you may continue to earn Gem bounties and titles.
|
||||
tr
|
||||
td
|
||||
a.label.label-contributor-8(ng-click='toggleUserTier($event)') Moderator (Guardian)
|
||||
div
|
||||
p
|
||||
| Moderators were selected carefully from high tier contributors, so please give them your respect and listen to their suggestions.
|
||||
tr
|
||||
td
|
||||
a.label.label-contributor-9(ng-click='toggleUserTier($event)') Staff (Heroic)
|
||||
div
|
||||
p
|
||||
| The Heroic tier contains Habitica staff and staff-level contributors. If you have this title, you were appointed to it (or hired!).
|
||||
tr
|
||||
td
|
||||
a.label.label-npc(ng-click='toggleUserTier($event)') NPC
|
||||
div
|
||||
p
|
||||
| NPCs backed Habitica's Kickstarter at the highest tier. You can find their avatars watching over site features!
|
||||
.section-header
|
||||
.row
|
||||
.col-10
|
||||
h3(v-once) {{ $t('staffAndModerators') }}
|
||||
.col-2
|
||||
.toggle-up(@click="sections.staff = !sections.staff", v-if="sections.staff")
|
||||
.svg-icon(v-html="icons.upIcon")
|
||||
.toggle-down(@click="sections.staff = !sections.staff", v-if="!sections.staff")
|
||||
.svg-icon(v-html="icons.downIcon")
|
||||
.section.row(v-if="sections.staff")
|
||||
.col-3.staff(v-for='user in staff', :class='{staff: user.type === "Staff", moderator: user.type === "Moderator", bailey: user.name === "It\'s Bailey"}')
|
||||
.title {{user.name}}
|
||||
.type {{user.type}}
|
||||
|
||||
.ui.card
|
||||
.content
|
||||
.header Challenges
|
||||
.content
|
||||
.section-header
|
||||
.row
|
||||
.col-10
|
||||
h3(v-once) {{ $t('helpfulLinks') }}
|
||||
.col-2
|
||||
.toggle-up(@click="sections.staff = !sections.staff", v-if="sections.staff")
|
||||
.svg-icon(v-html="icons.upIcon")
|
||||
.toggle-down(@click="sections.staff = !sections.staff", v-if="!sections.staff")
|
||||
.svg-icon(v-html="icons.downIcon")
|
||||
.section.row(v-if="sections.staff")
|
||||
ul
|
||||
li Challenge 1
|
||||
li
|
||||
a(herf='', v-once) {{ $t('communityGuidelinesLink') }}
|
||||
li
|
||||
a(herf='', v-once) {{ $t('lookingForGroup') }}
|
||||
li
|
||||
a(herf='', v-once) {{ $t('faq') }}
|
||||
li
|
||||
a(herf='', v-once) {{ $t('glossary') }}
|
||||
li
|
||||
a(herf='', v-once) {{ $t('wiki') }}
|
||||
li
|
||||
a(herf='', v-once) {{ $t('dataDisplayTool') }}
|
||||
li
|
||||
a(herf='', v-once) {{ $t('reportProblem') }}
|
||||
li
|
||||
a(herf='', v-once) {{ $t('requestFeature') }}
|
||||
li
|
||||
a(herf='', v-once) {{ $t('communityForum') }}
|
||||
li
|
||||
a(herf='', v-once) {{ $t('askQuestionGuild') }}
|
||||
|
||||
.col-md-8
|
||||
h2.ui.dividing.header Tavern Chat
|
||||
.ui.comments
|
||||
|
||||
.field
|
||||
textarea(v-model='newMessage')
|
||||
.ui.blue.labeled.submit.icon.button(v-on:click='sendChat')
|
||||
i.icon.edit
|
||||
| Send Chat
|
||||
|
||||
.comment(v-for="message in messages")
|
||||
a.avatar
|
||||
img(src='http://semantic-ui.com/images/avatar/small/matt.jpg')
|
||||
.content
|
||||
a.author {{message.from}}
|
||||
.metadata
|
||||
span.date {{message.date}}
|
||||
.text
|
||||
| {{message.message}}
|
||||
.section-header
|
||||
.row
|
||||
.col-10
|
||||
h3(v-once) {{ $t('playerTiers') }}
|
||||
.col-2
|
||||
.toggle-up(@click="sections.staff = !sections.staff", v-if="sections.staff")
|
||||
.svg-icon(v-html="icons.upIcon")
|
||||
.toggle-down(@click="sections.staff = !sections.staff", v-if="!sections.staff")
|
||||
.svg-icon(v-html="icons.downIcon")
|
||||
.section.row(v-if="sections.staff")
|
||||
.col-12
|
||||
p(v-once) {{ $t('playerTiersDesc') }}
|
||||
ul.tier-list
|
||||
li.tier1(v-once) {{ $t('tier1') }}
|
||||
li.tier2(v-once) {{ $t('tier2') }}
|
||||
li.tier3(v-once) {{ $t('tier3') }}
|
||||
li.tier4(v-once) {{ $t('tier4') }}
|
||||
li.tier5(v-once) {{ $t('tier5') }}
|
||||
li.tier6(v-once) {{ $t('tier6') }}
|
||||
li.tier7(v-once) {{ $t('tier7') }}
|
||||
li.moderator(v-once) {{ $t('tierModerator') }}
|
||||
li.staff(v-once) {{ $t('tierStaff') }}
|
||||
li.npc(v-once) {{ $t('tierNPC') }}
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
// @TODO: Move chat to component
|
||||
.chat-row {
|
||||
position: relative;
|
||||
|
||||
.community-guidelines {
|
||||
background-color: rgba(135, 129, 144, 0.84);
|
||||
padding: 1em;
|
||||
color: $white;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 150px;
|
||||
padding-top: 3em;
|
||||
margin-top: 2.3em;
|
||||
width: 98%;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
height: 150px;
|
||||
width: 100%;
|
||||
background-color: $white;
|
||||
border: solid 1px $gray-400;
|
||||
font-size: 16px;
|
||||
font-style: italic;
|
||||
line-height: 1.43;
|
||||
color: $gray-300;
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
.hr {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
border-bottom: 1px solid $gray-500;
|
||||
text-align: center;
|
||||
margin: 2em 0;
|
||||
}
|
||||
|
||||
.hr-middle {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
font-family: 'Roboto Condensed';
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
color: $gray-200;
|
||||
background-color: $gray-700;
|
||||
padding: .2em;
|
||||
margin-top: .2em;
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: $purple-200;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
background-color: $gray-600;
|
||||
padding-top: 2em;
|
||||
}
|
||||
|
||||
.toggle-up, .toggle-down {
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.toggle-up:hover, .toggle-down:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pause-button {
|
||||
background-color: #ffb445 !important;
|
||||
color: $white;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
.grassy-meadow-backdrop {
|
||||
background-image: url('~assets/images/groups/grassy-meadow-backdrop.png');
|
||||
width: 472px;
|
||||
height: 246px;
|
||||
}
|
||||
|
||||
.sleep {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.staff {
|
||||
margin-bottom: 1em;
|
||||
|
||||
.title {
|
||||
color: #6133b4;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.tier-list {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
width: 98%;
|
||||
|
||||
li {
|
||||
border-radius: 2px;
|
||||
background-color: #edecee;
|
||||
border: solid 1px #c3c0c7;
|
||||
text-align: center;
|
||||
padding: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.tier1 {
|
||||
color: #c42870;
|
||||
}
|
||||
|
||||
.tier2 {
|
||||
color: #b01515;
|
||||
}
|
||||
|
||||
.tier3 {
|
||||
color: #d70e14;
|
||||
}
|
||||
|
||||
.tier4 {
|
||||
color: #c24d00;
|
||||
}
|
||||
|
||||
.tier5 {
|
||||
color: #9e650f;
|
||||
}
|
||||
|
||||
.tier6 {
|
||||
color: #2b8363;
|
||||
}
|
||||
|
||||
.tier7 {
|
||||
color: #167e87;
|
||||
}
|
||||
|
||||
.moderator {
|
||||
color: #277eab;
|
||||
}
|
||||
|
||||
.staff {
|
||||
color: #6133b4;
|
||||
}
|
||||
|
||||
.npc {
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
|
||||
.staff .title {
|
||||
color: #6133b4;
|
||||
}
|
||||
|
||||
.moderator .title {
|
||||
color: #277eab;
|
||||
}
|
||||
|
||||
.bailey .title {
|
||||
color: $black;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'client/libs/store';
|
||||
|
||||
import deleteIcon from 'assets/svg/delete.svg';
|
||||
import copyIcon from 'assets/svg/copy.svg';
|
||||
import likeIcon from 'assets/svg/like.svg';
|
||||
import likedIcon from 'assets/svg/liked.svg';
|
||||
import reportIcon from 'assets/svg/report.svg';
|
||||
import gemIcon from 'assets/svg/gem.svg';
|
||||
import questIcon from 'assets/svg/quest.svg';
|
||||
import challengeIcon from 'assets/svg/challenge.svg';
|
||||
import informationIcon from 'assets/svg/information.svg';
|
||||
import questBackground from 'assets/svg/quest-background-border.svg';
|
||||
import upIcon from 'assets/svg/up.svg';
|
||||
import downIcon from 'assets/svg/down.svg';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
// TODO: Abstract to Store
|
||||
let messages = [
|
||||
{
|
||||
from: 'Paglias',
|
||||
fromUserId: 1234,
|
||||
to: 'TheHollidayInn',
|
||||
message: 'I love the Gang of Four',
|
||||
date: new Date(),
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
messages,
|
||||
newMessage: '',
|
||||
icons: Object.freeze({
|
||||
like: likeIcon,
|
||||
copy: copyIcon,
|
||||
report: reportIcon,
|
||||
delete: deleteIcon,
|
||||
gem: gemIcon,
|
||||
liked: likedIcon,
|
||||
questIcon,
|
||||
challengeIcon,
|
||||
information: informationIcon,
|
||||
questBackground,
|
||||
upIcon,
|
||||
downIcon,
|
||||
}),
|
||||
group: {
|
||||
chat: [],
|
||||
},
|
||||
sections: {
|
||||
staff: true,
|
||||
},
|
||||
staff: [
|
||||
{
|
||||
name: 'beffymaroo',
|
||||
type: 'Staff',
|
||||
},
|
||||
{
|
||||
name: 'lefnire',
|
||||
type: 'Staff',
|
||||
},
|
||||
{
|
||||
name: 'Lemoness',
|
||||
type: 'Staff',
|
||||
},
|
||||
{
|
||||
name: 'paglias',
|
||||
type: 'Staff',
|
||||
},
|
||||
{
|
||||
name: 'redphoenix',
|
||||
type: 'Staff',
|
||||
},
|
||||
{
|
||||
name: 'SabreCat',
|
||||
type: 'Staff',
|
||||
},
|
||||
{
|
||||
name: 'TheHollidayInn',
|
||||
type: 'Staff',
|
||||
},
|
||||
{
|
||||
name: 'viirus',
|
||||
type: 'Staff',
|
||||
},
|
||||
{
|
||||
name: 'It\'s Bailey',
|
||||
type: 'Moderator',
|
||||
},
|
||||
{
|
||||
name: 'Alys',
|
||||
type: 'Moderator',
|
||||
},
|
||||
{
|
||||
name: 'Blade',
|
||||
type: 'Moderator',
|
||||
},
|
||||
{
|
||||
name: 'Breadstrings',
|
||||
type: 'Moderator',
|
||||
},
|
||||
{
|
||||
name: 'Cantras',
|
||||
type: 'Moderator',
|
||||
},
|
||||
{
|
||||
name: 'Daniel the Bard',
|
||||
type: 'Moderator',
|
||||
},
|
||||
{
|
||||
name: 'deilann 5.0.5b',
|
||||
type: 'Moderator',
|
||||
},
|
||||
{
|
||||
name: 'Dewines',
|
||||
type: 'Moderator',
|
||||
},
|
||||
{
|
||||
name: 'Megan',
|
||||
type: 'Moderator',
|
||||
},
|
||||
{
|
||||
name: 'shanaqui',
|
||||
type: 'Moderator',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
communityGuidelinesAccepted () {
|
||||
return this.user.flags.communityGuidelinesAccepted;
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
// @TODO: Load tavern
|
||||
},
|
||||
methods: {
|
||||
sendChat: function sendChat () {
|
||||
// TODO: This will be default values based on the conversation and current user
|
||||
let messageToSend = {
|
||||
from: 'TheHollidayInn',
|
||||
fromUserId: 3211,
|
||||
to: 'Paglias',
|
||||
message: this.newMessage,
|
||||
date: new Date(),
|
||||
};
|
||||
this.messages.push(messageToSend);
|
||||
this.newMessage = '';
|
||||
aggreeToGuideLines () {
|
||||
// @TODO:
|
||||
},
|
||||
pauseDailies () {
|
||||
// @TODO:
|
||||
},
|
||||
acceptCommunityGuidelines () {
|
||||
this.$store.dispatch('user:set', {'flags.communityGuidelinesAccepted': true});
|
||||
},
|
||||
toggleSleep () {
|
||||
this.$store.dispatch('user:sleep');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template lang="pug">
|
||||
.row
|
||||
.col-2.standard-sidebar
|
||||
.standard-sidebar
|
||||
.form-group
|
||||
input.form-control.input-search(type="text", v-model="searchText", :placeholder="$t('search')")
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ group.label }}
|
||||
|
||||
.col-10.standard-page
|
||||
.standard-page
|
||||
.clearfix
|
||||
h1.float-left.mb-0.page-header(v-once) {{ $t('equipment') }}
|
||||
.float-right
|
||||
|
||||
@@ -8,18 +8,15 @@ b-popover(
|
||||
v-else,
|
||||
:triggers="['hover']",
|
||||
:placement="popoverPosition",
|
||||
@click="click",
|
||||
)
|
||||
span(slot="content")
|
||||
slot(name="popoverContent", :item="item")
|
||||
|
||||
.item-wrapper
|
||||
.item-wrapper(@click="click")
|
||||
.item
|
||||
slot(name="itemBadge", :item="item")
|
||||
span.item-content(
|
||||
:class="itemContentClass",
|
||||
:draggable="draggable",
|
||||
@dragstart="onDrag"
|
||||
:class="itemContentClass"
|
||||
)
|
||||
span.item-label(v-if="label") {{ label }}
|
||||
</template>
|
||||
@@ -49,22 +46,11 @@ export default {
|
||||
type: String,
|
||||
default: 'bottom',
|
||||
},
|
||||
draggable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
click () {
|
||||
this.$emit('click', this.item);
|
||||
},
|
||||
onDrag (ev) {
|
||||
if (this.draggable) {
|
||||
this.$emit('onDrag', ev);
|
||||
} else {
|
||||
ev.preventDefault();
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template lang="pug">
|
||||
.row
|
||||
.col-2.standard-sidebar
|
||||
.row(v-mousePosition="30", @mouseMoved="mouseMoved($event)")
|
||||
.standard-sidebar
|
||||
.form-group
|
||||
input.form-control.input-search(type="text", v-model="searchText", :placeholder="$t('search')")
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
input.custom-control-input(type="checkbox", v-model="group.selected")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ $t(group.key) }}
|
||||
.col-10.standard-page
|
||||
.standard-page
|
||||
.clearfix
|
||||
h1.float-left.mb-0.page-header(v-once) {{ $t('items') }}
|
||||
.float-right
|
||||
@@ -34,16 +34,55 @@
|
||||
|
|
||||
span.badge.badge-pill.badge-default {{group.quantity}}
|
||||
|
||||
.items
|
||||
.items(v-if="group.key === 'eggs'")
|
||||
item(
|
||||
v-for="({data: item, quantity}, index) in items[group.key]",
|
||||
v-if="group.open || index < itemsPerLine",
|
||||
:item="item",
|
||||
:key="item.key",
|
||||
:itemContentClass="`${group.classPrefix}${item.key}`"
|
||||
:selected="true",
|
||||
:itemContentClass="`${group.classPrefix}${item.key}`",
|
||||
v-drag.drop.hatch="item.key",
|
||||
|
||||
@itemDragOver="onDragOver($event, item)",
|
||||
@itemDropped="onDrop($event, item)",
|
||||
@itemDragLeave="onDragLeave()",
|
||||
|
||||
@click="onEggClicked($event, item)"
|
||||
)
|
||||
template(slot="popoverContent", scope="ctx")
|
||||
template(slot="popoverContent", scope="ctx")
|
||||
h4.popover-content-title {{ ctx.item.text() }}
|
||||
.popover-content-text {{ ctx.item.notes() }}
|
||||
template(slot="itemBadge", scope="ctx")
|
||||
span.badge.badge-pill.badge-item.badge-quantity {{ quantity }}
|
||||
|
||||
.items(v-else-if="group.key === 'hatchingPotions'")
|
||||
item(
|
||||
v-for="({data: item, quantity}, index) in items[group.key]",
|
||||
v-if="group.open || index < itemsPerLine",
|
||||
:item="item",
|
||||
:key="item.key",
|
||||
:itemContentClass="`${group.classPrefix}${item.key}`",
|
||||
v-drag.hatch="item.key",
|
||||
|
||||
@itemDragEnd="onDragEnd($event, item)",
|
||||
@itemDragStart="onDragStart($event, item)",
|
||||
|
||||
@click="onPotionClicked($event, item)"
|
||||
)
|
||||
template(slot="popoverContent", scope="ctx")
|
||||
h4.popover-content-title {{ ctx.item.text() }}
|
||||
.popover-content-text {{ ctx.item.notes() }}
|
||||
template(slot="itemBadge", scope="ctx")
|
||||
span.badge.badge-pill.badge-item.badge-quantity {{ quantity }}
|
||||
.items(v-else)
|
||||
item(
|
||||
v-for="({data: item, quantity}, index) in items[group.key]",
|
||||
v-if="group.open || index < itemsPerLine",
|
||||
:item="item",
|
||||
:key="item.key",
|
||||
:itemContentClass="`${group.classPrefix}${item.key}`",
|
||||
)
|
||||
template(slot="popoverContent", scope="ctx")
|
||||
h4.popover-content-title {{ ctx.item.text() }}
|
||||
.popover-content-text {{ ctx.item.notes() }}
|
||||
template(slot="itemBadge", scope="ctx")
|
||||
@@ -55,9 +94,40 @@
|
||||
@click="group.open = !group.open"
|
||||
) {{ group.open ? $t('showLessItems', { type: $t(group.key) }) : $t('showAllItems', { type: $t(group.key), items: items[group.key].length }) }}
|
||||
|
||||
div.hatchingPotionInfo(ref="draggingPotionInfo")
|
||||
div(v-if="currentDraggingPotion != null")
|
||||
div.potion-icon(:class="'Pet_HatchingPotion_'+currentDraggingPotion.key")
|
||||
div.popover
|
||||
div.popover-content {{ $t('dragThisPotion', {potionName: currentDraggingPotion.text() }) }}
|
||||
|
||||
div.hatchingPotionInfo.mouse(ref="clickPotionInfo", v-if="potionClickMode")
|
||||
div(v-if="currentDraggingPotion != null")
|
||||
div.potion-icon(:class="'Pet_HatchingPotion_'+currentDraggingPotion.key")
|
||||
div.popover
|
||||
div.popover-content {{ $t('clickOnEggToHatch', {potionName: currentDraggingPotion.text() }) }}
|
||||
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.hatchingPotionInfo {
|
||||
position: absolute;
|
||||
left: -500px;
|
||||
|
||||
&.mouse {
|
||||
position: fixed;
|
||||
pointer-events: none
|
||||
}
|
||||
|
||||
.potion-icon {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.popover {
|
||||
position: inherit;
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
@@ -71,6 +141,9 @@ import Item from 'client/components/inventory/item';
|
||||
|
||||
const allowedSpecialItems = ['snowball', 'spookySparkles', 'shinySeed', 'seafoam'];
|
||||
|
||||
import DragDropDirective from 'client/directives/dragdrop.directive';
|
||||
import MouseMoveDirective from 'client/directives/mouseposition.directive';
|
||||
|
||||
const groups = [
|
||||
['eggs', 'Pet_Egg_'],
|
||||
['hatchingPotions', 'Pet_HatchingPotion_'],
|
||||
@@ -94,6 +167,10 @@ export default {
|
||||
bDropdown,
|
||||
bDropdownItem,
|
||||
},
|
||||
directives: {
|
||||
drag: DragDropDirective,
|
||||
mousePosition: MouseMoveDirective,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
itemsPerLine: 9,
|
||||
@@ -101,6 +178,10 @@ export default {
|
||||
searchTextThrottled: null,
|
||||
groups,
|
||||
sortBy: 'quantity', // or 'AZ'
|
||||
|
||||
|
||||
currentDraggingPotion: null,
|
||||
potionClickMode: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
@@ -150,5 +231,71 @@ export default {
|
||||
return itemsByType;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
petExists (potionKey, eggKey) {
|
||||
let animalKey = `${eggKey}-${potionKey}`;
|
||||
|
||||
let result = this.user.items.pets[animalKey] > 0;
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
hatchPet (potionKey, eggKey) {
|
||||
this.$store.dispatch('common:hatch', {egg: eggKey, hatchingPotion: potionKey});
|
||||
},
|
||||
|
||||
onDragEnd () {
|
||||
this.currentDraggingPotion = null;
|
||||
},
|
||||
onDragStart ($event, potion) {
|
||||
this.currentDraggingPotion = potion;
|
||||
|
||||
let itemRef = this.$refs.draggingPotionInfo;
|
||||
|
||||
let dragEvent = $event.event;
|
||||
|
||||
dragEvent.dataTransfer.setDragImage(itemRef, -20, -20);
|
||||
},
|
||||
|
||||
onDragOver ($event, egg) {
|
||||
let potionKey = this.currentDraggingPotion.key;
|
||||
|
||||
if (this.petExists(potionKey, egg.key)) {
|
||||
$event.dropable = false;
|
||||
}
|
||||
},
|
||||
onDrop ($event, egg) {
|
||||
this.hatchPet(this.currentDraggingPotion.key, egg.key);
|
||||
},
|
||||
onDragLeave () {
|
||||
|
||||
},
|
||||
|
||||
onEggClicked ($event, egg) {
|
||||
if (!this.petExists(this.currentDraggingPotion.key, egg.key)) {
|
||||
this.hatchPet(this.currentDraggingPotion.key, egg.key);
|
||||
}
|
||||
|
||||
this.currentDraggingPotion = null;
|
||||
this.potionClickMode = false;
|
||||
},
|
||||
|
||||
onPotionClicked ($event, potion) {
|
||||
if (this.currentDraggingPotion === null || this.currentDraggingPotion !== potion) {
|
||||
this.currentDraggingPotion = potion;
|
||||
this.potionClickMode = true;
|
||||
} else {
|
||||
this.currentDraggingPotion = null;
|
||||
this.potionClickMode = false;
|
||||
}
|
||||
},
|
||||
|
||||
mouseMoved ($event) {
|
||||
if (this.potionClickMode) {
|
||||
this.$refs.clickPotionInfo.style.left = `${$event.x + 20}px`;
|
||||
this.$refs.clickPotionInfo.style.top = `${$event.y + 20}px`;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -7,15 +7,17 @@ b-popover(
|
||||
h4.popover-content-title {{ item.text() }}
|
||||
div.popover-content-text(v-html="item.notes()")
|
||||
|
||||
.item-wrapper
|
||||
.item
|
||||
.item-wrapper(@click="click($event)")
|
||||
.item(:class="{'item-active': active }")
|
||||
countBadge(
|
||||
:show="true",
|
||||
:count="itemCount"
|
||||
)
|
||||
span.item-content(
|
||||
:class="'Pet_Food_'+item.key",
|
||||
v-drag.food="item.key"
|
||||
v-drag.food="item.key",
|
||||
@itemDragEnd="dragend($event)",
|
||||
@itemDragStart="dragstart($event)"
|
||||
)
|
||||
</template>
|
||||
|
||||
@@ -43,6 +45,20 @@ export default {
|
||||
itemContentClass: {
|
||||
type: String,
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
dragend ($event) {
|
||||
this.$emit('itemDragEnd', $event);
|
||||
},
|
||||
dragstart ($event) {
|
||||
this.$emit('itemDragStart', $event);
|
||||
},
|
||||
click ($event) {
|
||||
this.$emit('itemClick', $event);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template lang="pug">
|
||||
.row.stable
|
||||
.row.stable(v-mousePosition="30", @mouseMoved="mouseMoved($event)")
|
||||
.standard-sidebar
|
||||
div
|
||||
b-popover(
|
||||
@@ -21,22 +21,28 @@
|
||||
.form-group
|
||||
.form-check(
|
||||
v-for="petGroup in petGroups",
|
||||
v-if="viewOptions[petGroup.key].animalCount != 0",
|
||||
:key="petGroup.key"
|
||||
)
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox", v-model="viewOptions[petGroup.key].selected")
|
||||
input.custom-control-input(
|
||||
type="checkbox",
|
||||
v-model="viewOptions[petGroup.key].selected",
|
||||
:disabled="viewOptions[petGroup.key].animalCount == 0"
|
||||
)
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ petGroup.label }}
|
||||
h3(v-once) {{ $t('mounts') }}
|
||||
.form-group
|
||||
.form-check(
|
||||
v-for="mountGroup in mountGroups",
|
||||
v-if="viewOptions[mountGroup.key].animalCount != 0",
|
||||
:key="mountGroup.key"
|
||||
)
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox", v-model="viewOptions[mountGroup.key].selected")
|
||||
input.custom-control-input(
|
||||
type="checkbox",
|
||||
v-model="viewOptions[mountGroup.key].selected",
|
||||
:disabled="viewOptions[mountGroup.key].animalCount == 0"
|
||||
)
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ mountGroup.label }}
|
||||
|
||||
@@ -79,8 +85,10 @@
|
||||
v-for="pet in pets(petGroup, viewOptions[petGroup.key].open, hideMissing, selectedSortBy, searchTextThrottled, availableContentWidth)",
|
||||
:key="pet.key",
|
||||
v-drag.drop.food="pet.key",
|
||||
@dragover="onDragOver($event, pet)",
|
||||
@dropped="onDrop($event, pet)",
|
||||
@itemDragOver="onDragOver($event, pet)",
|
||||
@itemDropped="onDrop($event, pet)",
|
||||
@itemDragLeave="onDragLeave()",
|
||||
:class="{'last': pet.isLastInRow}"
|
||||
)
|
||||
petItem(
|
||||
:item="pet",
|
||||
@@ -88,20 +96,13 @@
|
||||
:popoverPosition="'top'",
|
||||
:progress="pet.progress",
|
||||
:emptyItem="!pet.isOwned()",
|
||||
:showPopover="pet.isOwned() || pet.isHatchable()",
|
||||
@hatchPet="hatchPet",
|
||||
:showPopover="pet.isOwned()",
|
||||
:highlightBorder="highlightPet == pet.key",
|
||||
@click="petClicked(pet)"
|
||||
)
|
||||
span(slot="popoverContent")
|
||||
div(v-if="pet.isOwned()")
|
||||
h4.popover-content-title {{ pet.name }}
|
||||
div.hatchablePopover(v-else-if="pet.isHatchable()")
|
||||
h4.popover-content-title {{ pet.name }}
|
||||
div.popover-content-text(v-html="$t('haveHatchablePet', { potion: pet.potionName, egg: pet.eggName })")
|
||||
div.potionEggGroup
|
||||
div.potionEggBackground
|
||||
div(:class="'Pet_HatchingPotion_'+pet.potionKey")
|
||||
div.potionEggBackground
|
||||
div(:class="'Pet_Egg_'+pet.eggKey")
|
||||
|
||||
template(slot="itemBadge", scope="ctx")
|
||||
starBadge(
|
||||
@@ -128,12 +129,14 @@
|
||||
h4(v-if="viewOptions[mountGroup.key].animalCount != 0") {{ mountGroup.label }}
|
||||
|
||||
div.items
|
||||
item(
|
||||
mountItem(
|
||||
v-for="mount in mounts(mountGroup, viewOptions[mountGroup.key].open, hideMissing, selectedSortBy, searchTextThrottled, availableContentWidth)",
|
||||
:item="mount",
|
||||
:itemContentClass="mount.isOwned() ? ('Mount_Icon_' + mount.key) : 'PixelPaw greyedOut'",
|
||||
:itemContentClass="mount.isOwned() ? ('Mount_Icon_' + mount.key) : 'PixelPaw GreyedOut'",
|
||||
:key="mount.key",
|
||||
:popoverPosition="'top'"
|
||||
:popoverPosition="'top'",
|
||||
:emptyItem="!mount.isOwned()",
|
||||
:showPopover="mount.isOwned()",
|
||||
)
|
||||
span(slot="popoverContent")
|
||||
h4.popover-content-title {{ mount.name }}
|
||||
@@ -168,13 +171,15 @@
|
||||
) {{ drawerTabs[1].label }}
|
||||
|
||||
b-popover(
|
||||
:triggers="['hover']",
|
||||
:triggers="['click']",
|
||||
:placement="'top'"
|
||||
)
|
||||
span(slot="content")
|
||||
.popover-content-text Test Popover
|
||||
.popover-content-text(v-html="$t('petLikeToEatText')", v-once)
|
||||
|
||||
div.float-right What does my pet like to eat?
|
||||
div.float-right(v-once)
|
||||
| {{ $t('petLikeToEat') + ' ' }}
|
||||
span.svg-icon.inline.icon-16(v-html="icons.information")
|
||||
|
||||
|
||||
drawer-slider(
|
||||
@@ -188,6 +193,10 @@
|
||||
foodItem(
|
||||
:item="ctx.item",
|
||||
:itemCount="userItems.food[ctx.item.key]",
|
||||
:active="currentDraggingFood == ctx.item",
|
||||
@itemDragEnd="onDragEnd()",
|
||||
@itemDragStart="onDragStart($event, ctx.item)",
|
||||
@itemClick="onFoodClicked($event, ctx.item)"
|
||||
)
|
||||
|
||||
b-modal#welcome-modal(
|
||||
@@ -201,12 +210,49 @@
|
||||
h1.page-header(v-once) {{ $t('welcomeStable') }}
|
||||
div.content-text(v-once) {{ $t('welcomeStableText') }}
|
||||
|
||||
b-modal#hatching-modal(
|
||||
:visible="hatchablePet != null",
|
||||
@change="resetHatchablePet($event)"
|
||||
)
|
||||
|
||||
div.content(v-if="hatchablePet")
|
||||
div.potionEggGroup
|
||||
div.potionEggBackground
|
||||
div(:class="'Pet_HatchingPotion_'+hatchablePet.potionKey")
|
||||
div.potionEggBackground
|
||||
div(:class="'Pet_Egg_'+hatchablePet.eggKey")
|
||||
|
||||
h4.title {{ hatchablePet.name }}
|
||||
div.text(v-html="$t('haveHatchablePet', { potion: hatchablePet.potionName, egg: hatchablePet.eggName })")
|
||||
|
||||
span.svg-icon.icon-10(v-html="icons.close", slot="modal-header", @click="closeHatchPetDialog()")
|
||||
|
||||
div(slot="modal-footer")
|
||||
button.btn.btn-primary(@click="hatchPet(hatchablePet)") {{ $t('hatch') }}
|
||||
button.btn.btn-secondary.btn-flat(@click="closeHatchPetDialog()") {{ $t('cancel') }}
|
||||
|
||||
div.foodInfo(ref="dragginFoodInfo")
|
||||
div(v-if="currentDraggingFood != null")
|
||||
div.food-icon(:class="'Pet_Food_'+currentDraggingFood.key")
|
||||
div.popover
|
||||
div.popover-content {{ $t('dragThisFood', {foodName: currentDraggingFood.text() }) }}
|
||||
|
||||
div.foodInfo.mouse(ref="clickFoodInfo", v-if="foodClickMode")
|
||||
div(v-if="currentDraggingFood != null")
|
||||
div.food-icon(:class="'Pet_Food_'+currentDraggingFood.key")
|
||||
div.popover
|
||||
div.popover-content {{ $t('clickOnPetToFeed', {foodName: currentDraggingFood.text() }) }}
|
||||
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.standard-page .clearfix .float-right {
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
.inventory-item-container {
|
||||
padding: 20px;
|
||||
border: 1px solid;
|
||||
@@ -234,10 +280,10 @@
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 2px;
|
||||
background-color: #4e4a57;
|
||||
width: 112px;
|
||||
height: 112px;
|
||||
border-radius: 4px;
|
||||
background-color: #f9f9f9;
|
||||
|
||||
&:first-child {
|
||||
margin-right: 24px;
|
||||
@@ -266,21 +312,24 @@
|
||||
.stable {
|
||||
|
||||
.standard-page {
|
||||
flex: 1;
|
||||
padding-right:0;
|
||||
}
|
||||
|
||||
.drawer-container {
|
||||
// 3% padding + 252px sidebar width
|
||||
left: calc(3% + 252px) !important;
|
||||
}
|
||||
|
||||
.svg-icon.inline.icon-16 {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-slider .items {
|
||||
height: 114px;
|
||||
}
|
||||
|
||||
|
||||
div#welcome-modal {
|
||||
@mixin habitModal() {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
@@ -289,6 +338,14 @@
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
#welcome-modal {
|
||||
@include habitModal();
|
||||
|
||||
.npc_matt {
|
||||
margin: 0 auto 21px auto;
|
||||
}
|
||||
@@ -310,9 +367,43 @@
|
||||
|
||||
width: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
justify-content: center;
|
||||
#hatching-modal {
|
||||
@include habitModal();
|
||||
|
||||
.content {
|
||||
text-align: center;
|
||||
|
||||
margin: 9px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.title {
|
||||
height: 24px;
|
||||
margin-top: 24px;
|
||||
font-family: Roboto;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
font-stretch: condensed;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
color: #4e4a57;
|
||||
}
|
||||
|
||||
.text {
|
||||
height: 60px;
|
||||
font-family: Roboto;
|
||||
font-size: 14px;
|
||||
line-height: 1.43;
|
||||
text-align: center;
|
||||
color: #686274;
|
||||
}
|
||||
|
||||
span.svg-icon.icon-10 {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,6 +411,38 @@
|
||||
background-color: $purple-50;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.last {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.no-focus:focus {
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.popover-content-text {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.foodInfo {
|
||||
position: absolute;
|
||||
left: -500px;
|
||||
|
||||
&.mouse {
|
||||
position: fixed;
|
||||
pointer-events: none
|
||||
}
|
||||
|
||||
.food-icon {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.popover {
|
||||
position: inherit;
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
@@ -337,9 +460,11 @@
|
||||
import _drop from 'lodash/drop';
|
||||
import _flatMap from 'lodash/flatMap';
|
||||
import _throttle from 'lodash/throttle';
|
||||
import _last from 'lodash/last';
|
||||
|
||||
import Item from '../item';
|
||||
import PetItem from './petItem';
|
||||
import MountItem from './mountItem.vue';
|
||||
import FoodItem from './foodItem';
|
||||
import Drawer from 'client/components/inventory/drawer';
|
||||
import toggleSwitch from 'client/components/ui/toggleSwitch';
|
||||
@@ -349,6 +474,10 @@
|
||||
|
||||
import ResizeDirective from 'client/directives/resize.directive';
|
||||
import DragDropDirective from 'client/directives/dragdrop.directive';
|
||||
import MouseMoveDirective from 'client/directives/mouseposition.directive';
|
||||
|
||||
import svgInformation from 'assets/svg/information.svg';
|
||||
import svgClose from 'assets/svg/close.svg';
|
||||
|
||||
// TODO Normalize special pets and mounts
|
||||
// import Store from 'client/store';
|
||||
@@ -360,6 +489,7 @@
|
||||
PetItem,
|
||||
Item,
|
||||
FoodItem,
|
||||
MountItem,
|
||||
Drawer,
|
||||
bDropdown,
|
||||
bDropdownItem,
|
||||
@@ -373,6 +503,7 @@
|
||||
directives: {
|
||||
resize: ResizeDirective,
|
||||
drag: DragDropDirective,
|
||||
mousePosition: MouseMoveDirective,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -391,6 +522,17 @@
|
||||
'sortByHatchable',
|
||||
],
|
||||
|
||||
icons: Object.freeze({
|
||||
information: svgInformation,
|
||||
close: svgClose,
|
||||
}),
|
||||
|
||||
highlightPet: '',
|
||||
|
||||
hatchablePet: null,
|
||||
foodClickMode: false,
|
||||
currentDraggingFood: null,
|
||||
|
||||
selectedDrawerTab: 0,
|
||||
availableContentWidth: 0,
|
||||
};
|
||||
@@ -443,7 +585,6 @@
|
||||
petSource: {
|
||||
special: this.content.specialPets,
|
||||
},
|
||||
alwaysHideMissing: true,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -490,7 +631,6 @@
|
||||
petSource: {
|
||||
special: this.content.specialMounts,
|
||||
},
|
||||
alwaysHideMissing: true,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -547,9 +687,15 @@
|
||||
key: specialKey,
|
||||
eggKey,
|
||||
potionKey,
|
||||
pet: this.content[`${type}Info`][specialKey].text(),
|
||||
name: this.content[`${type}Info`][specialKey].text(),
|
||||
isOwned () {
|
||||
return [`${type}s`][this.key] > 0;
|
||||
return userItems[`${type}s`][this.key] > 0;
|
||||
},
|
||||
mountOwned () {
|
||||
return userItems.mounts[this.key] > 0;
|
||||
},
|
||||
isAllowedToFeed () {
|
||||
return type === 'pet' && this.isOwned() && !this.mountOwned();
|
||||
},
|
||||
isHatchable () {
|
||||
return false;
|
||||
@@ -574,6 +720,12 @@
|
||||
isOwned () {
|
||||
return userItems[`${type}s`][animalKey] > 0;
|
||||
},
|
||||
mountOwned () {
|
||||
return userItems.mounts[this.key] > 0;
|
||||
},
|
||||
isAllowedToFeed () {
|
||||
return type === 'pet' && this.isOwned() && !this.mountOwned();
|
||||
},
|
||||
isHatchable () {
|
||||
return userItems.eggs[egg.key] > 0 && userItems.hatchingPotions[potion.key] > 0;
|
||||
},
|
||||
@@ -594,7 +746,7 @@
|
||||
let withProgress = isPetList && animalGroup.key !== 'specialPets';
|
||||
|
||||
// 1. Filter
|
||||
if (hideMissing || animalGroup.alwaysHideMissing) {
|
||||
if (hideMissing) {
|
||||
animals = _filter(animals, (a) => {
|
||||
return a.isOwned();
|
||||
});
|
||||
@@ -630,7 +782,7 @@
|
||||
|
||||
let animalRows = [];
|
||||
|
||||
let itemsPerRow = Math.floor(availableSpace / (94 + 24));
|
||||
let itemsPerRow = Math.floor(availableSpace / (94 + 20));
|
||||
|
||||
let rowsToShow = isOpen ? Math.ceil(animals.length / itemsPerRow) : 1;
|
||||
|
||||
@@ -647,6 +799,11 @@
|
||||
};
|
||||
}) : row;
|
||||
|
||||
let lastRowItem = _last(rowWithProgressData);
|
||||
if (lastRowItem) {
|
||||
lastRowItem.isLastInRow = true;
|
||||
}
|
||||
|
||||
animalRows.push(...rowWithProgressData);
|
||||
}
|
||||
|
||||
@@ -679,6 +836,10 @@
|
||||
return `Pet Pet-${pet.key}`;
|
||||
}
|
||||
|
||||
if (pet.mountOwned()) {
|
||||
return `GreyedOut Pet Pet-${pet.key}`;
|
||||
}
|
||||
|
||||
if (pet.isHatchable()) {
|
||||
return 'PixelPaw';
|
||||
}
|
||||
@@ -707,14 +868,80 @@
|
||||
this.$store.dispatch('common:hatch', {egg: pet.eggKey, hatchingPotion: pet.potionKey});
|
||||
},
|
||||
|
||||
onDragStart (ev, food) {
|
||||
this.currentDraggingFood = food;
|
||||
|
||||
let itemRef = this.$refs.dragginFoodInfo;
|
||||
|
||||
let dragEvent = ev.event;
|
||||
|
||||
dragEvent.dataTransfer.setDragImage(itemRef, -20, -20);
|
||||
},
|
||||
|
||||
onDragOver (ev, pet) {
|
||||
if (this.userItems.mounts[pet.key]) {
|
||||
if (!pet.isAllowedToFeed()) {
|
||||
ev.dropable = false;
|
||||
} else {
|
||||
this.highlightPet = pet.key;
|
||||
}
|
||||
},
|
||||
|
||||
onDrop (ev, pet) {
|
||||
this.$store.dispatch('common:feed', {pet: pet.key, food: ev.draggingKey});
|
||||
|
||||
this.highlightPet = '';
|
||||
},
|
||||
|
||||
onDragEnd () {
|
||||
this.currentDraggingFood = null;
|
||||
this.highlightPet = '';
|
||||
},
|
||||
|
||||
onDragLeave () {
|
||||
this.highlightPet = '';
|
||||
},
|
||||
|
||||
petClicked (pet) {
|
||||
if (this.currentDraggingFood !== null && pet.isAllowedToFeed()) {
|
||||
// food process
|
||||
this.$store.dispatch('common:feed', {pet: pet.key, food: this.currentDraggingFood.key});
|
||||
this.currentDraggingFood = null;
|
||||
this.foodClickMode = false;
|
||||
} else {
|
||||
if (pet.isOwned() || !pet.isHatchable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// opens the hatch dialog
|
||||
this.hatchablePet = pet;
|
||||
}
|
||||
},
|
||||
|
||||
closeHatchPetDialog () {
|
||||
this.$root.$emit('hide::modal', 'hatching-modal');
|
||||
},
|
||||
|
||||
resetHatchablePet ($event) {
|
||||
if (!$event) {
|
||||
this.hatchablePet = null;
|
||||
}
|
||||
},
|
||||
|
||||
onFoodClicked ($event, food) {
|
||||
if (this.currentDraggingFood === null || this.currentDraggingFood !== food) {
|
||||
this.currentDraggingFood = food;
|
||||
this.foodClickMode = true;
|
||||
} else {
|
||||
this.currentDraggingFood = null;
|
||||
this.foodClickMode = false;
|
||||
}
|
||||
},
|
||||
|
||||
mouseMoved ($event) {
|
||||
if (this.foodClickMode) {
|
||||
this.$refs.clickFoodInfo.style.left = `${$event.x + 20}px`;
|
||||
this.$refs.clickFoodInfo.style.top = `${$event.y + 20}px`;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
45
website/client/components/inventory/stable/mountItem.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template lang="pug">
|
||||
b-popover(
|
||||
:triggers="[showPopover?'hover':'']",
|
||||
:placement="popoverPosition",
|
||||
)
|
||||
span(slot="content")
|
||||
slot(name="popoverContent", :item="item")
|
||||
|
||||
.item-wrapper
|
||||
.item(
|
||||
:class="{'item-empty': emptyItem}",
|
||||
)
|
||||
slot(name="itemBadge", :item="item")
|
||||
span.item-content(:class="itemContentClass")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import bPopover from 'bootstrap-vue/lib/components/popover';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
bPopover,
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
},
|
||||
itemContentClass: {
|
||||
type: String,
|
||||
},
|
||||
emptyItem: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
popoverPosition: {
|
||||
type: String,
|
||||
default: 'bottom',
|
||||
},
|
||||
showPopover: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -6,19 +6,14 @@ b-popover(
|
||||
span(slot="content")
|
||||
slot(name="popoverContent", :item="item")
|
||||
|
||||
.item-wrapper
|
||||
.item-wrapper(@click="click()")
|
||||
.item(
|
||||
:class="{'item-empty': emptyItem}",
|
||||
@mouseup="holdStop",
|
||||
@mouseleave="holdStop",
|
||||
@mousedown.left="holdStart"
|
||||
:class="{'item-empty': emptyItem, 'highlight': highlightBorder}",
|
||||
)
|
||||
slot(name="itemBadge", :item="item")
|
||||
span.item-content(:class="itemContentClass")
|
||||
span.pet-progress-background(v-if="progress > 0")
|
||||
span.pet-progress-background(v-if="item.isAllowedToFeed() && progress > 0")
|
||||
div.pet-progress-bar(v-bind:style="{width: 100 * progress/50 + '%' }")
|
||||
span.pet-progress-background(v-if="holdProgress > 0")
|
||||
div.pet-progress-bar.hold(v-bind:style="{width: 100 * holdProgress/5 + '%' }")
|
||||
span.item-label(v-if="label") {{ label }}
|
||||
</template>
|
||||
|
||||
@@ -36,15 +31,10 @@ b-popover(
|
||||
height: 4px;
|
||||
background-color: #24cc8f;
|
||||
}
|
||||
|
||||
.pet-progress-bar.hold {
|
||||
background-color: #54c3cc;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import bPopover from 'bootstrap-vue/lib/components/popover';
|
||||
import {mapState} from 'client/libs/store';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -68,6 +58,10 @@ b-popover(
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
highlightBorder: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
popoverPosition: {
|
||||
type: String,
|
||||
default: 'bottom',
|
||||
@@ -77,40 +71,9 @@ b-popover(
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
holdProgress: -1,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
ATTRIBUTES: 'constants.ATTRIBUTES',
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
holdStart () {
|
||||
let pet = this.item;
|
||||
if (pet.isOwned() || !pet.isHatchable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.holdProgress = 1;
|
||||
|
||||
this.currentHoldingTimer = setInterval(() => {
|
||||
if (this.holdProgress === 5) {
|
||||
this.holdStop();
|
||||
this.$emit('hatchPet', pet);
|
||||
}
|
||||
|
||||
this.holdProgress += 1;
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
holdStop () {
|
||||
if (this.currentHoldingTimer) {
|
||||
clearInterval(this.currentHoldingTimer);
|
||||
this.holdProgress = -1;
|
||||
}
|
||||
click () {
|
||||
this.$emit('click', {});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
218
website/client/components/userMenu/inbox.vue
Normal file
@@ -0,0 +1,218 @@
|
||||
<template lang="pug">
|
||||
b-modal#inbox-modal(title="", :hide-footer="true", size='lg')
|
||||
.header-wrap.container(slot="modal-header")
|
||||
.row
|
||||
.col-4
|
||||
.row
|
||||
.col-2
|
||||
.svg-icon.envelope(v-html="icons.messageIcon")
|
||||
.col-6
|
||||
h2.text-center(v-once) {{$t('messages')}}
|
||||
// @TODO: Implement this after we fix username bug
|
||||
// .col-2.offset-1
|
||||
// button.btn.btn-secondary(@click='toggleClick()') +
|
||||
// .col-8.to-form(v-if='displayCreate')
|
||||
// strong To:
|
||||
// b-form-input
|
||||
.row
|
||||
.col-4.sidebar
|
||||
.search-section
|
||||
b-form-input(:placeholder="$t('search')", v-model='search')
|
||||
.empty-messages.text-center(v-if='filtersConversations.length === 0')
|
||||
.svg-icon.envelope(v-html="icons.messageIcon")
|
||||
h4(v-once) {{$t('emptyMessagesLine1')}}
|
||||
p(v-once) {{$t('emptyMessagesLine2')}}
|
||||
.conversations(v-if='filtersConversations.length > 0')
|
||||
.conversation(v-for='conversation in conversations', @click='selectConversation(conversation.key)', :class="{active: selectedConversation === conversation.key}")
|
||||
div
|
||||
span {{conversation.name}}
|
||||
span.timeago {{conversation.date}}
|
||||
div {{conversation.lastMessageText}}
|
||||
.col-8.messages
|
||||
.message(v-for='message in currentMessages') {{message.text}}
|
||||
|
||||
// @TODO: Implement new message header here when we fix the above
|
||||
|
||||
.new-message-row(v-if='selectedConversation')
|
||||
b-form-input(v-model='newMessage')
|
||||
button.btn.btn-secondary(@click='sendPrivateMessage()') Send
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.envelope {
|
||||
color: $gray-400 !important;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: .5em;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
background-color: $gray-700;
|
||||
min-height: 600px;
|
||||
padding: 0;
|
||||
|
||||
.search-section {
|
||||
padding: 1em;
|
||||
box-shadow: 0 1px 2px 0 rgba(26, 24, 29, 0.24);
|
||||
}
|
||||
}
|
||||
|
||||
.messages {
|
||||
position: relative;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.to-form input {
|
||||
width: 60%;
|
||||
display: inline-block;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.empty-messages {
|
||||
margin-top: 10em;
|
||||
color: $gray-400;
|
||||
padding: 1em;
|
||||
|
||||
h4 {
|
||||
color: $gray-400;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.envelope {
|
||||
width: 30px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.new-message-row {
|
||||
background-color: $gray-700;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
height: 88px;
|
||||
width: 100%;
|
||||
padding: 1em;
|
||||
|
||||
input {
|
||||
display: inline-block;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
button {
|
||||
box-shadow: none;
|
||||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.conversation {
|
||||
padding: 1.5em;
|
||||
background: $white;
|
||||
height: 80px;
|
||||
|
||||
.timeago {
|
||||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.conversation.active {
|
||||
border: 1px solid $purple-400;
|
||||
}
|
||||
|
||||
.conversation:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import filter from 'lodash/filter';
|
||||
import { mapState } from 'client/libs/store';
|
||||
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
import bFormInput from 'bootstrap-vue/lib/components/form-input';
|
||||
|
||||
import messageIcon from 'assets/svg/message.svg';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
bModal,
|
||||
bFormInput,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
messageIcon,
|
||||
}),
|
||||
displayCreate: true,
|
||||
selectedConversation: '',
|
||||
search: '',
|
||||
newMessage: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
conversations () {
|
||||
let conversations = {};
|
||||
for (let messageId in this.user.inbox.messages) {
|
||||
let message = this.user.inbox.messages[messageId];
|
||||
let userId = message.uuid;
|
||||
|
||||
if (!this.selectedConversation) this.selectedConversation = userId;
|
||||
|
||||
if (!conversations[userId]) {
|
||||
conversations[userId] = {
|
||||
name: message.user,
|
||||
key: userId,
|
||||
messages: [],
|
||||
};
|
||||
}
|
||||
|
||||
conversations[userId].messages.push({
|
||||
text: message.text,
|
||||
timestamp: message.timestamp,
|
||||
});
|
||||
conversations[userId].lastMessageText = message.text;
|
||||
conversations[userId].date = moment(new Date(message.timestamp)).fromNow();
|
||||
}
|
||||
|
||||
return conversations;
|
||||
},
|
||||
currentMessages () {
|
||||
if (!this.selectedConversation) return;
|
||||
return this.conversations[this.selectedConversation].messages;
|
||||
},
|
||||
filtersConversations () {
|
||||
if (!this.search) return Object.values(this.conversations);
|
||||
return filter(this.conversations, (conversation) => {
|
||||
return conversation.name.toLowerCase().indexOf(this.search.toLowerCase()) !== -1;
|
||||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleClick () {
|
||||
this.displayCreate = !this.displayCreate;
|
||||
},
|
||||
selectConversation (key) {
|
||||
this.selectedConversation = key;
|
||||
},
|
||||
sendPrivateMessage () {
|
||||
this.$store.dispatch('members:sendPrivateMessage', {
|
||||
toUserId: this.selectedConversation,
|
||||
message: this.newMessage,
|
||||
});
|
||||
|
||||
this.conversations[this.selectedConversation].messages.push({
|
||||
text: this.newMessage,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
this.conversations[this.selectedConversation].lastMessageText = this.newMessage;
|
||||
this.conversations[this.selectedConversation].date = new Date();
|
||||
|
||||
this.newMessage = '';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -7,12 +7,15 @@ import _without from 'lodash/without';
|
||||
* DRAG_GROUP is a static custom value
|
||||
* KEY_OF_ITEM
|
||||
*
|
||||
* v-drag.DRAG_GROUP="KEY_OF_ITEM"
|
||||
* v-drag.drop.DRAG_GROUP="KEY_OF_ITEM" @dropped="callback" @dragover="optional"
|
||||
* v-drag.DRAG_GROUP="KEY_OF_ITEM" @itemDragEnd="optional" @itemDragStart="optional"
|
||||
* v-drag.drop.DRAG_GROUP="KEY_OF_ITEM" @itemDropped="callback" @itemDragOver="optional"
|
||||
*/
|
||||
|
||||
const DROPPED_EVENT_NAME = 'dropped';
|
||||
const DRAGOVER_EVENT_NAME = 'dragover';
|
||||
const DROPPED_EVENT_NAME = 'itemDropped';
|
||||
const DRAGSTART_EVENT_NAME = 'itemDragStart';
|
||||
const DRAGEND_EVENT_NAME = 'itemDragEnd';
|
||||
const DRAGOVER_EVENT_NAME = 'itemDragOver';
|
||||
const DRAGLEAVE_EVENT_NAME = 'itemDragLeave';
|
||||
|
||||
export default {
|
||||
bind (el, binding, vnode) {
|
||||
@@ -24,13 +27,28 @@ export default {
|
||||
el.draggable = true;
|
||||
el.handleDrag = (ev) => {
|
||||
ev.dataTransfer.setData('KEY', binding.value);
|
||||
let dragStartEventData = {
|
||||
event: ev,
|
||||
};
|
||||
|
||||
emit(vnode, DRAGSTART_EVENT_NAME, dragStartEventData);
|
||||
};
|
||||
el.addEventListener('dragstart', el.handleDrag);
|
||||
|
||||
el.handleDragEnd = () => {
|
||||
let dragEndEventData = {};
|
||||
|
||||
emit(vnode, DRAGEND_EVENT_NAME, dragEndEventData);
|
||||
};
|
||||
|
||||
|
||||
el.addEventListener('dragend', el.handleDrag);
|
||||
} else {
|
||||
el.handleDragOver = (ev) => {
|
||||
let dragOverEventData = {
|
||||
dropable: true,
|
||||
draggingKey: ev.dataTransfer.getData('KEY'),
|
||||
event: ev,
|
||||
};
|
||||
|
||||
emit(vnode, DRAGOVER_EVENT_NAME, dragOverEventData);
|
||||
@@ -47,16 +65,23 @@ export default {
|
||||
emit(vnode, DROPPED_EVENT_NAME, dropEventData);
|
||||
};
|
||||
|
||||
el.handleDragLeave = () => {
|
||||
emit(vnode, DRAGLEAVE_EVENT_NAME, {});
|
||||
};
|
||||
|
||||
el.addEventListener('dragover', el.handleDragOver);
|
||||
el.addEventListener('dragleave', el.handleDragLeave);
|
||||
el.addEventListener('drop', el.handleDrop);
|
||||
}
|
||||
},
|
||||
|
||||
unbind (el) {
|
||||
if (!el.isDropHandler) {
|
||||
el.removeEventListener('drag', el.handleDrag);
|
||||
el.removeEventListener('dragstart', el.handleDrag);
|
||||
el.removeEventListener('dragend', el.handleDragEnd);
|
||||
} else {
|
||||
el.removeEventListener('dragover', el.handleDragOver);
|
||||
el.removeEventListener('dragleave', el.handleDragLeave);
|
||||
el.removeEventListener('drop', el.handleDrop);
|
||||
}
|
||||
},
|
||||
|
||||
31
website/client/directives/mouseposition.directive.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import Vue from 'vue';
|
||||
|
||||
import _throttle from 'lodash/throttle';
|
||||
|
||||
import { emit } from './directive.common';
|
||||
|
||||
/**
|
||||
* v-mousePosition="throttleTimeout", @mouseMoved="callback()"
|
||||
*/
|
||||
|
||||
const EVENT_NAME = 'mouseMoved';
|
||||
|
||||
export default {
|
||||
bind (el, binding, vnode) {
|
||||
el.handleMouseMove = _throttle((ev) => {
|
||||
emit(vnode, EVENT_NAME, {
|
||||
x: ev.clientX,
|
||||
y: ev.clientY,
|
||||
});
|
||||
}, binding.value);
|
||||
|
||||
window.addEventListener('mousemove', el.handleMouseMove);
|
||||
|
||||
// send the first width
|
||||
Vue.nextTick(el.handleWindowResize);
|
||||
},
|
||||
|
||||
unbind (el) {
|
||||
window.removeEventListener('mousemove', el.handleMouseMove);
|
||||
},
|
||||
};
|
||||
@@ -12,6 +12,9 @@ import Page from './components/page';
|
||||
const Home = () => import(/* webpackChunkName: "static" */'./components/static/home');
|
||||
const RegisterLogin = () => import(/* webpackChunkName: "auth" */'./components/auth/registerLogin');
|
||||
|
||||
const CreatorIntro = () => import(/* webpackChunkName: "creator" */'./components/creatorIntro');
|
||||
|
||||
// Except for tasks that are always loaded all the other main level
|
||||
// All the main level
|
||||
// components are loaded in separate webpack chunks.
|
||||
// See https://webpack.js.org/guides/code-splitting-async/
|
||||
@@ -32,7 +35,7 @@ const InboxConversationPage = () => import(/* webpackChunkName: "inbox" */ './co
|
||||
|
||||
// Guilds
|
||||
const GuildIndex = () => import(/* webpackChunkName: "guilds" */ './components/guilds/index');
|
||||
// const TavernPage = () => import(/* webpackChunkName: "guilds" */ './components/guilds/tavern');
|
||||
const TavernPage = () => import(/* webpackChunkName: "guilds" */ './components/guilds/tavern');
|
||||
const MyGuilds = () => import(/* webpackChunkName: "guilds" */ './components/guilds/myGuilds');
|
||||
const GuildsDiscoveryPage = () => import(/* webpackChunkName: "guilds" */ './components/guilds/discovery');
|
||||
const GuildPage = () => import(/* webpackChunkName: "guilds" */ './components/guilds/guild');
|
||||
@@ -50,6 +53,7 @@ const router = new VueRouter({
|
||||
},
|
||||
// requiresLogin is true by default, isStatic false
|
||||
routes: [
|
||||
{ name: 'creator', path: '/creator', component: CreatorIntro },
|
||||
{ name: 'home', path: '/home', component: Home, meta: {requiresLogin: false} },
|
||||
{ name: 'register', path: '/register', component: RegisterLogin, meta: {requiresLogin: false} },
|
||||
{ name: 'login', path: '/login', component: RegisterLogin, meta: {requiresLogin: false} },
|
||||
@@ -69,7 +73,7 @@ const router = new VueRouter({
|
||||
path: '/guilds',
|
||||
component: GuildIndex,
|
||||
children: [
|
||||
{ name: 'tavern', path: 'tavern', component: GuildPage },
|
||||
{ name: 'tavern', path: 'tavern', component: TavernPage },
|
||||
{
|
||||
name: 'myGuilds',
|
||||
path: 'myGuilds',
|
||||
|
||||
@@ -25,4 +25,8 @@ export function set (store, changes) {
|
||||
// TODO
|
||||
// .then((res) => console.log('set', res))
|
||||
// .catch((err) => console.error('set', err));
|
||||
}
|
||||
}
|
||||
|
||||
export function sleep () {
|
||||
// @TODO: Implemented
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"user": "User",
|
||||
"market": "Market",
|
||||
"groupPlansTitle": "Group Plans",
|
||||
"newGroupTitle": "New Group",
|
||||
"subscriberItem": "Mystery Item",
|
||||
"newSubscriberItem": "New Mystery Item",
|
||||
"subscriberItemText": "Each month, subscribers will receive a mystery item. This is usually released about one week before the end of the month. See the wiki's 'Mystery Item' page for more information.",
|
||||
|
||||
@@ -229,7 +229,7 @@
|
||||
"yourTaskHasBeenApproved": "Your task \"<%= taskText %>\" has been approved",
|
||||
"userHasRequestedTaskApproval": "<%= user %> has requested task approval for <%= taskName %>",
|
||||
"approve": "Approve",
|
||||
"approvalTitle": "<%= text %> for user: <%= userName %>",
|
||||
"approvalTitle": "<%= userName %> has completed <%= type %>: \"<%= text %>\"",
|
||||
"confirmTaskApproval": "Do you want to reward <%= username %> for completing this task?",
|
||||
"groupSubscriptionPrice": "$9 every month + $3 a month for every additional group member",
|
||||
"groupAdditionalUserCost": " +$3.00/month/user",
|
||||
@@ -251,7 +251,18 @@
|
||||
"groupBenefitSevenTitle": "Get a brand-new exclusive Jackalope Mount",
|
||||
"groupBenefitEightTitle": "Add Group Managers to help manage tasks",
|
||||
"groupBenefitEightDescription": "Want to share your group's responsibilities? Promote people to Group Managers to help the Leader add, assign, and approve tasks!",
|
||||
"groupBenefitMessageLimitTitle": "Increase message limit",
|
||||
"groupBenefitMessageLimitDescription": "Your message limit is doubled to house up to 400 messages at a time!",
|
||||
"teamBasedTasks": "Team-based Tasks",
|
||||
"specializedCommunication": "Specialized Communication",
|
||||
"funExtras": "Fun Extras",
|
||||
"enterprisePlansButton": "Ask about Enterprise Plans",
|
||||
"enterprisePlansDescription": "Looking for a larger install with custom needs? See if our enterprise plans are right for you.",
|
||||
"enterprisePlansEmailSubject": "Question regarding Enterprise Plans",
|
||||
"familyPlansButton": "Sign Up for Family Plan Mailing List",
|
||||
"familyPlansDescription": "Want a cozier solution to manage your household? Family Plans are coming soon!",
|
||||
"createAGroup": "Create a Group",
|
||||
"getAGroupPlanToday": "Get a Group Plan Today",
|
||||
"assignFieldPlaceholder": "Type a group member's profile name",
|
||||
"cannotDeleteActiveGroup": "You cannot remove a group with an active subscription",
|
||||
"groupTasksTitle": "Group Tasks List",
|
||||
@@ -286,5 +297,7 @@
|
||||
"leaderMarker": " - Leader",
|
||||
"managerMarker": " - Manager",
|
||||
"joinedGuild": "Joined a Guild",
|
||||
"joinedGuildText": "Ventured into the social side of Habitica by joining a Guild!"
|
||||
"joinedGuildText": "Ventured into the social side of Habitica by joining a Guild!",
|
||||
"badAmountOfGemsToPurchase": "Amount must be at least 1.",
|
||||
"groupPolicyCannotGetGems": "The policy of one group you're part of prevents its members from obtaining gems."
|
||||
}
|
||||
|
||||
@@ -35,14 +35,17 @@
|
||||
"groupBy2": "Group By",
|
||||
"quantity": "Quantity",
|
||||
"AZ": "A-Z",
|
||||
"costumeDisabled": "You have disabled your costume.",
|
||||
"filterByStandard": "Standard",
|
||||
"filterByMagicPotion": "Magin Potion",
|
||||
"filterByMagicPotion": "Magic Potion",
|
||||
"filterByQuest": "Quest",
|
||||
"standard": "Standard",
|
||||
"sortByColor": "Color",
|
||||
"sortByHatchable": "Hatchable",
|
||||
"haveHatchablePet": "You have a <%= potion %> and <%= egg %> to hatch this pet! <b>Click and hold</b> the paw print to hatch.",
|
||||
"hatch": "Hatch!",
|
||||
"dragThisFood": "Drag this <%= foodName %> to a Pet and watch it grow!",
|
||||
"clickOnPetToFeed": "Click on a Pet to feed <%= foodName %> and watch it grow!",
|
||||
"dragThisPotion": "Drag this <%= potionName %> to an Egg and hatch a new pet!",
|
||||
"clickOnEggToHatch": "Click on an Egg to use your <%= potionName %> and hatch a new pet!",
|
||||
"editAvatar": "Edit Avatar",
|
||||
"sort": "Sort",
|
||||
"memberCount": "Member Count",
|
||||
@@ -127,5 +130,58 @@
|
||||
"noFoodAvailable": "You don't have any food.",
|
||||
"gotIt": "Got it!",
|
||||
"welcomeStable": "Welcome to the Stable!",
|
||||
"welcomeStableText": "I'm Matt, the Beast Master. Starting at level 3, you can hatch Pets from Eggs by using Potions you find! When you hatch a Pet from your Inventory, it will appear here! Click a Pet's to add it to your avatar. Feed them with the Food you find in your Inventory after level 3, and they'll grow into hardy Mounts."
|
||||
"welcomeStableText": "I'm Matt, the Beast Master. Starting at level 3, you can hatch Pets from Eggs by using Potions you find! When you hatch a Pet from your Inventory, it will appear here! Click a Pet's to add it to your avatar. Feed them with the Food you find in your Inventory after level 3, and they'll grow into hardy Mounts.",
|
||||
"excercise": "Excercise",
|
||||
"creativity": "Creativity",
|
||||
"budgeting": "Budgeting",
|
||||
"petLikeToEatText": "Pets will grow no matter what you feed them, but they'll grow faster if you feed them the one food that they like best. Experiment to find out the pattern, or see the answers here: <br/> <a href=\"http://habitica.wikia.com/wiki/Food_Preferences\" target=\"_blank\">http://habitica.wikia.com/wiki/Food_Preferences</a>",
|
||||
"petLikeToEat": "What does my pet like to eat?",
|
||||
"welcomeTo": "Welcome to",
|
||||
"justinIntroMessage1": "Hello there! You must be new here. My name is Justin, I’ll be your guide in Habitica.",
|
||||
"justinIntroMessage2": "To start, you’ll need to create an avatar.",
|
||||
"justinIntroMessage3": "Great! Now, what are you interested in working on throughout this journey?",
|
||||
"prev": "Prev",
|
||||
"next": "Next",
|
||||
"randomize": "Randomize",
|
||||
"skin": "Skin",
|
||||
"hair": "Hair",
|
||||
"extra": "Extra",
|
||||
"size": "Size",
|
||||
"shirt": "Shirt",
|
||||
"bangs": "Bangs",
|
||||
"ponytail": "Ponytail",
|
||||
"glasses": "Glasses",
|
||||
"welcomeToTavern": "Welcome to The Tavern!",
|
||||
"communityGuidelinesIntro": "Habitica tries to create a welcoming environment for users of all ages and backgrounds, especially in public spaces like the Tavern. If you have any questions, please consult our Community Guidelines.",
|
||||
"acceptCommunityGuidelines": "I agree to follow the Community Guidelines",
|
||||
"sleepDescription": "Need a break? Check into Daniel’s Inn to pause some of Habitica’s more difficult game mechanics:",
|
||||
"sleepBullet1": "Missed Dailies won’t damage you",
|
||||
"sleepBullet2": "Tasks won’t lose streaks or decay in color",
|
||||
"sleepBullet3": "Bosses won’t do damage for your missed Dailies",
|
||||
"sleepBullet4": "Your boss damage or collection Quest items will stay pending until check-out",
|
||||
"pauseDailies": "Pause Damage",
|
||||
"unpauseDailies": "Unpause Damage",
|
||||
"staffAndModerators": "Staff and Moderators",
|
||||
"helpfulLinks": "Helpful Links",
|
||||
"communityGuidelinesLink": "Community Guidelines",
|
||||
"lookingForGroup": "Looking for Group (Party Wanted) Posts",
|
||||
"faq": "FAQ",
|
||||
"dataDisplayTool": "Data Display Tool",
|
||||
"reportProblem": "Report a Problem",
|
||||
"requestFeature": "Request a Feature",
|
||||
"askQuestionGuild": "Ask a Question (Habitica Help guild)",
|
||||
"playerTiersDesc": "The colored usernames you see in chat represent a person’s contributor tier. The higher the tier, the more the person has contributed to habitica through art, code, the community, or more!",
|
||||
"tier1": "Tier 1 (Friend)",
|
||||
"tier2": "Tier 2 (Friend)",
|
||||
"tier3": "Tier 3 (Elite)",
|
||||
"tier4": "Tier 4 (Elite)",
|
||||
"tier5": "Tier 5 (Champion)",
|
||||
"tier6": "Tier 6 (Champion)",
|
||||
"tier7": "Tier 7 (Legendary)",
|
||||
"tierModerator": "Moderator (Guardian)",
|
||||
"tierStaff": "Staff (Heroic)",
|
||||
"tierNPC": "NPC",
|
||||
"messages": "Messages",
|
||||
"emptyMessagesLine1": "You don’t have any messages",
|
||||
"emptyMessagesLine2": "Send a message to start a conversation!"
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
"displayNow": "Display Now",
|
||||
"displayLater": "Display Later",
|
||||
"petNotOwned": "You do not own this pet.",
|
||||
"mountNotOwned": "You do not own this mount.",
|
||||
"earnedCompanion": "With all your productivity, you've earned a new companion. Feed it to make it grow!",
|
||||
"feedPet": "Feed <%= article %><%= text %> to your <%= name %>?",
|
||||
"useSaddle": "Saddle <%= pet %>?",
|
||||
|
||||
@@ -439,7 +439,7 @@
|
||||
"questTaskwoodsTerror3DropWeapon": "Taskwoods Lantern (Two-Handed Weapon)",
|
||||
|
||||
"questFerretText": "The Nefarious Ferret",
|
||||
"questFerretNotes": "Walking through Habit City, you see an unhappy crowd surrounding a red-robed Ferret.<br><br>\"That productivity potion you sold me is useless!\" @Beffymaroo complains. \"I watched three hours of TV last night instead of doing my chores!\"<br><br>\"Yeah!\" shouts @Pandah. \"And today I spent an hour rearranging my books instead of reading them!\"<br><br>The Nefarious Ferret spreads his hands innocently. \"That's more TV watching and book organizing than you'd normally get done, isnt it?\"<br><br>The crowd erupts in anger.<br><br>\"No refunds!\" crows the Nefarious Ferret. He fires a bolt of magic into the crowd, preparing to escape in the smoke.<br><br>\"Please, Habitican!\" @Faye says, grabbing your arm. \"Defeat the ferret and make him refund his dishonest earnings!\"",
|
||||
"questFerretNotes": "Walking through Habit City, you see an unhappy crowd surrounding a red-robed Ferret.<br><br>\"That productivity potion you sold me is useless!\" @Beffymaroo complains. \"I watched three hours of TV last night instead of doing my chores!\"<br><br>\"Yeah!\" shouts @Pandah. \"And today I spent an hour rearranging my books instead of reading them!\"<br><br>The Nefarious Ferret spreads his hands innocently. \"That's more TV watching and book organizing than you'd normally get done, isn't it?\"<br><br>The crowd erupts in anger.<br><br>\"No refunds!\" crows the Nefarious Ferret. He fires a bolt of magic into the crowd, preparing to escape in the smoke.<br><br>\"Please, Habitican!\" @Faye says, grabbing your arm. \"Defeat the ferret and make him refund his dishonest earnings!\"",
|
||||
"questFerretCompletion": "You defeat the soft-furred swindler and @UncommonCriminal gives the crowd their refunds. There's even a little gold left over for you. Plus, it looks like the Nefarious Ferret dropped some eggs in his hurry to get away!",
|
||||
"questFerretBoss": "Nefarious Ferret",
|
||||
"questFerretDropFerretEgg": "Ferret (Egg)",
|
||||
@@ -494,7 +494,7 @@
|
||||
"questStoikalmCalamity1DropArmor": "Mammoth Rider Armor",
|
||||
|
||||
"questStoikalmCalamity2Text": "Stoïkalm Calamity, Part 2: Seek the Icicle Caverns",
|
||||
"questStoikalmCalamity2Notes": "The stately hall of the Mammoth Riders is an austere masterpiece of architecture, but it is also entirely empty. There's no furniture, the weapons are missing, and even the columns were picked clean of their inlays.<br><br>\"Those skulls scoured the place,\" Lady Glaciate says, and there is a blizzard brewing in her tone. \"Humiliating. Not a soul is to mention this to the April Fool, or I will never hear the end of it.\"<br><br>\"How mysterious!\" says @Beffymaroo. \"But where did they--\"<br><br>\"The icicle drake caverns.\" Lady Glaciate gestures at shining coins spilled in the snow outside. \"Sloppy.\"<br><br>\"But aren't icicle drakes honorable creatures with their own treasure hoards?\" @Beffymaroo asks. \"Why would they possibly--\"<br><br>\"Mind control,\" says Lady Glaciate, utterly unphased. \"Or something equally melodramatic and inconvenient.\" She begins to stride from the hall. \"Why are you just standing there?\"<br><br>Quickly, go follow the trail of Icicle Coins!",
|
||||
"questStoikalmCalamity2Notes": "The stately hall of the Mammoth Riders is an austere masterpiece of architecture, but it is also entirely empty. There's no furniture, the weapons are missing, and even the columns were picked clean of their inlays.<br><br>\"Those skulls scoured the place,\" Lady Glaciate says, and there is a blizzard brewing in her tone. \"Humiliating. Not a soul is to mention this to the April Fool, or I will never hear the end of it.\"<br><br>\"How mysterious!\" says @Beffymaroo. \"But where did they--\"<br><br>\"The icicle drake caverns.\" Lady Glaciate gestures at shining coins spilled in the snow outside. \"Sloppy.\"<br><br>\"But aren't icicle drakes honorable creatures with their own treasure hoards?\" @Beffymaroo asks. \"Why would they possibly--\"<br><br>\"Mind control,\" says Lady Glaciate, utterly unfazed. \"Or something equally melodramatic and inconvenient.\" She begins to stride from the hall. \"Why are you just standing there?\"<br><br>Quickly, go follow the trail of Icicle Coins!",
|
||||
"questStoikalmCalamity2Completion": "The Icicle Coins lead you straight to the buried entrance of a cleverly hidden cavern. Though the weather outside is calm and lovely, with the sunlight sparkling across the expanse of snow, there is a howling within like a fierce winter wind. Lady Glaciate grimaces and hands you a Mammoth Rider helm. \"Wear this,\" she says. \"You'll need it.\"",
|
||||
"questStoikalmCalamity2CollectIcicleCoins": "Icicle Coins",
|
||||
"questStoikalmCalamity2DropHeadgear": "Mammoth Rider Helm (Headgear)",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"giftSubscriptionText4": "Thanks for supporting Habitica!",
|
||||
"monthUSD": "USD / Month",
|
||||
"organization": "Organization",
|
||||
"groupPlans": "Corporate Plans",
|
||||
"groupPlans": "Group Plans",
|
||||
"indivPlan1": "For individuals, Habitica is free to play. Even for small interest groups, free (or cheap)",
|
||||
"indivPlan2": "can be used to motivate participants in behavioral modification. Think writing groups, art challenges, and more.",
|
||||
"groupText1": "But some group leaders will want more control, privacy, security, and support. Examples of such groups are families, health and wellness groups, employee groups, and more. These plans provide private instances of Habitica for your group or organization, secure and independent of",
|
||||
|
||||
@@ -147,6 +147,10 @@
|
||||
"taskApprovalHasBeenRequested": "Approval has been requested",
|
||||
"approvals": "Approvals",
|
||||
"approvalRequired": "Approval Required",
|
||||
"repeatZero": "Daily is never due",
|
||||
"repeatType": "Repeat Type",
|
||||
"repeatTypeHelpTitle": "What kind of repeat is this?",
|
||||
"repeatTypeHelp": "Select \"Daily\" if you want this task to repeat every day or every third day, etc. Select \"Weekly\"if you want it to repeat on certain days of the week. If you select \"Monthly\" or \"Yearly\", adjust the Start Date to control which day of the month or year the task will be due on.",
|
||||
"weekly": "Weekly",
|
||||
"monthly": "Monthly",
|
||||
"yearly": "Yearly",
|
||||
|
||||
@@ -253,34 +253,34 @@
|
||||
"backgroundMagicBeanstalkNotes": "Mendaki Pohon Kacang Sihir.",
|
||||
"backgroundMeanderingCaveText": "Gua Berkelok-kelok",
|
||||
"backgroundMeanderingCaveNotes": "Jelajahi Gua Berkelok-kelok.",
|
||||
"backgroundMistiflyingCircusText": "Mistiflying Circus",
|
||||
"backgroundMistiflyingCircusNotes": "Carouse in the Mistiflying Circus.",
|
||||
"backgrounds042017": "SET 35: Released April 2017",
|
||||
"backgroundBugCoveredLogText": "Bug-Covered Log",
|
||||
"backgroundBugCoveredLogNotes": "Investigate a Bug-Covered Log.",
|
||||
"backgroundGiantBirdhouseText": "Giant Birdhouse",
|
||||
"backgroundGiantBirdhouseNotes": "Perch in a Giant Birdhouse.",
|
||||
"backgroundMistShroudedMountainText": "Mist-Shrouded Mountain",
|
||||
"backgroundMistShroudedMountainNotes": "Summit a Mist-Shrouded Mountain.",
|
||||
"backgrounds052017": "SET 36: Released May 2017",
|
||||
"backgroundGuardianStatuesText": "Guardian Statues",
|
||||
"backgroundGuardianStatuesNotes": "Stand vigil in front of Guardian Statues.",
|
||||
"backgroundHabitCityStreetsText": "Habit City Streets",
|
||||
"backgroundHabitCityStreetsNotes": "Explore the Streets of Habit City.",
|
||||
"backgroundOnATreeBranchText": "On a Tree Branch",
|
||||
"backgroundOnATreeBranchNotes": "Perch On a Tree Branch.",
|
||||
"backgrounds062017": "SET 37: Released June 2017",
|
||||
"backgroundBuriedTreasureText": "Buried Treasure",
|
||||
"backgroundBuriedTreasureNotes": "Unearth Buried Treasure.",
|
||||
"backgroundOceanSunriseText": "Ocean Sunrise",
|
||||
"backgroundOceanSunriseNotes": "Admire an Ocean Sunrise.",
|
||||
"backgroundSandcastleText": "Sandcastle",
|
||||
"backgroundSandcastleNotes": "Rule over a Sandcastle.",
|
||||
"backgrounds072017": "SET 38: Released July 2017",
|
||||
"backgroundGiantSeashellText": "Giant Seashell",
|
||||
"backgroundGiantSeashellNotes": "Lounge in a Giant Seashell.",
|
||||
"backgroundKelpForestText": "Kelp Forest",
|
||||
"backgroundKelpForestNotes": "Swim through a Kelp Forest.",
|
||||
"backgroundMidnightLakeText": "Midnight Lake",
|
||||
"backgroundMidnightLakeNotes": "Rest by a Midnight Lake."
|
||||
"backgroundMistiflyingCircusText": "Sirkus Mistiflying",
|
||||
"backgroundMistiflyingCircusNotes": "Bersenang-senang di Sirkus Mistiflying",
|
||||
"backgrounds042017": "SET 35: Dirilis April 2017",
|
||||
"backgroundBugCoveredLogText": "Batang Pohon Berserubung Serangga",
|
||||
"backgroundBugCoveredLogNotes": "Investigasi Batang Pohon Berserubung Serangga",
|
||||
"backgroundGiantBirdhouseText": "Rumah Burung Raksasa",
|
||||
"backgroundGiantBirdhouseNotes": "Bertengker di Rumah Burung Raksasa",
|
||||
"backgroundMistShroudedMountainText": "Pegunungan Berserabut Kabut",
|
||||
"backgroundMistShroudedMountainNotes": "Mendaki Pegunungan Berserabut Kabut",
|
||||
"backgrounds052017": "SET 36: Dirilis Mei 2017",
|
||||
"backgroundGuardianStatuesText": "Patung Penunggu",
|
||||
"backgroundGuardianStatuesNotes": "Berjaga-jaga di depan Patung Penunggu",
|
||||
"backgroundHabitCityStreetsText": "Jalanan Kota Habit",
|
||||
"backgroundHabitCityStreetsNotes": "Bereksplorasi di Jalanan Kota Habit",
|
||||
"backgroundOnATreeBranchText": "Di Ranting Pohon",
|
||||
"backgroundOnATreeBranchNotes": "Bertengker di Ranting Pohon",
|
||||
"backgrounds062017": "SET 37: Dirilis Juni 2017",
|
||||
"backgroundBuriedTreasureText": "Harta Karun Tersembunyi",
|
||||
"backgroundBuriedTreasureNotes": "Galilah Harta Karun Tersembunyi",
|
||||
"backgroundOceanSunriseText": "Matahari Terbit di Tepi Lautan",
|
||||
"backgroundOceanSunriseNotes": "Mengagumi Matahari Terbit di Tepi Lautan",
|
||||
"backgroundSandcastleText": "Istana Pasir",
|
||||
"backgroundSandcastleNotes": "Memerintah Sebuah Istana Pasir",
|
||||
"backgrounds072017": "SET 38: Dirilis Juli 2017",
|
||||
"backgroundGiantSeashellText": "Kerang Raksasa",
|
||||
"backgroundGiantSeashellNotes": "Bermalas-malas di Kerang Raksasa",
|
||||
"backgroundKelpForestText": "Hutan Rumput Laut",
|
||||
"backgroundKelpForestNotes": "Berenang di Hutan Rumput Laut",
|
||||
"backgroundMidnightLakeText": "Danau Tengah Malam",
|
||||
"backgroundMidnightLakeNotes": "Beristirahat di Danau Tengah Malam"
|
||||
}
|
||||
@@ -80,6 +80,6 @@
|
||||
"userAlreadyInChallenge": "Pengguna telah berpartisipasi di dalam tantangan ini.",
|
||||
"cantOnlyUnlinkChalTask": "Hanya tugas tantangan rusak yang dapat diputuskan.",
|
||||
"shortNameTooShort": "Nama Label harus setidaknya memiliki 3 karakter.",
|
||||
"joinedChallenge": "Joined a Challenge",
|
||||
"joinedChallengeText": "This user put themselves to the test by joining a Challenge!"
|
||||
"joinedChallenge": "Bergabung dengan sebuah Tantangan",
|
||||
"joinedChallengeText": "User ini telah mengetes dirinya dengan bergabung pada sebuah Tantangan!"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"communityGuidelinesWarning": "Please keep in mind that your Display Name, profile photo, and blurb must comply with the <a href='https://habitica.com/static/community-guidelines' target='_blank'>Community Guidelines</a> (e.g. no profanity, no adult topics, no insults, etc). If you have any questions about whether or not something is appropriate, feel free to email <%= hrefBlankCommunityManagerEmail %>!",
|
||||
"communityGuidelinesWarning": "Ingatlah bahwa Nama Tampilan, foto profil, dan celotehmu harus mengikuti <a href='https://habitica.com/static/community-guidelines' target='_blank'>Pedoman Komunitas</a> (misalnya, tidak menggunakan bahasa senonoh, topik dewasa, penghinaan, dsb). Jika kamu mempunyai pertanyaan apapun mengenai pantasnya sesuatu hal, silahkan email <%= hrefBlankCommunityManagerEmail %>!",
|
||||
"profile": "Profil",
|
||||
"avatar": "Ubah Tampilan Avatar",
|
||||
"other": "Lainnya",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"commGuideList01B": "<strong>Tabiat yang Rajin.</strong> Habiticans bekerja keras untuk memperbaiki hidup mereka, juga untuk membangun dan memperbaiki situs ini secara terus-menerus. Kami adalah proyek open-source, yang berarti kami sebisa mungkin selalu berusaha untuk membuat situs ini menjadi tempat terbaik.",
|
||||
"commGuideList01C": "<strong>Perilaku yang Suportif.</strong> Habiticans senang jika orang lain senang dan saling menghibur pada saat-saat yang sulit. Kita saling menguatkan, bergantung pada satu sama lain, dan belajar pada satu sama lain. Dalam kelompok, kita melakukan hal ini melalui mantera; di ruang chat, kita melakukan ini dengan kata-kata yang ramah dan suportif.",
|
||||
"commGuideList01D": "<strong>Sikap Saling Menghargai.</strong> Kita berasal dari latar belakang yang berbeda, juga memiliki keahlian dan opini yang berbeda. Itulah yang membuat komunitas kita sangat menakjubkan. Habiticans menghargai perbedaan ini, bahkan merayakannya. Jangan pergi ke mana-mana, dan kamu akan segera mendapatkan teman-teman baru dari berbagai lapisan komunitas.",
|
||||
"commGuideHeadingMeet": "Meet the Staff and Mods!",
|
||||
"commGuideHeadingMeet": "Temuilah para Staff dan Moderator!",
|
||||
"commGuidePara006": "Habitica memiliki beberapa ksatria yang bersatu padu dengan anggota staff untuk menjaga komunitas ini tetap damai, puas, dan bebas dari pengganggu. Masing-masing memiliki domain yang spesifik, namun sewaktu-waktu dapat dipanggil untuk membantu di lingkup sosial yang lain. Staff dan Moderator sering kali memulai pernyataan resmi dengan kata-kata \"Mod Talk\" atau \"Mod Hat On\"",
|
||||
"commGuidePara007": "Staf punya warna ungu dengan tanda mahkota. Gelar mereka adalah \"Pahlawan\".",
|
||||
"commGuidePara008": "Moderator memiliki tag biru gelap dengan tanda bintang. Gelar mereka adalah \"Pengawal\", kecuali Bailey, yang merupakan NPC dan memiliki tag hitam-hijau dengan tanda bintang.",
|
||||
|
||||
@@ -152,8 +152,8 @@
|
||||
"questEggButterflyText": "Ulat",
|
||||
"questEggButterflyMountText": "Kupu-kupu",
|
||||
"questEggButterflyAdjective": "a cute",
|
||||
"questEggNudibranchText": "Nudibranch",
|
||||
"questEggNudibranchMountText": "Nudibranch",
|
||||
"questEggNudibranchText": "Nudibranchia",
|
||||
"questEggNudibranchMountText": "Nudibranchia",
|
||||
"questEggNudibranchAdjective": "a nifty",
|
||||
"eggNotes": "Dapatkan obat penetas untuk diberikan pada telur ini, dan ia akan menetas menjadi <%= eggText(locale) %> yang <%= eggAdjective(locale) %>;",
|
||||
"hatchingPotionBase": "Biasa",
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
"surveysMultiple": "Membantu Habitica berkembang sebanyak <%= count %> kali, baik dengan mengisi survey atau membantu dalam pengujian utama. Terima kasih!",
|
||||
"currentSurvey": "Kuisioner Sekarang",
|
||||
"surveyWhen": "Lencana akan diberikan kepada semua peserta ketika survei telah diproses, pada akhir Maret.",
|
||||
"blurbInbox": "This is where your private messages are stored! You can send someone a message by clicking on the envelope icon next to their name in Tavern, Party, or Guild Chat. If you've received an inappropriate PM, you should email a screenshot of it to Lemoness (<%= hrefCommunityManagerEmail %>)",
|
||||
"blurbInbox": "Di sinilah pesan pribadimu disimpan! Kamu dapat mengirimkan pesan ke seseorang dengan menekan ikon amplop di sebelah nama mereka di Kedai Minuman, Party, atau Guild Chat. Jika kamu menerima PM yang tidak pantas, kirimlah screenshot tersebut melalui email ke Lemoness (<%= hrefCommunityManagerEmail %>)",
|
||||
"blurbGuildsPage": "Guild adalah grup obrolan bertopik yang dibuat oleh pengguna, untuk pengguna. Telusuri daftar tema dan bergabunglah dengan guild yang menarik untukmu!",
|
||||
"blurbChallenges": "Tantangan dibuat oleh sesama pemain. Bergabung pada sebuah tantangan akan menambah tugas ke daftarmu, dan memenangkan tantangan akan memberikanmu lencana dan terkadang hadiah permata!",
|
||||
"blurbHallPatrons": "Ini adalah Aula para Pelindung, di mana kami memberi penghormatan pada petualang pemberani yang membantu Kickstarter awal Habitica. Kami berterima kasih kepada mereka yang membantu kami menjadikan Habitica kenyataan!",
|
||||
|
||||
@@ -134,7 +134,7 @@
|
||||
"playButtonFull": "Masuk Habitica",
|
||||
"presskit": "Paket Pres",
|
||||
"presskitDownload": "Mengunduh seluruh gambar:",
|
||||
"presskitText": "Thanks for your interest in Habitica! The following images can be used for articles or videos about Habitica. For more information, please contact Siena Leslie at <%= pressEnquiryEmail %>.",
|
||||
"presskitText": "Terima kasih untuk ketertarikan anda pada Habitica! Gambar-gamar berikut ini dapat digunakan untuk artikel atau video mengenai Habitica. Untuk informasi lebih lanjut, silahkan hubungi Siena Leslie di <%= pressEnquiryEmail %>.",
|
||||
"pkVideo": "Video",
|
||||
"pkPromo": "Promo",
|
||||
"pkLogo": "Logo",
|
||||
@@ -259,7 +259,7 @@
|
||||
"passwordResetEmailHtml": "Kalau kamu meminta reset kata sandi untuk <strong><%= username %></strong> di Habitica, <a href=\"<%= passwordResetLink %>\">klik di sini</a> untuk membuat kata sandi baru. Tautan akan hangus setelah 24 jam.<br/><br>Kalau kamu tidak pernah meminta reset kata sandi, abaikan saja email ini.",
|
||||
"invalidLoginCredentialsLong": "O-oh - nama pengguna atau kata sandi tidak benar.\n- Pastikan nama pengguna atau email diketikkan dengan benar.\n- Kamu mungkin mendaftar dengan menggunakan Facebook, bukan email. Periksa ulang dengan mencoba masuk melalui Facebook.\n- Jika kamu lupa kata sandi, klik \"Lupa Kata Sandi\".",
|
||||
"invalidCredentials": "Tidak ada akun yang menggunakan credential tersebut.",
|
||||
"accountSuspended": "Account has been suspended, please contact <%= communityManagerEmail %> with your User ID \"<%= userId %>\" for assistance.",
|
||||
"accountSuspended": "Akun anda telah disuspend, mohon hubungi <%= communityManagerEmail %> dengan User ID anda \"<%= userId %>\" untuk bantuan.",
|
||||
"unsupportedNetwork": "Jaringan ini saat ini belum didukung.",
|
||||
"cantDetachSocial": "Akun tidak memiliki metode autentikasi lain; tidak dapat memutuskan metode autentikasi ini.",
|
||||
"onlySocialAttachLocal": "Autentikasi lokal dapat ditambahkan hanya pada akun sosial.",
|
||||
|
||||
@@ -802,8 +802,8 @@
|
||||
"headSpecialSpring2017MageNotes": "Questo cappello può aiutarti a lanciare potenti incantesimi... o lo puoi usare per evocare delle palline da tennis. A te la scelta. Aumenta la Percezione di <%= per %>. Edizione limitata, primavera 2017.",
|
||||
"headSpecialSpring2017HealerText": "Tiara di Petali",
|
||||
"headSpecialSpring2017HealerNotes": "Questa delicata corona emana il confortante aroma dei nuovi fiori primaverili. Aumenta l’Intelligenza di <%= int %>. Edizione limitata, primavera 2017.",
|
||||
"headSpecialSummer2017RogueText": "Sea Dragon Helm",
|
||||
"headSpecialSummer2017RogueNotes": "This helm changes colors to help you blend in with your surroundings. Increases Perception by <%= per %>. Limited Edition 2017 Summer Gear.",
|
||||
"headSpecialSummer2017RogueText": "Elmo del Drago Marino",
|
||||
"headSpecialSummer2017RogueNotes": "Questo elmo cambia colore per aiutarti a mimettizarti nell'ambiente. Aumenta la Percezione di <%= per %>. Edizione limitata, estate 2017.",
|
||||
"headSpecialSummer2017WarriorText": "Elmo Castello di Sabbia",
|
||||
"headSpecialSummer2017WarriorNotes": "L'elmo più raffinato che si possa sperare di indossare... per lo meno fino all'arrivo della marea. Aumenta la Forza di <%= str % >. Edizione limitata, estate 2017.",
|
||||
"headSpecialSummer2017MageText": "Whirlpool Hat",
|
||||
|
||||
@@ -270,10 +270,10 @@
|
||||
"backgroundOnATreeBranchText": "Op een Boomtak",
|
||||
"backgroundOnATreeBranchNotes": "Strijk neer op een boomtak.",
|
||||
"backgrounds062017": "SET 37: uitgebracht in juni 2017",
|
||||
"backgroundBuriedTreasureText": "Begraven schat",
|
||||
"backgroundBuriedTreasureNotes": "Graaf begraven schatten op.",
|
||||
"backgroundOceanSunriseText": "Oceaan met zonsopgang",
|
||||
"backgroundOceanSunriseNotes": "Bewonder een oceaan met zonsopgang.",
|
||||
"backgroundBuriedTreasureText": "Begraven Schat",
|
||||
"backgroundBuriedTreasureNotes": "Graaf verborgen schatten op.",
|
||||
"backgroundOceanSunriseText": "Oceaan met Zonsopgang",
|
||||
"backgroundOceanSunriseNotes": "Bewonder een zonsopgang boven de oceaan.",
|
||||
"backgroundSandcastleText": "Zandkasteel",
|
||||
"backgroundSandcastleNotes": "Heers over een zandkasteel",
|
||||
"backgrounds072017": "SET 38: Uitgebracht in juli 2017",
|
||||
|
||||
@@ -112,6 +112,6 @@
|
||||
"dateEndApril": "19 april",
|
||||
"dateEndMay": "17 mei",
|
||||
"dateEndJune": "14 juni",
|
||||
"dateEndJuly": "Juli 29",
|
||||
"dateEndJuly": "29 juli",
|
||||
"discountBundle": "bundel"
|
||||
}
|
||||
@@ -74,7 +74,7 @@
|
||||
"earnedCompanion": "Door al jouw productiviteit heb je een nieuwe kameraad verdiend. Voer hem en laat hem groeien! ",
|
||||
"feedPet": "<%= text %> aan je <%= name %> voeren?",
|
||||
"useSaddle": "<%= pet %> zadelen?",
|
||||
"raisedPet": "undefined",
|
||||
"raisedPet": "Je hebt je <%= pet %> laten opgroeien!",
|
||||
"earnedSteed": "Door het voltooien van taken heb je een trouw ros verdiend!",
|
||||
"rideNow": "Berijd nu",
|
||||
"rideLater": "Berijd later",
|
||||
|
||||
@@ -1081,7 +1081,7 @@
|
||||
"shieldSpecialSummer2017WarriorText": "Scallop Shield",
|
||||
"shieldSpecialSummer2017WarriorNotes": "This shell that you just found is both decorative AND defensive! Increases Constitution by <%= con %>. Limited Edition 2017 Summer Gear.",
|
||||
"shieldSpecialSummer2017HealerText": "Щит из раковины моллюска",
|
||||
"shieldSpecialSummer2017HealerNotes": "Этот щит волшебной устрицы постоянно порождает жемчуг так де как и защищает. Увеличивает телосложение на <%= con %>. Ограниченный выпуск лета 2017.",
|
||||
"shieldSpecialSummer2017HealerNotes": "Этот щит волшебной устрицы постоянно порождает жемчуг, а так же защищает. Увеличивает телосложение на <%= con %>. Ограниченный выпуск лета 2017.",
|
||||
"shieldMystery201601Text": "Уничтожитель Решительности",
|
||||
"shieldMystery201601Notes": "Этот клинок может быть использован, чтобы парировать все отвлечения. Бонусов не дает. Подарок подписчикам января 2016.",
|
||||
"shieldMystery201701Text": "Время-Замораживающий Щит",
|
||||
|
||||
@@ -209,7 +209,7 @@
|
||||
"onlyCreatorOrAdminCanDeleteChat": "Вы не авторизованы чтобы удаить это сообщение!",
|
||||
"onlyGroupLeaderCanEditTasks": "Вы не авторизованы, чтобы редактировать задачи!",
|
||||
"onlyGroupTasksCanBeAssigned": "Можно назначать только командные задачи",
|
||||
"chatPrivilegesRevoked": "Your chat privileges have been revoked.",
|
||||
"chatPrivilegesRevoked": "Ваши права в чате были отменены.",
|
||||
"newChatMessagePlainNotification": "Новое сообщение в <%= groupName %> от <%= authorName %>. Нажмите, чтобы открыть чат!",
|
||||
"newChatMessageTitle": "Новое сообщение в <%= groupName %>",
|
||||
"exportInbox": "Экспортировать сообщения",
|
||||
|
||||
@@ -169,5 +169,5 @@
|
||||
"missingCustomerId": "Отсутствует req.query.customerId",
|
||||
"missingPaypalBlock": "Отсутствует req.session.paypalBlock",
|
||||
"missingSubKey": "Отсутствует req.query.sub",
|
||||
"paypalCanceled": "Your subscription has been canceled"
|
||||
"paypalCanceled": "Ваша подписка была отменена"
|
||||
}
|
||||