mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
Group tasks ui picked (#7996)
* Added initial group tasks ui * Changed group compnent directory * Added group task checklist support * Added checklist support to ui * Fixed delete tags route * Added checklist routes to support new group tasks * Added assign user tag input * Added new group members autocomplete directive * Linked assign ui to api * Added styles * Limited tag use * Fixed line endings * Updated to new file structure * Fixed failing task tests * Updatd with new checklist logic and fixed columns * Added purchased info to group and prevented non purchased group from seeing new group tasks * Updated add task function * Added userid check back to tag routes * Marked tag tests as pending * Added comments to pending tests * Added back routes accidently deleted * Added locale strings * Other clarity fixes * Moved common task function to task service * Removed files from manifest * Fixed naming collision and remove logic * Removed group get tasks until live * Fixed test to check update task. Removed extra removeTask call. Synced updated checklists. Added purchased to noset * Fixed delete group task
This commit is contained in:
committed by
Matteo Pagliazzi
parent
6a82206f81
commit
285041cdee
@@ -43,7 +43,8 @@
|
|||||||
"smart-app-banner": "78ef9c0679723b25be1a0ae04f7b4aef7cbced4f",
|
"smart-app-banner": "78ef9c0679723b25be1a0ae04f7b4aef7cbced4f",
|
||||||
"habitica-markdown": "1.2.2",
|
"habitica-markdown": "1.2.2",
|
||||||
"pusher-js-auth": "^2.0.0",
|
"pusher-js-auth": "^2.0.0",
|
||||||
"pusher-websocket-iso": "pusher#^3.2.0"
|
"pusher-websocket-iso": "pusher#^3.2.0",
|
||||||
|
"taggle": "^1.11.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"angular-mocks": "1.3.9"
|
"angular-mocks": "1.3.9"
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import {
|
||||||
|
createAndPopulateGroup,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../../../helpers/api-integration/v3';
|
||||||
|
import { v4 as generateUUID } from 'uuid';
|
||||||
|
|
||||||
|
describe('DELETE group /tasks/:taskId/checklist/:itemId', () => {
|
||||||
|
let user, guild, task;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
let {group, groupLeader} = await createAndPopulateGroup({
|
||||||
|
groupDetails: {
|
||||||
|
name: 'Test Guild',
|
||||||
|
type: 'guild',
|
||||||
|
},
|
||||||
|
members: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
guild = group;
|
||||||
|
user = groupLeader;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('deletes a checklist item', async () => {
|
||||||
|
task = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
type: 'daily',
|
||||||
|
text: 'Daily with checklist',
|
||||||
|
});
|
||||||
|
|
||||||
|
let savedTask = await user.post(`/tasks/${task._id}/checklist`, {text: 'Checklist Item 1', completed: false});
|
||||||
|
|
||||||
|
await user.del(`/tasks/${task._id}/checklist/${savedTask.checklist[0].id}`);
|
||||||
|
savedTask = await user.get(`/tasks/group/${guild._id}`);
|
||||||
|
|
||||||
|
expect(savedTask[0].checklist.length).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not work with habits', async () => {
|
||||||
|
let habit = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
type: 'habit',
|
||||||
|
text: 'habit with checklist',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(user.del(`/tasks/${habit._id}/checklist/${generateUUID()}`)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('checklistOnlyDailyTodo'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not work with rewards', async () => {
|
||||||
|
let reward = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
type: 'reward',
|
||||||
|
text: 'reward with checklist',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(user.del(`/tasks/${reward._id}/checklist/${generateUUID()}`)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('checklistOnlyDailyTodo'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails on task not found', async () => {
|
||||||
|
await expect(user.del(`/tasks/${generateUUID()}/checklist/${generateUUID()}`)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('taskNotFound'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails on checklist item not found', async () => {
|
||||||
|
let createdTask = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
type: 'daily',
|
||||||
|
text: 'daily with checklist',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(user.del(`/tasks/${createdTask._id}/checklist/${generateUUID()}`)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('checklistItemNotFound'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import {
|
||||||
|
createAndPopulateGroup,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../../../helpers/api-integration/v3';
|
||||||
|
import { v4 as generateUUID } from 'uuid';
|
||||||
|
|
||||||
|
describe('POST group /tasks/:taskId/checklist/', () => {
|
||||||
|
let user, guild, task;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
let {group, groupLeader} = await createAndPopulateGroup({
|
||||||
|
groupDetails: {
|
||||||
|
name: 'Test Guild',
|
||||||
|
type: 'guild',
|
||||||
|
},
|
||||||
|
members: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
guild = group;
|
||||||
|
user = groupLeader;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds a checklist item to a task', async () => {
|
||||||
|
task = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
type: 'daily',
|
||||||
|
text: 'Daily with checklist',
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post(`/tasks/${task._id}/checklist`, {
|
||||||
|
text: 'Checklist Item 1',
|
||||||
|
ignored: false,
|
||||||
|
_id: 123,
|
||||||
|
});
|
||||||
|
|
||||||
|
let updatedTasks = await user.get(`/tasks/group/${guild._id}`);
|
||||||
|
let updatedTask = updatedTasks[0];
|
||||||
|
|
||||||
|
expect(updatedTask.checklist.length).to.equal(1);
|
||||||
|
expect(updatedTask.checklist[0].text).to.equal('Checklist Item 1');
|
||||||
|
expect(updatedTask.checklist[0].completed).to.equal(false);
|
||||||
|
expect(updatedTask.checklist[0].id).to.be.a('string');
|
||||||
|
expect(updatedTask.checklist[0].id).to.not.equal('123');
|
||||||
|
expect(updatedTask.checklist[0].ignored).to.be.an('undefined');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not add a checklist to habits', async () => {
|
||||||
|
let habit = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
type: 'habit',
|
||||||
|
text: 'habit with checklist',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(user.post(`/tasks/${habit._id}/checklist`, {
|
||||||
|
text: 'Checklist Item 1',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('checklistOnlyDailyTodo'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not add a checklist to rewards', async () => {
|
||||||
|
let reward = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
type: 'reward',
|
||||||
|
text: 'reward with checklist',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(user.post(`/tasks/${reward._id}/checklist`, {
|
||||||
|
text: 'Checklist Item 1',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('checklistOnlyDailyTodo'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails on task not found', async () => {
|
||||||
|
await expect(user.post(`/tasks/${generateUUID()}/checklist`, {
|
||||||
|
text: 'Checklist Item 1',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('taskNotFound'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import {
|
||||||
|
createAndPopulateGroup,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../../../helpers/api-integration/v3';
|
||||||
|
import { v4 as generateUUID } from 'uuid';
|
||||||
|
|
||||||
|
describe('PUT group /tasks/:taskId/checklist/:itemId', () => {
|
||||||
|
let user, guild, task;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
let {group, groupLeader} = await createAndPopulateGroup({
|
||||||
|
groupDetails: {
|
||||||
|
name: 'Test Guild',
|
||||||
|
type: 'guild',
|
||||||
|
},
|
||||||
|
members: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
guild = group;
|
||||||
|
user = groupLeader;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates a checklist item', async () => {
|
||||||
|
task = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
type: 'daily',
|
||||||
|
text: 'Daily with checklist',
|
||||||
|
});
|
||||||
|
|
||||||
|
let savedTask = await user.post(`/tasks/${task._id}/checklist`, {
|
||||||
|
text: 'Checklist Item 1',
|
||||||
|
completed: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
savedTask = await user.put(`/tasks/${task._id}/checklist/${savedTask.checklist[0].id}`, {
|
||||||
|
text: 'updated',
|
||||||
|
completed: true,
|
||||||
|
_id: 123, // ignored
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(savedTask.checklist.length).to.equal(1);
|
||||||
|
expect(savedTask.checklist[0].text).to.equal('updated');
|
||||||
|
expect(savedTask.checklist[0].completed).to.equal(true);
|
||||||
|
expect(savedTask.checklist[0].id).to.not.equal('123');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails on habits', async () => {
|
||||||
|
let habit = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
type: 'habit',
|
||||||
|
text: 'habit with checklist',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(user.put(`/tasks/${habit._id}/checklist/${generateUUID()}`)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('checklistOnlyDailyTodo'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails on rewards', async () => {
|
||||||
|
let reward = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
type: 'reward',
|
||||||
|
text: 'reward with checklist',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(user.put(`/tasks/${reward._id}/checklist/${generateUUID()}`)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('checklistOnlyDailyTodo'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails on task not found', async () => {
|
||||||
|
await expect(user.put(`/tasks/${generateUUID()}/checklist/${generateUUID()}`)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('taskNotFound'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails on checklist item not found', async () => {
|
||||||
|
let createdTask = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
type: 'daily',
|
||||||
|
text: 'daily with checklist',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(user.put(`/tasks/${createdTask._id}/checklist/${generateUUID()}`)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('checklistItemNotFound'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import {
|
||||||
|
createAndPopulateGroup,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../../../helpers/api-integration/v3';
|
||||||
|
import { v4 as generateUUID } from 'uuid';
|
||||||
|
// Currently we do not support adding tags to group original tasks, but if we do in the future, these tests will check
|
||||||
|
xdescribe('DELETE group /tasks/:taskId/tags/:tagId', () => {
|
||||||
|
let user, guild, task;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
let {group, groupLeader} = await createAndPopulateGroup({
|
||||||
|
groupDetails: {
|
||||||
|
name: 'Test Guild',
|
||||||
|
type: 'guild',
|
||||||
|
},
|
||||||
|
members: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
guild = group;
|
||||||
|
user = groupLeader;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('removes a tag from a task', async () => {
|
||||||
|
task = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
type: 'habit',
|
||||||
|
text: 'Task with tag',
|
||||||
|
});
|
||||||
|
|
||||||
|
let tag = await user.post('/tags', {name: 'Tag 1'});
|
||||||
|
|
||||||
|
await user.post(`/tasks/${task._id}/tags/${tag.id}`);
|
||||||
|
await user.del(`/tasks/${task._id}/tags/${tag.id}`);
|
||||||
|
|
||||||
|
let updatedTask = await user.get(`/tasks/group/${guild._id}`);
|
||||||
|
|
||||||
|
expect(updatedTask[0].tags.length).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('only deletes existing tags', async () => {
|
||||||
|
let createdTask = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
type: 'habit',
|
||||||
|
text: 'Task with tag',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(user.del(`/tasks/${createdTask._id}/tags/${generateUUID()}`)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('tagNotFound'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import {
|
||||||
|
createAndPopulateGroup,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../../../helpers/api-integration/v3';
|
||||||
|
import { v4 as generateUUID } from 'uuid';
|
||||||
|
// Currently we do not support adding tags to group original tasks, but if we do in the future, these tests will check
|
||||||
|
xdescribe('POST group /tasks/:taskId/tags/:tagId', () => {
|
||||||
|
let user, guild, task;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
let {group, groupLeader} = await createAndPopulateGroup({
|
||||||
|
groupDetails: {
|
||||||
|
name: 'Test Guild',
|
||||||
|
type: 'guild',
|
||||||
|
},
|
||||||
|
members: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
guild = group;
|
||||||
|
user = groupLeader;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds a tag to a task', async () => {
|
||||||
|
task = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
type: 'habit',
|
||||||
|
text: 'Task with tag',
|
||||||
|
});
|
||||||
|
|
||||||
|
let tag = await user.post('/tags', {name: 'Tag 1'});
|
||||||
|
let savedTask = await user.post(`/tasks/${task._id}/tags/${tag.id}`);
|
||||||
|
|
||||||
|
expect(savedTask.tags[0]).to.equal(tag.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not add a tag to a task twice', async () => {
|
||||||
|
task = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
type: 'habit',
|
||||||
|
text: 'Task with tag',
|
||||||
|
});
|
||||||
|
|
||||||
|
let tag = await user.post('/tags', {name: 'Tag 1'});
|
||||||
|
|
||||||
|
await user.post(`/tasks/${task._id}/tags/${tag.id}`);
|
||||||
|
|
||||||
|
await expect(user.post(`/tasks/${task._id}/tags/${tag.id}`)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('alreadyTagged'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not add a non existing tag to a task', async () => {
|
||||||
|
task = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
type: 'habit',
|
||||||
|
text: 'Task with tag',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(user.post(`/tasks/${task._id}/tags/${generateUUID()}`)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('invalidReqParams'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
217
website/client-old/css/groups.styl
Normal file
217
website/client-old/css/groups.styl
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
group-members-autocomplete
|
||||||
|
.clearfix:before, .clearfix:after
|
||||||
|
display block
|
||||||
|
content ""
|
||||||
|
line-height 0
|
||||||
|
clear both
|
||||||
|
|
||||||
|
.taggle_list
|
||||||
|
float left
|
||||||
|
width 100%
|
||||||
|
li
|
||||||
|
float left
|
||||||
|
display inline-block
|
||||||
|
white-space nowrap
|
||||||
|
font-weight 500
|
||||||
|
margin-bottom 5px
|
||||||
|
.taggle
|
||||||
|
margin-right 8px
|
||||||
|
background #E2E1DF
|
||||||
|
padding 5px 10px
|
||||||
|
border-radius 3px
|
||||||
|
position relative
|
||||||
|
cursor pointer
|
||||||
|
transition all .3s
|
||||||
|
-webkit-animation-duration 1s
|
||||||
|
animation-duration 1s
|
||||||
|
-webkit-animation-fill-mode both
|
||||||
|
animation-fill-mode both
|
||||||
|
.close
|
||||||
|
font-size 1.1rem
|
||||||
|
position absolute
|
||||||
|
top 5px
|
||||||
|
right 3px
|
||||||
|
text-decoration none
|
||||||
|
padding-left 2px
|
||||||
|
padding-top 3px
|
||||||
|
line-height 0.5
|
||||||
|
color #ccc
|
||||||
|
color rgba(0, 0, 0, 0.2)
|
||||||
|
padding-bottom 4px
|
||||||
|
display none
|
||||||
|
border 0
|
||||||
|
background none
|
||||||
|
cursor pointer
|
||||||
|
&:hover
|
||||||
|
color $color-purple
|
||||||
|
&:hover
|
||||||
|
padding 5px
|
||||||
|
padding-right 15px
|
||||||
|
background #ccc
|
||||||
|
transition all .3s
|
||||||
|
& > .close
|
||||||
|
display block
|
||||||
|
.taggle_hot
|
||||||
|
background #cac8c4
|
||||||
|
|
||||||
|
.taggle_input
|
||||||
|
border none
|
||||||
|
outline none
|
||||||
|
font-size 16px
|
||||||
|
font-weight 300
|
||||||
|
padding 8px
|
||||||
|
padding-left 0
|
||||||
|
float left
|
||||||
|
margin-top -5px
|
||||||
|
background none
|
||||||
|
width 100%
|
||||||
|
max-width 100%
|
||||||
|
|
||||||
|
.taggle_placeholder
|
||||||
|
position absolute
|
||||||
|
color #CCC
|
||||||
|
top 12px
|
||||||
|
left 8px
|
||||||
|
transition opacity, .25s
|
||||||
|
-webkit-user-select none
|
||||||
|
-moz-user-select none
|
||||||
|
-ms-user-select none
|
||||||
|
user-select none
|
||||||
|
|
||||||
|
.taggle_sizer
|
||||||
|
padding 0
|
||||||
|
margin 0
|
||||||
|
position absolute
|
||||||
|
top -500px
|
||||||
|
z-index -1
|
||||||
|
visibility hidden
|
||||||
|
|
||||||
|
textarea.input,
|
||||||
|
.textarea.input
|
||||||
|
border 0
|
||||||
|
background #FDFDFD
|
||||||
|
box-shadow inset 0 1px 2px rgba(0, 0, 0, 0.2), 0 1px 1px rgba(255, 255, 255, 0.7)
|
||||||
|
min-height 60px
|
||||||
|
padding 8px
|
||||||
|
border-radius 3px
|
||||||
|
color #555
|
||||||
|
transition all .25s
|
||||||
|
cursor text
|
||||||
|
margin-bottom 10px
|
||||||
|
position relative
|
||||||
|
|
||||||
|
.textarea.input:focus,
|
||||||
|
.textarea.input.active,
|
||||||
|
textarea.input:focus,
|
||||||
|
textarea.input.active
|
||||||
|
background #fff
|
||||||
|
transition all .25s
|
||||||
|
|
||||||
|
.textarea.input,
|
||||||
|
textarea.input
|
||||||
|
height auto
|
||||||
|
|
||||||
|
.textarea
|
||||||
|
&.tags
|
||||||
|
position relative
|
||||||
|
*
|
||||||
|
box-sizing content-box
|
||||||
|
|
||||||
|
.placeholder_input
|
||||||
|
position relative
|
||||||
|
span
|
||||||
|
position absolute
|
||||||
|
color #AAA
|
||||||
|
top 50%
|
||||||
|
margin-top -11px
|
||||||
|
left 10px
|
||||||
|
input
|
||||||
|
width 120px
|
||||||
|
|
||||||
|
.ui-autocomplete
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
max-height 200px
|
||||||
|
width 99% !important
|
||||||
|
|
||||||
|
.ui-menu
|
||||||
|
list-style none
|
||||||
|
padding 2px
|
||||||
|
margin 0
|
||||||
|
display block
|
||||||
|
outline none
|
||||||
|
.ui-menu-item
|
||||||
|
margin 0
|
||||||
|
padding 0
|
||||||
|
width 100%
|
||||||
|
a
|
||||||
|
text-decoration none
|
||||||
|
display block
|
||||||
|
padding 2px .4em
|
||||||
|
line-height 1.5
|
||||||
|
min-height 0
|
||||||
|
font-weight normal
|
||||||
|
color #8a8a8a
|
||||||
|
cursor pointer
|
||||||
|
&:hover
|
||||||
|
color #fff
|
||||||
|
background $color-purple
|
||||||
|
|
||||||
|
.ui-widget-content
|
||||||
|
background #fff
|
||||||
|
color $color-purple
|
||||||
|
|
||||||
|
.ui-state-hover,
|
||||||
|
.ui-widget-content .ui-state-hover,
|
||||||
|
.ui-widget-header .ui-state-hover,
|
||||||
|
.ui-state-focus,
|
||||||
|
.ui-widget-content .ui-state-focus,
|
||||||
|
.ui-widget-header .ui-state-focus
|
||||||
|
background $color-purple
|
||||||
|
color #fff !important
|
||||||
|
|
||||||
|
.ui-state-hover a,
|
||||||
|
.ui-state-hover a:hover,
|
||||||
|
.ui-state-hover a:link,
|
||||||
|
.ui-state-hover a:visited
|
||||||
|
color #fff
|
||||||
|
|
||||||
|
.ui-state-active,
|
||||||
|
.ui-widget-content .ui-state-active,
|
||||||
|
.ui-widget-header .ui-state-active
|
||||||
|
border 1px solid #aaaaaa
|
||||||
|
background #ffffff
|
||||||
|
font-weight normal
|
||||||
|
color #212121
|
||||||
|
|
||||||
|
.ui-helper-hidden
|
||||||
|
display none
|
||||||
|
|
||||||
|
.ui-helper-hidden-accessible
|
||||||
|
border 0
|
||||||
|
clip rect(0 0 0 0)
|
||||||
|
height 1px
|
||||||
|
margin -1px
|
||||||
|
overflow hidden
|
||||||
|
padding 0
|
||||||
|
position absolute
|
||||||
|
width 1px
|
||||||
|
|
||||||
|
.autocomplete
|
||||||
|
max-height 200px
|
||||||
|
position absolute
|
||||||
|
top 66px
|
||||||
|
background white
|
||||||
|
width 99.5%
|
||||||
|
left 0.25%
|
||||||
|
z-index 2
|
||||||
|
ul
|
||||||
|
li
|
||||||
|
display block
|
||||||
|
padding 6px 8px
|
||||||
|
|
||||||
|
.autocomplete ul li.selected, .autocomplete ul li:hover
|
||||||
|
background #ff6633
|
||||||
|
color #fff
|
||||||
|
cursor pointer
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
@import "./filters.styl"
|
@import "./filters.styl"
|
||||||
@import "./scrollbars.styl"
|
@import "./scrollbars.styl"
|
||||||
@import "./game-pane.styl"
|
@import "./game-pane.styl"
|
||||||
|
@import "./groups.styl"
|
||||||
@import "./npcs.styl"
|
@import "./npcs.styl"
|
||||||
@import "./challenges.styl"
|
@import "./challenges.styl"
|
||||||
@import "./classes.styl"
|
@import "./classes.styl"
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#main-wrap>div
|
#main-wrap>div
|
||||||
{
|
{
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#block-up-arrow, #block-down-arrow {
|
#block-up-arrow, #block-down-arrow {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,215 +1,215 @@
|
|||||||
(function( $ ) {
|
(function( $ ) {
|
||||||
|
|
||||||
$.fn.blockScroll = function(options) {
|
$.fn.blockScroll = function(options) {
|
||||||
var settings = $.extend({
|
var settings = $.extend({
|
||||||
// These are the defaults.
|
// These are the defaults.
|
||||||
startDiv : 1,
|
startDiv : 1,
|
||||||
fadeDuration : "slow",
|
fadeDuration : "slow",
|
||||||
paddingRatio : 0.05,
|
paddingRatio : 0.05,
|
||||||
triggerRatio : 0.005,
|
triggerRatio : 0.005,
|
||||||
scrollDuration: "fast",
|
scrollDuration: "fast",
|
||||||
fadeBlocks: true
|
fadeBlocks: true
|
||||||
}, options );
|
}, options );
|
||||||
|
|
||||||
if(settings.triggerRatio > settings.paddingRatio*.95) { settings.triggerRatio = settings.paddingRatio*.95 }
|
if(settings.triggerRatio > settings.paddingRatio*.95) { settings.triggerRatio = settings.paddingRatio*.95 }
|
||||||
|
|
||||||
var theDivs = this.children().filter("div");
|
var theDivs = this.children().filter("div");
|
||||||
var activeDiv = settings.startDiv-1; //Active did is 0-index, settings is 1-index
|
var activeDiv = settings.startDiv-1; //Active did is 0-index, settings is 1-index
|
||||||
var windowHeight;
|
var windowHeight;
|
||||||
var paddingHeight;
|
var paddingHeight;
|
||||||
var triggerHeight;
|
var triggerHeight;
|
||||||
var currentDownTrigger;
|
var currentDownTrigger;
|
||||||
var currentUpTrigger;
|
var currentUpTrigger;
|
||||||
var totalDivs = theDivs.length;
|
var totalDivs = theDivs.length;
|
||||||
var lastScrollPos;
|
var lastScrollPos;
|
||||||
var activelyScrolling = false;
|
var activelyScrolling = false;
|
||||||
var activeBackground= 0;
|
var activeBackground= 0;
|
||||||
|
|
||||||
// Ensure that all of the elements are hidden just in case the css is not setup properly
|
// Ensure that all of the elements are hidden just in case the css is not setup properly
|
||||||
if(settings.fadeBlocks)
|
if(settings.fadeBlocks)
|
||||||
{
|
{
|
||||||
this.children().each(function() {
|
this.children().each(function() {
|
||||||
$(this).css('opacity','0');
|
$(this).css('opacity','0');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
arrange();
|
arrange();
|
||||||
// Fade in the first div
|
// Fade in the first div
|
||||||
$(theDivs[activeDiv]).animate({opacity: 1},settings.fadeDuration,'linear', function() {
|
$(theDivs[activeDiv]).animate({opacity: 1},settings.fadeDuration,'linear', function() {
|
||||||
$(window).scrollTop(0);
|
$(window).scrollTop(0);
|
||||||
calcTriggers();
|
calcTriggers();
|
||||||
bindEvents();
|
bindEvents();
|
||||||
lastScrollPos = $(window).scrollTop();
|
lastScrollPos = $(window).scrollTop();
|
||||||
});
|
});
|
||||||
|
|
||||||
function bindEvents()
|
function bindEvents()
|
||||||
{
|
{
|
||||||
$(window).on('scroll', function(e) {
|
$(window).on('scroll', function(e) {
|
||||||
var scrollPosition = $(window).scrollTop();
|
var scrollPosition = $(window).scrollTop();
|
||||||
var scrollDistance = $(window).height();
|
var scrollDistance = $(window).height();
|
||||||
var indexOfClosest = 0;
|
var indexOfClosest = 0;
|
||||||
|
|
||||||
theDivs.each(function(index, element) {
|
theDivs.each(function(index, element) {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var topPosition = $this.offset().top;
|
var topPosition = $this.offset().top;
|
||||||
var newScrollDistance = Math.abs(scrollPosition - topPosition);
|
var newScrollDistance = Math.abs(scrollPosition - topPosition);
|
||||||
if(newScrollDistance < scrollDistance)
|
if(newScrollDistance < scrollDistance)
|
||||||
{
|
{
|
||||||
indexOfClosest = index;
|
indexOfClosest = index;
|
||||||
scrollDistance = newScrollDistance;
|
scrollDistance = newScrollDistance;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
gotoDiv(indexOfClosest);
|
gotoDiv(indexOfClosest);
|
||||||
}, 250);
|
}, 250);
|
||||||
|
|
||||||
$(window).resize(function() {
|
$(window).resize(function() {
|
||||||
arrange();
|
arrange();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#block-up-arrow").click(function() {
|
$("#block-up-arrow").click(function() {
|
||||||
goUp();
|
goUp();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#block-down-arrow").click(function() {
|
$("#block-down-arrow").click(function() {
|
||||||
goDown();
|
goDown();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).keydown(function(e){
|
$(document).keydown(function(e){
|
||||||
if (e.keyCode == 37 || e.keyCode == 38) {
|
if (e.keyCode == 37 || e.keyCode == 38) {
|
||||||
goUp();
|
goUp();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.keyCode == 39 || e.keyCode == 40) {
|
if (e.keyCode == 39 || e.keyCode == 40) {
|
||||||
goDown();
|
goDown();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$(window).bind('mousewheel', function(e){
|
$(window).bind('mousewheel', function(e){
|
||||||
if(e.originalEvent.wheelDelta > 119) {
|
if(e.originalEvent.wheelDelta > 119) {
|
||||||
goUp();
|
goUp();
|
||||||
}
|
}
|
||||||
else if (e.originalEvent.wheelDelta < -119) {
|
else if (e.originalEvent.wheelDelta < -119) {
|
||||||
goDown();
|
goDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function goUp()
|
function goUp()
|
||||||
{
|
{
|
||||||
if(activeDiv > 0 && !activelyScrolling)
|
if(activeDiv > 0 && !activelyScrolling)
|
||||||
{
|
{
|
||||||
gotoDiv(activeDiv-1);
|
gotoDiv(activeDiv-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function goDown()
|
function goDown()
|
||||||
{
|
{
|
||||||
if(activeDiv < totalDivs - 1 && !activelyScrolling)
|
if(activeDiv < totalDivs - 1 && !activelyScrolling)
|
||||||
{
|
{
|
||||||
gotoDiv(activeDiv+1);
|
gotoDiv(activeDiv+1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function gotoDiv(number)
|
function gotoDiv(number)
|
||||||
{
|
{
|
||||||
if(number == 0)
|
if(number == 0)
|
||||||
$("#block-up-arrow").hide();
|
$("#block-up-arrow").hide();
|
||||||
else
|
else
|
||||||
$("#block-up-arrow").show();
|
$("#block-up-arrow").show();
|
||||||
if(number == totalDivs-1)
|
if(number == totalDivs-1)
|
||||||
$("#block-down-arrow").hide();
|
$("#block-down-arrow").hide();
|
||||||
else
|
else
|
||||||
$("#block-down-arrow").show();
|
$("#block-down-arrow").show();
|
||||||
activeDiv = number;
|
activeDiv = number;
|
||||||
activelyScrolling = true;
|
activelyScrolling = true;
|
||||||
$('html, body').animate({scrollTop: $(theDivs[activeDiv]).offset().top}, settings.scrollDuration, 'linear', function() {
|
$('html, body').animate({scrollTop: $(theDivs[activeDiv]).offset().top}, settings.scrollDuration, 'linear', function() {
|
||||||
$(theDivs[activeDiv]).animate({opacity: 1}, settings.fadeDuration,'linear', function() {
|
$(theDivs[activeDiv]).animate({opacity: 1}, settings.fadeDuration,'linear', function() {
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
activelyScrolling = false; lastScrollPos = $(window).scrollTop();
|
activelyScrolling = false; lastScrollPos = $(window).scrollTop();
|
||||||
},50);
|
},50);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
calcTriggers();
|
calcTriggers();
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcTriggers()
|
function calcTriggers()
|
||||||
{
|
{
|
||||||
if (activeDiv < totalDivs -1)
|
if (activeDiv < totalDivs -1)
|
||||||
{
|
{
|
||||||
currentDownTrigger = $(theDivs[activeDiv+1]).offset().top;
|
currentDownTrigger = $(theDivs[activeDiv+1]).offset().top;
|
||||||
} else {
|
} else {
|
||||||
currentDownTrigger = -1;
|
currentDownTrigger = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeDiv > 0) {
|
if (activeDiv > 0) {
|
||||||
currentUpTrigger = $(theDivs[activeDiv-1]).offset().top;
|
currentUpTrigger = $(theDivs[activeDiv-1]).offset().top;
|
||||||
} else {
|
} else {
|
||||||
currentUpTrigger = -1;
|
currentUpTrigger = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcDims()
|
function calcDims()
|
||||||
{
|
{
|
||||||
windowHeight = $(window).height();
|
windowHeight = $(window).height();
|
||||||
paddingHeight = windowHeight * settings.paddingRatio;
|
paddingHeight = windowHeight * settings.paddingRatio;
|
||||||
triggerHeight = windowHeight * settings.triggerRatio;
|
triggerHeight = windowHeight * settings.triggerRatio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function arrange()
|
function arrange()
|
||||||
{
|
{
|
||||||
calcDims();
|
calcDims();
|
||||||
theDivs.each(function(index, element) {
|
theDivs.each(function(index, element) {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
$this.height('auto');
|
$this.height('auto');
|
||||||
if($this.height() < windowHeight)
|
if($this.height() < windowHeight)
|
||||||
{
|
{
|
||||||
var margin = windowHeight/2 - $this.height()/2;
|
var margin = windowHeight/2 - $this.height()/2;
|
||||||
$this.height(windowHeight-margin);
|
$this.height(windowHeight-margin);
|
||||||
$this.css('padding-top', margin + "px");
|
$this.css('padding-top', margin + "px");
|
||||||
var $innerDiv = $($this.children().filter('div')[0]);
|
var $innerDiv = $($this.children().filter('div')[0]);
|
||||||
// $innerDiv.css('padding-top', margin + "px");
|
// $innerDiv.css('padding-top', margin + "px");
|
||||||
}
|
}
|
||||||
if(index != totalDivs - 1)
|
if(index != totalDivs - 1)
|
||||||
{
|
{
|
||||||
//$this.css('padding-bottom',paddingHeight + 'px');
|
//$this.css('padding-bottom',paddingHeight + 'px');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
gotoDiv(activeDiv);
|
gotoDiv(activeDiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
var gotoView = function(number)
|
var gotoView = function(number)
|
||||||
{
|
{
|
||||||
gotoDiv(number-1);
|
gotoDiv(number-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
goto: gotoView
|
goto: gotoView
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}( jQuery ));
|
}( jQuery ));
|
||||||
|
|
||||||
;(function ($) {
|
;(function ($) {
|
||||||
var on = $.fn.on, timer;
|
var on = $.fn.on, timer;
|
||||||
$.fn.on = function () {
|
$.fn.on = function () {
|
||||||
var args = Array.apply(null, arguments);
|
var args = Array.apply(null, arguments);
|
||||||
var last = args[args.length - 1];
|
var last = args[args.length - 1];
|
||||||
|
|
||||||
if (isNaN(last) || (last === 1 && args.pop())) return on.apply(this, args);
|
if (isNaN(last) || (last === 1 && args.pop())) return on.apply(this, args);
|
||||||
|
|
||||||
var delay = args.pop();
|
var delay = args.pop();
|
||||||
var fn = args.pop();
|
var fn = args.pop();
|
||||||
|
|
||||||
args.push(function () {
|
args.push(function () {
|
||||||
var self = this, params = arguments;
|
var self = this, params = arguments;
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
timer = setTimeout(function () {
|
timer = setTimeout(function () {
|
||||||
fn.apply(self, params);
|
fn.apply(self, params);
|
||||||
}, delay);
|
}, delay);
|
||||||
});
|
});
|
||||||
|
|
||||||
return on.apply(this, args);
|
return on.apply(this, args);
|
||||||
};
|
};
|
||||||
}(this.jQuery || this.Zepto));
|
}(this.jQuery || this.Zepto));
|
||||||
|
|||||||
@@ -159,11 +159,11 @@ window.habitrpg = angular.module('habitrpg',
|
|||||||
url: '/:gid',
|
url: '/:gid',
|
||||||
templateUrl: 'partials/options.social.guilds.detail.html',
|
templateUrl: 'partials/options.social.guilds.detail.html',
|
||||||
title: env.t('titleGuilds'),
|
title: env.t('titleGuilds'),
|
||||||
controller: ['$scope', 'Groups', 'Chat', '$stateParams', 'Members', 'Challenges',
|
controller: ['$scope', 'Groups', 'Chat', '$stateParams', 'Members', 'Challenges', 'Tasks',
|
||||||
function($scope, Groups, Chat, $stateParams, Members, Challenges){
|
function($scope, Groups, Chat, $stateParams, Members, Challenges, Tasks) {
|
||||||
Groups.Group.get($stateParams.gid)
|
Groups.Group.get($stateParams.gid)
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
$scope.group = response.data.data;
|
$scope.obj = $scope.group = response.data.data;
|
||||||
Chat.markChatSeen($scope.group._id);
|
Chat.markChatSeen($scope.group._id);
|
||||||
Members.getGroupMembers($scope.group._id)
|
Members.getGroupMembers($scope.group._id)
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
@@ -177,7 +177,16 @@ window.habitrpg = angular.module('habitrpg',
|
|||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
$scope.group.challenges = response.data.data;
|
$scope.group.challenges = response.data.data;
|
||||||
});
|
});
|
||||||
});
|
//@TODO: Add this back when group tasks go live
|
||||||
|
// return Tasks.getGroupTasks($scope.group._id);
|
||||||
|
})
|
||||||
|
// .then(function (response) {
|
||||||
|
// var tasks = response.data.data;
|
||||||
|
// tasks.forEach(function (element, index, array) {
|
||||||
|
// if (!$scope.group[element.type + 's']) $scope.group[element.type + 's'] = [];
|
||||||
|
// $scope.group[element.type + 's'].push(element);
|
||||||
|
// })
|
||||||
|
// });
|
||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
angular
|
||||||
|
.module('habitrpg')
|
||||||
|
.directive('groupMembersAutocomplete', groupMembersAutocomplete);
|
||||||
|
|
||||||
|
groupMembersAutocomplete.$inject = [
|
||||||
|
'$parse',
|
||||||
|
'$rootScope',
|
||||||
|
];
|
||||||
|
|
||||||
|
function groupMembersAutocomplete($parse, $rootScope) {
|
||||||
|
|
||||||
|
return {
|
||||||
|
templateUrl: 'partials/groups.members.autocomplete.html',
|
||||||
|
compile: function (element, attrs) {
|
||||||
|
var modelAccessor = $parse(attrs.ngModel);
|
||||||
|
|
||||||
|
return function (scope, element, attrs, controller) {
|
||||||
|
var availableTags = _.pluck(scope.group.members, 'profile.name');
|
||||||
|
var memberProfileNameToIdMap = _.object(_.map(scope.group.members, function(item) {
|
||||||
|
return [item.profile.name, item.id]
|
||||||
|
}));
|
||||||
|
var memberIdToProfileNameMap = _.object(_.map(scope.group.members, function(item) {
|
||||||
|
return [item.id, item.profile.name]
|
||||||
|
}));
|
||||||
|
|
||||||
|
var currentTags = [];
|
||||||
|
_.each(scope.task.group.assignedUsers, function(userId) { currentTags.push(memberIdToProfileNameMap[userId]) })
|
||||||
|
|
||||||
|
var taggle = new Taggle('taggle', {
|
||||||
|
tags: currentTags,
|
||||||
|
allowedTags: currentTags,
|
||||||
|
allowDuplicates: false,
|
||||||
|
onBeforeTagAdd: function(event, tag) {
|
||||||
|
return confirm(window.env.t('confirmAddTag', {tag: tag}));
|
||||||
|
},
|
||||||
|
onTagAdd: function(event, tag) {
|
||||||
|
$rootScope.$broadcast('addedGroupMember', memberProfileNameToIdMap[tag]);
|
||||||
|
},
|
||||||
|
onBeforeTagRemove: function(event, tag) {
|
||||||
|
return confirm(window.env.t('confirmRemoveTag', {tag: tag}))
|
||||||
|
},
|
||||||
|
onTagRemove: function(event, tag) {
|
||||||
|
$rootScope.$broadcast('removedGroupMember', memberProfileNameToIdMap[tag]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var container = taggle.getContainer();
|
||||||
|
var input = taggle.getInput();
|
||||||
|
|
||||||
|
$(input).autocomplete({
|
||||||
|
source: availableTags, // See jQuery UI documentaton for options
|
||||||
|
appendTo: container,
|
||||||
|
position: { at: "left bottom", of: container },
|
||||||
|
select: function(event, data) {
|
||||||
|
event.preventDefault();
|
||||||
|
//Add the tag if user clicks
|
||||||
|
if (event.which === 1) {
|
||||||
|
taggle.add(data.item.value);
|
||||||
|
var taggleTags = taggle.getTags();
|
||||||
|
scope.$apply(function (scope) {
|
||||||
|
// Change bound variable
|
||||||
|
modelAccessor.assign(scope, taggleTags.values);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}());
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
habitrpg.controller('GroupTaskActionsCtrl', ['$scope', 'Shared', 'Tasks', 'User',
|
||||||
|
function ($scope, Shared, Tasks, User) {
|
||||||
|
$scope.assignedMembers = [];
|
||||||
|
$scope.user = User.user;
|
||||||
|
|
||||||
|
$scope.$on('addedGroupMember', function(evt, userId) {
|
||||||
|
Tasks.assignTask($scope.task.id, userId);
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.$on('removedGroupMember', function(evt, userId) {
|
||||||
|
Tasks.unAssignTask($scope.task.id, userId);
|
||||||
|
});
|
||||||
|
}]);
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
angular
|
||||||
|
.module('habitrpg')
|
||||||
|
.directive('groupTasksActions', hrpgSortTags);
|
||||||
|
|
||||||
|
hrpgSortTags.$inject = [
|
||||||
|
];
|
||||||
|
|
||||||
|
function hrpgSortTags() {
|
||||||
|
|
||||||
|
return {
|
||||||
|
scope: {
|
||||||
|
task: '=',
|
||||||
|
group: '=',
|
||||||
|
},
|
||||||
|
templateUrl: 'partials/groups.tasks.actions.html',
|
||||||
|
controller: 'GroupTaskActionsCtrl',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}());
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
habitrpg.controller('GroupTasksCtrl', ['$scope', 'Shared', 'Tasks', 'User', function ($scope, Shared, Tasks, User) {
|
||||||
|
$scope.editTask = Tasks.editTask;
|
||||||
|
$scope.toggleBulk = Tasks.toggleBulk;
|
||||||
|
$scope.cancelTaskEdit = Tasks.cancelTaskEdit;
|
||||||
|
|
||||||
|
function addTask (listDef, task) {
|
||||||
|
var task = Shared.taskDefaults({text: task, type: listDef.type});
|
||||||
|
//If the group has not been created, we bulk add tasks on save
|
||||||
|
var group = $scope.obj;
|
||||||
|
if (group._id) Tasks.createGroupTasks(group._id, task);
|
||||||
|
if (!group[task.type + 's']) group[task.type + 's'] = [];
|
||||||
|
group[task.type + 's'].unshift(task);
|
||||||
|
delete listDef.newTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.addTask = function(listDef) {
|
||||||
|
Tasks.addTasks(listDef, addTask);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.removeTask = function(task, group) {
|
||||||
|
if (!Tasks.removeTask(task)) return;
|
||||||
|
//We only pass to the api if the group exists, otherwise, the tasks only exist on the client
|
||||||
|
if (group._id) Tasks.deleteTask(task._id);
|
||||||
|
var index = group[task.type + 's'].indexOf(task);
|
||||||
|
group[task.type + 's'].splice(index, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.saveTask = function(task, stayOpen, isSaveAndClose) {
|
||||||
|
Tasks.saveTask (task, stayOpen, isSaveAndClose);
|
||||||
|
Tasks.updateTask(task._id, task);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.shouldShow = function(task, list, prefs){
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.canEdit = function(task) {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------
|
||||||
|
Tags
|
||||||
|
------------------------
|
||||||
|
*/
|
||||||
|
$scope.updateTaskTags = function (tagId, task) {
|
||||||
|
var tagIndex = task.tags.indexOf(tagId);
|
||||||
|
if (tagIndex === -1) {
|
||||||
|
Tasks.addTagToTask(task._id, tagId);
|
||||||
|
task.tags.push(tagId);
|
||||||
|
} else {
|
||||||
|
Tasks.removeTagFromTask(task._id, tagId);
|
||||||
|
task.tags.splice(tagIndex, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------
|
||||||
|
Checklists
|
||||||
|
------------------------
|
||||||
|
*/
|
||||||
|
$scope.addChecklist = Tasks.addChecklist;
|
||||||
|
|
||||||
|
$scope.addChecklistItem = Tasks.addChecklistItemToUI;
|
||||||
|
|
||||||
|
$scope.removeChecklistItem = Tasks.removeChecklistItemFromUI;
|
||||||
|
|
||||||
|
$scope.swapChecklistItems = Tasks.swapChecklistItems;
|
||||||
|
|
||||||
|
$scope.navigateChecklist = Tasks.navigateChecklist;
|
||||||
|
|
||||||
|
$scope.checklistCompletion = Tasks.checklistCompletion;
|
||||||
|
|
||||||
|
$scope.collapseChecklist = function (task) {
|
||||||
|
Tasks.collapseChecklist(task);
|
||||||
|
//@TODO: Currently the api save of the task is separate, so whenever we need to save the task we need to call the respective api
|
||||||
|
Tasks.updateTask(task._id, task);
|
||||||
|
};
|
||||||
|
}]);
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
angular
|
||||||
|
.module('habitrpg')
|
||||||
|
.directive('groupTasks', hrpgSortTags);
|
||||||
|
|
||||||
|
hrpgSortTags.$inject = [
|
||||||
|
];
|
||||||
|
|
||||||
|
function hrpgSortTags() {
|
||||||
|
|
||||||
|
return {
|
||||||
|
scope: true,
|
||||||
|
templateUrl: 'partials/groups.tasks.html',
|
||||||
|
controller: 'GroupTasksCtrl',
|
||||||
|
link: function($scope, element, attrs, ngModel) {
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}());
|
||||||
@@ -101,24 +101,8 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N
|
|||||||
};
|
};
|
||||||
|
|
||||||
$scope.saveTask = function(task, stayOpen, isSaveAndClose) {
|
$scope.saveTask = function(task, stayOpen, isSaveAndClose) {
|
||||||
if (task._edit) {
|
Tasks.saveTask (task, stayOpen, isSaveAndClose);
|
||||||
angular.copy(task._edit, task);
|
|
||||||
}
|
|
||||||
task._edit = undefined;
|
|
||||||
|
|
||||||
if (task.checklist) {
|
|
||||||
task.checklist = _.filter(task.checklist, function (i) {
|
|
||||||
return !!i.text
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
User.updateTask(task, {body: task});
|
User.updateTask(task, {body: task});
|
||||||
if (!stayOpen) task._editing = false;
|
|
||||||
|
|
||||||
if (isSaveAndClose) {
|
|
||||||
$("#task-" + task._id).parent().children('.popover').removeClass('in');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (task.type == 'habit') Guide.goto('intro', 3);
|
if (task.type == 'habit') Guide.goto('intro', 3);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -132,8 +116,7 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N
|
|||||||
$scope.cancelTaskEdit = Tasks.cancelTaskEdit;
|
$scope.cancelTaskEdit = Tasks.cancelTaskEdit;
|
||||||
|
|
||||||
$scope.removeTask = function(task) {
|
$scope.removeTask = function(task) {
|
||||||
if (!confirm(window.env.t('sureDelete', {taskType: window.env.t(task.type), taskText: task.text}))) return;
|
if (!Tasks.removeTask(task)) return;
|
||||||
task._edit = undefined;
|
|
||||||
User.deleteTask({params:{id: task._id, taskType: task.type}})
|
User.deleteTask({params:{id: task._id, taskType: task.type}})
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -190,60 +173,28 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N
|
|||||||
Checklists
|
Checklists
|
||||||
------------------------
|
------------------------
|
||||||
*/
|
*/
|
||||||
function focusChecklist(task,index) {
|
/*
|
||||||
window.setTimeout(function(){
|
------------------------
|
||||||
$('#task-'+task._id+' .checklist-form input[type="text"]')[index].focus();
|
Checklists
|
||||||
});
|
------------------------
|
||||||
}
|
*/
|
||||||
|
$scope.addChecklist = Tasks.addChecklist;
|
||||||
|
|
||||||
$scope.addChecklist = function(task) {
|
$scope.addChecklistItem = Tasks.addChecklistItemToUI;
|
||||||
task._edit.checklist = [{completed:false, text:""}];
|
|
||||||
focusChecklist(task._edit,0);
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.addChecklistItem = function(task, $event, $index) {
|
$scope.removeChecklistItem = Tasks.removeChecklistItemFromUI;
|
||||||
if (task._edit.checklist[$index].text) {
|
|
||||||
if ($index === task._edit.checklist.length - 1) {
|
|
||||||
task._edit.checklist.push({ completed: false, text: '' });
|
|
||||||
}
|
|
||||||
focusChecklist(task._edit, $index + 1);
|
|
||||||
} else {
|
|
||||||
// TODO Provide UI feedback that this item is still blank
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.removeChecklistItem = function(task, $event, $index, force) {
|
$scope.swapChecklistItems = Tasks.swapChecklistItems;
|
||||||
// Remove item if clicked on trash icon
|
|
||||||
if (force) {
|
|
||||||
task._edit.checklist.splice($index, 1);
|
|
||||||
} else if (!task._edit.checklist[$index].text) {
|
|
||||||
// User deleted all the text and is now wishing to delete the item
|
|
||||||
// saveTask will prune the empty item
|
|
||||||
// Move focus if the list is still non-empty
|
|
||||||
if ($index > 0)
|
|
||||||
focusChecklist(task._edit, $index-1);
|
|
||||||
// Don't allow the backspace key to navigate back now that the field is gone
|
|
||||||
$event.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.swapChecklistItems = function(task, oldIndex, newIndex) {
|
$scope.navigateChecklist = Tasks.navigateChecklist;
|
||||||
var toSwap = task._edit.checklist.splice(oldIndex, 1)[0];
|
|
||||||
task._edit.checklist.splice(newIndex, 0, toSwap);
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.navigateChecklist = function(task,$index,$event){
|
$scope.checklistCompletion = Tasks.checklistCompletion;
|
||||||
focusChecklist(task, $event.keyCode == '40' ? $index+1 : $index-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.checklistCompletion = function(checklist){
|
$scope.collapseChecklist = function (task) {
|
||||||
return _.reduce(checklist,function(m,i){return m+(i.completed ? 1 : 0);},0)
|
Tasks.collapseChecklist(task);
|
||||||
}
|
//@TODO: Currently the api save of the task is separate, so whenever we need to save the task we need to call the respective api
|
||||||
|
Tasks.updateTask(task._id, task);
|
||||||
$scope.collapseChecklist = function(task) {
|
};
|
||||||
task.collapseChecklist = !task.collapseChecklist;
|
|
||||||
$scope.saveTask(task,true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
------------------------
|
------------------------
|
||||||
|
|||||||
@@ -29,6 +29,33 @@ angular.module('habitrpg')
|
|||||||
list.focus = true;
|
list.focus = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function removeTask (task) {
|
||||||
|
if (!confirm(window.env.t('sureDelete', {taskType: window.env.t(task.type), taskText: task.text}))) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
task._edit = undefined;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveTask (task, stayOpen, isSaveAndClose) {
|
||||||
|
if (task._edit) {
|
||||||
|
angular.copy(task._edit, task);
|
||||||
|
}
|
||||||
|
task._edit = undefined;
|
||||||
|
|
||||||
|
if (task.checklist) {
|
||||||
|
task.checklist = _.filter(task.checklist, function (i) {
|
||||||
|
return !!i.text
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stayOpen) task._editing = false;
|
||||||
|
|
||||||
|
if (isSaveAndClose) {
|
||||||
|
$("#task-" + task._id).parent().children('.popover').removeClass('in');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getUserTasks (getCompletedTodos) {
|
function getUserTasks (getCompletedTodos) {
|
||||||
var url = '/api/v3/tasks/user';
|
var url = '/api/v3/tasks/user';
|
||||||
|
|
||||||
@@ -64,6 +91,21 @@ angular.module('habitrpg')
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getGroupTasks (groupId) {
|
||||||
|
return $http({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/api/v3/tasks/group/' + groupId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function createGroupTasks (groupId, taskDetails) {
|
||||||
|
return $http({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/api/v3/tasks/group/' + groupId,
|
||||||
|
data: taskDetails,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
function getTask (taskId) {
|
function getTask (taskId) {
|
||||||
return $http({
|
return $http({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@@ -175,6 +217,20 @@ angular.module('habitrpg')
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function assignTask (taskId, userId) {
|
||||||
|
return $http({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/api/v3/tasks/' + taskId + '/assign/' + userId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function unAssignTask (taskId, userId) {
|
||||||
|
return $http({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/api/v3/tasks/' + taskId + '/unassign/' + userId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
function editTask(task, user) {
|
function editTask(task, user) {
|
||||||
task._editing = true;
|
task._editing = true;
|
||||||
task._tags = !user.preferences.tagsCollapsed;
|
task._tags = !user.preferences.tagsCollapsed;
|
||||||
@@ -211,14 +267,79 @@ angular.module('habitrpg')
|
|||||||
return cleansedTask;
|
return cleansedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------
|
||||||
|
Checklists
|
||||||
|
------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
function focusChecklist(task, index) {
|
||||||
|
window.setTimeout(function(){
|
||||||
|
$('#task-'+task._id+' .checklist-form input[type="text"]')[index].focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addChecklist(task) {
|
||||||
|
task._edit.checklist = [{completed:false, text:""}];
|
||||||
|
focusChecklist(task._edit,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addChecklistItemToUI(task, $event, $index) {
|
||||||
|
if (task._edit.checklist[$index].text) {
|
||||||
|
if ($index === task._edit.checklist.length - 1) {
|
||||||
|
task._edit.checklist.push({ completed: false, text: '' });
|
||||||
|
}
|
||||||
|
focusChecklist(task._edit, $index + 1);
|
||||||
|
} else {
|
||||||
|
// TODO Provide UI feedback that this item is still blank
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeChecklistItemFromUI(task, $event, $index, force) {
|
||||||
|
// Remove item if clicked on trash icon
|
||||||
|
if (force) {
|
||||||
|
task._edit.checklist.splice($index, 1);
|
||||||
|
} else if (!task._edit.checklist[$index].text) {
|
||||||
|
// User deleted all the text and is now wishing to delete the item
|
||||||
|
// saveTask will prune the empty item
|
||||||
|
// Move focus if the list is still non-empty
|
||||||
|
if ($index > 0)
|
||||||
|
focusChecklist(task._edit, $index-1);
|
||||||
|
// Don't allow the backspace key to navigate back now that the field is gone
|
||||||
|
$event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function swapChecklistItems(task, oldIndex, newIndex) {
|
||||||
|
var toSwap = task._edit.checklist.splice(oldIndex, 1)[0];
|
||||||
|
task._edit.checklist.splice(newIndex, 0, toSwap);
|
||||||
|
}
|
||||||
|
|
||||||
|
function navigateChecklist(task,$index,$event) {
|
||||||
|
focusChecklist(task, $event.keyCode == '40' ? $index+1 : $index-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checklistCompletion(checklist) {
|
||||||
|
return _.reduce(checklist,function(m,i){return m+(i.completed ? 1 : 0);},0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function collapseChecklist(task) {
|
||||||
|
task.collapseChecklist = !task.collapseChecklist;
|
||||||
|
saveTask(task, true);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
addTasks: addTasks,
|
addTasks: addTasks,
|
||||||
toggleBulk: toggleBulk,
|
toggleBulk: toggleBulk,
|
||||||
getUserTasks: getUserTasks,
|
getUserTasks: getUserTasks,
|
||||||
|
removeTask: removeTask,
|
||||||
|
saveTask: saveTask,
|
||||||
loadedCompletedTodos: false,
|
loadedCompletedTodos: false,
|
||||||
createUserTasks: createUserTasks,
|
createUserTasks: createUserTasks,
|
||||||
getChallengeTasks: getChallengeTasks,
|
getChallengeTasks: getChallengeTasks,
|
||||||
createChallengeTasks: createChallengeTasks,
|
createChallengeTasks: createChallengeTasks,
|
||||||
|
getGroupTasks: getGroupTasks,
|
||||||
|
createGroupTasks: createGroupTasks,
|
||||||
getTask: getTask,
|
getTask: getTask,
|
||||||
updateTask: updateTask,
|
updateTask: updateTask,
|
||||||
deleteTask: deleteTask,
|
deleteTask: deleteTask,
|
||||||
@@ -235,6 +356,16 @@ angular.module('habitrpg')
|
|||||||
clearCompletedTodos: clearCompletedTodos,
|
clearCompletedTodos: clearCompletedTodos,
|
||||||
editTask: editTask,
|
editTask: editTask,
|
||||||
cancelTaskEdit: cancelTaskEdit,
|
cancelTaskEdit: cancelTaskEdit,
|
||||||
cloneTask: cloneTask
|
cloneTask: cloneTask,
|
||||||
|
assignTask: assignTask,
|
||||||
|
unAssignTask: unAssignTask,
|
||||||
|
|
||||||
|
addChecklist: addChecklist,
|
||||||
|
addChecklistItemToUI: addChecklistItemToUI,
|
||||||
|
removeChecklistItemFromUI: removeChecklistItemFromUI,
|
||||||
|
swapChecklistItems: swapChecklistItems,
|
||||||
|
navigateChecklist: navigateChecklist,
|
||||||
|
checklistCompletion: checklistCompletion,
|
||||||
|
collapseChecklist: collapseChecklist,
|
||||||
};
|
};
|
||||||
}]);
|
}]);
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,63 +1,63 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
viewBox="206.8 158.6 1132.2 280" style="enable-background:new 206.8 158.6 1132.2 280;" xml:space="preserve">
|
viewBox="206.8 158.6 1132.2 280" style="enable-background:new 206.8 158.6 1132.2 280;" xml:space="preserve">
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
.st0{fill:#30ABE9;}
|
.st0{fill:#30ABE9;}
|
||||||
</style>
|
</style>
|
||||||
<path class="st0" d="M500.9,182.9c-0.3-0.1-0.6-0.2-0.8-0.2H500c-1.3-0.3-2.5-0.6-3.7-0.9l0,0h-0.1l0,0l-0.3-0.1l-0.6-0.1l0,0l0,0
|
<path class="st0" d="M500.9,182.9c-0.3-0.1-0.6-0.2-0.8-0.2H500c-1.3-0.3-2.5-0.6-3.7-0.9l0,0h-0.1l0,0l-0.3-0.1l-0.6-0.1l0,0l0,0
|
||||||
l0,0h-0.1l0,0l0,0l0,0h-0.1l0,0l0,0l-0.5-0.1l0,0c-10.4-2.4-21.1-4.4-31.8-5.7c-11.1-1.4-22.5-2.3-33.8-2.5c-2.2,0-4.5-0.1-6.7-0.1
|
l0,0h-0.1l0,0l0,0l0,0h-0.1l0,0l0,0l-0.5-0.1l0,0c-10.4-2.4-21.1-4.4-31.8-5.7c-11.1-1.4-22.5-2.3-33.8-2.5c-2.2,0-4.5-0.1-6.7-0.1
|
||||||
c-7.1,0-14.2,0.2-21.2,0.7h-0.5l0.1,0.5c1.1,9.2-1.8,18.4-8,25.3c-5.4,6-13.1,9.8-21.1,10.4c-0.8,0.1-1.6,0.1-2.5,0.1
|
c-7.1,0-14.2,0.2-21.2,0.7h-0.5l0.1,0.5c1.1,9.2-1.8,18.4-8,25.3c-5.4,6-13.1,9.8-21.1,10.4c-0.8,0.1-1.6,0.1-2.5,0.1
|
||||||
c-8,0-15.7-3-21.6-8.4c-4.8-4.4-8.3-10.4-9.7-16.7l-0.1-0.5l-0.4,0.1c-10.6,3-21.2,6.5-31.5,10.6c-10,4-19.9,8.5-29.4,13.5
|
c-8,0-15.7-3-21.6-8.4c-4.8-4.4-8.3-10.4-9.7-16.7l-0.1-0.5l-0.4,0.1c-10.6,3-21.2,6.5-31.5,10.6c-10,4-19.9,8.5-29.4,13.5
|
||||||
c-4.8,2.5-9.5,5.1-14.1,7.8c-2.4,1.4-4.9,2.9-7.3,4.4c-0.1,0-0.2,0.1-0.4,0.2c-0.5,0.3-1.2,0.8-1.9,1.2c-1.3,0.8-1.5,0.9-1.3,1.3
|
c-4.8,2.5-9.5,5.1-14.1,7.8c-2.4,1.4-4.9,2.9-7.3,4.4c-0.1,0-0.2,0.1-0.4,0.2c-0.5,0.3-1.2,0.8-1.9,1.2c-1.3,0.8-1.5,0.9-1.3,1.3
|
||||||
l27.3,60l0.2,0.4l0.4-0.2c9.8-5.7,20-10.9,30.3-15.5c0.7-0.3,1.3-0.6,2-0.9l21.4,89.2l0.1,0.6l0.5-0.3c5.7-3.1,10.4-5.4,15-7.3
|
l27.3,60l0.2,0.4l0.4-0.2c9.8-5.7,20-10.9,30.3-15.5c0.7-0.3,1.3-0.6,2-0.9l21.4,89.2l0.1,0.6l0.5-0.3c5.7-3.1,10.4-5.4,15-7.3
|
||||||
c5.7-2.3,11-4.2,16.1-5.6c9.5-2.7,19.2-4,29.1-4c1.5,0,3.1,0,4.6,0.1c4.7,0.2,10.7,0.7,16.3,2.9c2.6,1,4.6,2.2,6.1,3.7
|
c5.7-2.3,11-4.2,16.1-5.6c9.5-2.7,19.2-4,29.1-4c1.5,0,3.1,0,4.6,0.1c4.7,0.2,10.7,0.7,16.3,2.9c2.6,1,4.6,2.2,6.1,3.7
|
||||||
c1.7,1.7,2.6,4,2.5,6.5c0,5.2-4.1,9.4-7.4,12.1c-2.2,1.7-4.6,3.1-6.4,4.1c-2.3,1.3-4.8,2.4-7.3,3.4c-4.9,2-10.1,3.5-16,4.7
|
c1.7,1.7,2.6,4,2.5,6.5c0,5.2-4.1,9.4-7.4,12.1c-2.2,1.7-4.6,3.1-6.4,4.1c-2.3,1.3-4.8,2.4-7.3,3.4c-4.9,2-10.1,3.5-16,4.7
|
||||||
c-5.8,1.2-11.5,1.9-16.9,2.1c-0.5,0-1,0-1.6,0c-0.7,0-1.3,0-2,0h-0.5c-1.4,0-2.8-0.1-4.2-0.3c-1.5-0.2-2.7-0.3-4-0.7h-0.1
|
c-5.8,1.2-11.5,1.9-16.9,2.1c-0.5,0-1,0-1.6,0c-0.7,0-1.3,0-2,0h-0.5c-1.4,0-2.8-0.1-4.2-0.3c-1.5-0.2-2.7-0.3-4-0.7h-0.1
|
||||||
c-1.3-0.3-1.7-0.6-1.9-0.7c0.6-0.9,1.6-1.6,2.5-2.2c1-0.6,2.1-1.3,3.4-1.9c2.2-1.1,4.7-2.1,7.7-3c5.1-1.6,10.6-2.7,16.5-3.2
|
c-1.3-0.3-1.7-0.6-1.9-0.7c0.6-0.9,1.6-1.6,2.5-2.2c1-0.6,2.1-1.3,3.4-1.9c2.2-1.1,4.7-2.1,7.7-3c5.1-1.6,10.6-2.7,16.5-3.2
|
||||||
c2.8-0.3,5.6-0.4,8.4-0.4c0.3,0,0.7,0,1,0c0.9,0,2.1,0,3.2,0.1c0.2,0,0.5,0,0.7,0.1c0.4,0,0.8,0.1,1.2,0.1h0.1h0.1
|
c2.8-0.3,5.6-0.4,8.4-0.4c0.3,0,0.7,0,1,0c0.9,0,2.1,0,3.2,0.1c0.2,0,0.5,0,0.7,0.1c0.4,0,0.8,0.1,1.2,0.1h0.1h0.1
|
||||||
c3-1,13.1-4.9,15.9-11.4l0.2-0.4l-0.4-0.2c-0.1-0.1-0.3-0.1-0.5-0.2c-0.1,0-0.1,0-0.2-0.1c-1.1-0.3-2.5-0.8-4-1.1
|
c3-1,13.1-4.9,15.9-11.4l0.2-0.4l-0.4-0.2c-0.1-0.1-0.3-0.1-0.5-0.2c-0.1,0-0.1,0-0.2-0.1c-1.1-0.3-2.5-0.8-4-1.1
|
||||||
c-2.6-0.6-5.4-1.1-8.4-1.5c-3.1-0.4-6.3-0.5-9.5-0.5c-2.6,0-5.2,0.1-7.8,0.4c-6.3,0.6-11.8,1.5-16.7,2.8c-6,1.5-11.1,3.4-15.5,5.8
|
c-2.6-0.6-5.4-1.1-8.4-1.5c-3.1-0.4-6.3-0.5-9.5-0.5c-2.6,0-5.2,0.1-7.8,0.4c-6.3,0.6-11.8,1.5-16.7,2.8c-6,1.5-11.1,3.4-15.5,5.8
|
||||||
c-5.5,2.9-9.3,6.4-11.5,10.4c-2.4,4.4-2.5,9.6-0.3,13.7c2.1,4,6.3,6.8,12.3,8.4c4,1,8.5,1.6,13.7,1.6c0.9,0,1.9,0,2.9,0
|
c-5.5,2.9-9.3,6.4-11.5,10.4c-2.4,4.4-2.5,9.6-0.3,13.7c2.1,4,6.3,6.8,12.3,8.4c4,1,8.5,1.6,13.7,1.6c0.9,0,1.9,0,2.9,0
|
||||||
c5.4-0.2,11.1-0.8,16.9-1.9c5.6-1.1,11.1-2.5,16.3-4.3c5.7-2,10.7-4.2,15.1-6.7c0.7-0.4,1.2-0.7,1.7-1l1.3-0.8c0,0.2,0,0.4,0,0.6
|
c5.4-0.2,11.1-0.8,16.9-1.9c5.6-1.1,11.1-2.5,16.3-4.3c5.7-2,10.7-4.2,15.1-6.7c0.7-0.4,1.2-0.7,1.7-1l1.3-0.8c0,0.2,0,0.4,0,0.6
|
||||||
c0,0.6,0,1.1-0.2,1.8c-0.4,2.3-1.3,4.5-2.9,6.6c-2.5,3.4-6.3,6.3-11.6,9c-4.4,2.2-9.4,4.1-15.2,5.8c-5.3,1.5-10.9,2.7-16.4,3.5
|
c0,0.6,0,1.1-0.2,1.8c-0.4,2.3-1.3,4.5-2.9,6.6c-2.5,3.4-6.3,6.3-11.6,9c-4.4,2.2-9.4,4.1-15.2,5.8c-5.3,1.5-10.9,2.7-16.4,3.5
|
||||||
c-1.2,0.2-2.4,0.3-3.5,0.5l-0.7,0.1c-0.3,0-0.7,0.1-1,0.1c-0.3,0-0.8,0.1-0.8,0.1h-0.4v0.4l0.8,14.4v0.5h0.5l2.3-0.2
|
c-1.2,0.2-2.4,0.3-3.5,0.5l-0.7,0.1c-0.3,0-0.7,0.1-1,0.1c-0.3,0-0.8,0.1-0.8,0.1h-0.4v0.4l0.8,14.4v0.5h0.5l2.3-0.2
|
||||||
c2.7-0.3,5.6-0.7,8.5-1.2c12.6-2.2,23-5.4,31.8-9.7c11.4-5.7,18.9-13,22.2-21.7c2-5.4,2.5-11.4,1.3-16.7c-0.3-1.3-0.7-2.6-1.2-3.8
|
c2.7-0.3,5.6-0.7,8.5-1.2c12.6-2.2,23-5.4,31.8-9.7c11.4-5.7,18.9-13,22.2-21.7c2-5.4,2.5-11.4,1.3-16.7c-0.3-1.3-0.7-2.6-1.2-3.8
|
||||||
c-0.1-0.2-0.2-0.4-0.3-0.7c-0.2-0.3-0.4-0.7-0.4-1c-0.1-0.2,0.2-0.7,0.5-1c0.1-0.1,0.2-0.3,0.3-0.4c1.4-2.3,2.5-4.8,3.2-7.5
|
c-0.1-0.2-0.2-0.4-0.3-0.7c-0.2-0.3-0.4-0.7-0.4-1c-0.1-0.2,0.2-0.7,0.5-1c0.1-0.1,0.2-0.3,0.3-0.4c1.4-2.3,2.5-4.8,3.2-7.5
|
||||||
c1.3-5.2,1-10.8-0.8-15.6c-1.8-4.7-5.2-8.8-9.9-11.8c-4.2-2.7-9.2-4.7-15.4-6c-5.2-1.1-10.7-1.6-16.9-1.6c-11.7,0-23.2,1.5-34,4.6
|
c1.3-5.2,1-10.8-0.8-15.6c-1.8-4.7-5.2-8.8-9.9-11.8c-4.2-2.7-9.2-4.7-15.4-6c-5.2-1.1-10.7-1.6-16.9-1.6c-11.7,0-23.2,1.5-34,4.6
|
||||||
c-5.6,1.6-11.4,3.6-18.4,6.4l-20.9-86.8l-0.1-0.5l-0.5,0.2c-10.6,4-21.1,8.5-31.3,13.6c-1.5,0.7-2.9,1.5-4.4,2.2l-16.3-35.7
|
c-5.6,1.6-11.4,3.6-18.4,6.4l-20.9-86.8l-0.1-0.5l-0.5,0.2c-10.6,4-21.1,8.5-31.3,13.6c-1.5,0.7-2.9,1.5-4.4,2.2l-16.3-35.7
|
||||||
c0.3-0.2,0.7-0.4,1.4-0.8c0.1-0.1,0.2-0.1,0.2-0.1c4.7-2.7,9.5-5.3,14.3-7.8c9.6-4.9,19.5-9.3,29.6-13.2c4.9-1.9,9.9-3.6,14.9-5.3
|
c0.3-0.2,0.7-0.4,1.4-0.8c0.1-0.1,0.2-0.1,0.2-0.1c4.7-2.7,9.5-5.3,14.3-7.8c9.6-4.9,19.5-9.3,29.6-13.2c4.9-1.9,9.9-3.6,14.9-5.3
|
||||||
c2.1,3.6,4.7,6.8,7.7,9.7c6.3,6,14.1,10.1,22.6,11.9c3,0.6,6,0.9,9.1,0.9c5.6,0,11-1,16.1-2.9c7.5-2.8,14.2-7.6,19.3-13.8
|
c2.1,3.6,4.7,6.8,7.7,9.7c6.3,6,14.1,10.1,22.6,11.9c3,0.6,6,0.9,9.1,0.9c5.6,0,11-1,16.1-2.9c7.5-2.8,14.2-7.6,19.3-13.8
|
||||||
c2.6-3.1,4.7-6.5,6.4-10.1c0.8-1.8,1.6-3.7,2.2-5.7c0.3-1,0.6-2,0.8-3c0.1-0.5,0.2-1,0.3-1.4l0,0h0.2c0.4,0,0.8,0,1.2,0
|
c2.6-3.1,4.7-6.5,6.4-10.1c0.8-1.8,1.6-3.7,2.2-5.7c0.3-1,0.6-2,0.8-3c0.1-0.5,0.2-1,0.3-1.4l0,0h0.2c0.4,0,0.8,0,1.2,0
|
||||||
c2.3-0.1,4.6-0.1,6.9-0.1c9.1,0,18.3,0.4,27.3,1.2c11.3,1,22.6,2.7,33.7,4.9h0.2c1,0.2,1.6,0.3,1.9,0.4l-7.9,38.7
|
c2.3-0.1,4.6-0.1,6.9-0.1c9.1,0,18.3,0.4,27.3,1.2c11.3,1,22.6,2.7,33.7,4.9h0.2c1,0.2,1.6,0.3,1.9,0.4l-7.9,38.7
|
||||||
c-0.9-0.1-3.2-0.4-4.9-0.7c-1.2-0.2-2.1-0.3-2.4-0.3c-2.8-0.4-5.7-0.7-8.6-0.9c-5.7-0.6-11.5-1-17.3-1.2c-2.9-0.1-5.8-0.2-8.7-0.2
|
c-0.9-0.1-3.2-0.4-4.9-0.7c-1.2-0.2-2.1-0.3-2.4-0.3c-2.8-0.4-5.7-0.7-8.6-0.9c-5.7-0.6-11.5-1-17.3-1.2c-2.9-0.1-5.8-0.2-8.7-0.2
|
||||||
c-0.1,0-0.7,0-0.7,0h-0.4v0.4l-3.6,85.5c0,0.2,0.1,0.5,0.5,0.6c0,0,0,0,0.1,0c1.2,0.6,2.4,1.2,3.6,1.8c2.5,1.4,4.7,3,6.5,4.7
|
c-0.1,0-0.7,0-0.7,0h-0.4v0.4l-3.6,85.5c0,0.2,0.1,0.5,0.5,0.6c0,0,0,0,0.1,0c1.2,0.6,2.4,1.2,3.6,1.8c2.5,1.4,4.7,3,6.5,4.7
|
||||||
c0.5,0.4,0.9,0.9,1.3,1.3c0.2,0.2,0.4,0.5,0.6,0.7c0.1,0.1,0.2,0.3,0.3,0.4l0.7,1.4l0.1-1.6l3.8-80.9c11.2,0.6,22.2,1.9,33.6,3.8
|
c0.5,0.4,0.9,0.9,1.3,1.3c0.2,0.2,0.4,0.5,0.6,0.7c0.1,0.1,0.2,0.3,0.3,0.4l0.7,1.4l0.1-1.6l3.8-80.9c11.2,0.6,22.2,1.9,33.6,3.8
|
||||||
c1.4,0.2,2.8,0.5,4.3,0.8l0.5,0.1c1.4,0.3,1.4,0.3,1.4,0.3h0.4l0.1-0.4l13.1-64.5C501.9,183.1,501.5,183,500.9,182.9z"/>
|
c1.4,0.2,2.8,0.5,4.3,0.8l0.5,0.1c1.4,0.3,1.4,0.3,1.4,0.3h0.4l0.1-0.4l13.1-64.5C501.9,183.1,501.5,183,500.9,182.9z"/>
|
||||||
<g>
|
<g>
|
||||||
<path class="st0" d="M522.4,339.3v-53h-15.2V269h15.2v-25.1h20V269H561v17.3h-18.6v48.1c0,6.3,3,11,8.7,11c3.8,0,7.2-1.7,8.7-3.4
|
<path class="st0" d="M522.4,339.3v-53h-15.2V269h15.2v-25.1h20V269H561v17.3h-18.6v48.1c0,6.3,3,11,8.7,11c3.8,0,7.2-1.7,8.7-3.4
|
||||||
l4.8,15.2c-3.6,3.2-9.5,5.9-18.6,5.9C530.4,363.1,522.4,354.7,522.4,339.3z"/>
|
l4.8,15.2c-3.6,3.2-9.5,5.9-18.6,5.9C530.4,363.1,522.4,354.7,522.4,339.3z"/>
|
||||||
<path class="st0" d="M573.3,314.8c0-26.6,19.4-48.1,46.6-48.1c27.5,0,45,20.9,45,49.8v4.9h-70.7c1.5,13.7,11.8,25.3,29.1,25.3
|
<path class="st0" d="M573.3,314.8c0-26.6,19.4-48.1,46.6-48.1c27.5,0,45,20.9,45,49.8v4.9h-70.7c1.5,13.7,11.8,25.3,29.1,25.3
|
||||||
c9.1,0,19.6-3.6,26.2-10.3l9.1,13.1c-9.3,8.7-22.8,13.5-37.2,13.5C593.6,363.1,573.3,343.9,573.3,314.8z M619.7,283.1
|
c9.1,0,19.6-3.6,26.2-10.3l9.1,13.1c-9.3,8.7-22.8,13.5-37.2,13.5C593.6,363.1,573.3,343.9,573.3,314.8z M619.7,283.1
|
||||||
c-16.9,0-24.9,13.1-25.7,24.1h51.7C645.1,296.4,637.7,283.1,619.7,283.1z"/>
|
c-16.9,0-24.9,13.1-25.7,24.1h51.7C645.1,296.4,637.7,283.1,619.7,283.1z"/>
|
||||||
<path class="st0" d="M679.1,314.8c0-26.6,19.4-48.1,46.6-48.1c27.5,0,45,20.9,45,49.8v4.9H700c1.5,13.7,11.8,25.3,29.1,25.3
|
<path class="st0" d="M679.1,314.8c0-26.6,19.4-48.1,46.6-48.1c27.5,0,45,20.9,45,49.8v4.9H700c1.5,13.7,11.8,25.3,29.1,25.3
|
||||||
c9.1,0,19.6-3.6,26.2-10.3l9.1,13.1c-9.3,8.7-22.8,13.5-37.2,13.5C699.5,363.1,679.1,343.9,679.1,314.8z M725.5,283.1
|
c9.1,0,19.6-3.6,26.2-10.3l9.1,13.1c-9.3,8.7-22.8,13.5-37.2,13.5C699.5,363.1,679.1,343.9,679.1,314.8z M725.5,283.1
|
||||||
c-16.9,0-24.9,13.1-25.7,24.1h51.7C751,296.4,743.5,283.1,725.5,283.1z"/>
|
c-16.9,0-24.9,13.1-25.7,24.1h51.7C751,296.4,743.5,283.1,725.5,283.1z"/>
|
||||||
<path class="st0" d="M782.7,348.6l9.1-14.2c6.5,6.6,19.2,12.7,31,12.7c12,0,18.2-4.9,18.2-12.2c0-18.1-55.5-5.1-55.5-40.5
|
<path class="st0" d="M782.7,348.6l9.1-14.2c6.5,6.6,19.2,12.7,31,12.7c12,0,18.2-4.9,18.2-12.2c0-18.1-55.5-5.1-55.5-40.5
|
||||||
c0-15,12.9-27.7,36.1-27.7c15.6,0,27.4,5.5,35.5,12.7l-8.4,14.1c-5.5-6.1-15.6-11-27.2-11c-10.4,0-16.9,4.9-16.9,11.4
|
c0-15,12.9-27.7,36.1-27.7c15.6,0,27.4,5.5,35.5,12.7l-8.4,14.1c-5.5-6.1-15.6-11-27.2-11c-10.4,0-16.9,4.9-16.9,11.4
|
||||||
c0,16.2,55.5,4,55.5,40.5c0,16.3-13.5,28.7-38.2,28.7C806.2,363.1,792.2,357.9,782.7,348.6z"/>
|
c0,16.2,55.5,4,55.5,40.5c0,16.3-13.5,28.7-38.2,28.7C806.2,363.1,792.2,357.9,782.7,348.6z"/>
|
||||||
<path class="st0" d="M900.1,348.1v47.7h-20V269h20v12.5c6.8-9.1,17.5-14.8,29.5-14.8c23.8,0,40.7,17.9,40.7,48.1
|
<path class="st0" d="M900.1,348.1v47.7h-20V269h20v12.5c6.8-9.1,17.5-14.8,29.5-14.8c23.8,0,40.7,17.9,40.7,48.1
|
||||||
s-16.9,48.3-40.7,48.3C918,363.1,907.7,357.8,900.1,348.1z M949.7,314.8c0-17.9-10.3-30.4-26-30.4c-9.3,0-19,5.3-23.6,12.2v36.3
|
s-16.9,48.3-40.7,48.3C918,363.1,907.7,357.8,900.1,348.1z M949.7,314.8c0-17.9-10.3-30.4-26-30.4c-9.3,0-19,5.3-23.6,12.2v36.3
|
||||||
c4.4,6.8,14.3,12.5,23.6,12.5C939.4,345.4,949.7,332.7,949.7,314.8z"/>
|
c4.4,6.8,14.3,12.5,23.6,12.5C939.4,345.4,949.7,332.7,949.7,314.8z"/>
|
||||||
<path class="st0" d="M990.2,360.8V269h20v13.3c7-8.7,18.1-15.6,29.8-15.6v19.8c-1.7-0.4-3.6-0.6-6.1-0.6c-8.6,0-19.8,5.7-23.8,12.4
|
<path class="st0" d="M990.2,360.8V269h20v13.3c7-8.7,18.1-15.6,29.8-15.6v19.8c-1.7-0.4-3.6-0.6-6.1-0.6c-8.6,0-19.8,5.7-23.8,12.4
|
||||||
v62.5H990.2z"/>
|
v62.5H990.2z"/>
|
||||||
<path class="st0" d="M1053.8,245.5c0-6.8,5.5-12.4,12.3-12.4c6.8,0,12.4,5.5,12.4,12.4c0,6.8-5.5,12.4-12.4,12.4
|
<path class="st0" d="M1053.8,245.5c0-6.8,5.5-12.4,12.3-12.4c6.8,0,12.4,5.5,12.4,12.4c0,6.8-5.5,12.4-12.4,12.4
|
||||||
S1053.8,252.3,1053.8,245.5z M1056.1,360.8V269h20v91.8H1056.1z"/>
|
S1053.8,252.3,1053.8,245.5z M1056.1,360.8V269h20v91.8H1056.1z"/>
|
||||||
<path class="st0" d="M1164,360.8v-57.4c0-14.4-7.4-19-18.6-19c-10.1,0-18.8,6.1-23.6,12.2v64.2h-20V269h20v12.4
|
<path class="st0" d="M1164,360.8v-57.4c0-14.4-7.4-19-18.6-19c-10.1,0-18.8,6.1-23.6,12.2v64.2h-20V269h20v12.4
|
||||||
c6.1-7.2,18.1-14.6,32.5-14.6c19.8,0,29.6,10.6,29.6,29.5v64.6L1164,360.8L1164,360.8z"/>
|
c6.1-7.2,18.1-14.6,32.5-14.6c19.8,0,29.6,10.6,29.6,29.5v64.6L1164,360.8L1164,360.8z"/>
|
||||||
<path class="st0" d="M1208.7,384.4l9.3-14.4c7.6,8.6,17.1,11.8,29.3,11.8c13.1,0,27.2-6.1,27.2-25.7v-10.5
|
<path class="st0" d="M1208.7,384.4l9.3-14.4c7.6,8.6,17.1,11.8,29.3,11.8c13.1,0,27.2-6.1,27.2-25.7v-10.5
|
||||||
c-7,9.3-17.5,15.4-29.5,15.4c-23.4,0-40.7-16.9-40.7-47.1c0-29.8,16.9-47.1,40.7-47.1c11.6,0,22,5.1,29.5,15v-12.7h20v87
|
c-7,9.3-17.5,15.4-29.5,15.4c-23.4,0-40.7-16.9-40.7-47.1c0-29.8,16.9-47.1,40.7-47.1c11.6,0,22,5.1,29.5,15v-12.7h20v87
|
||||||
c0,33.1-24.7,42-47.1,42C1231.6,398,1220.1,394.6,1208.7,384.4z M1274.4,331v-34.4c-4.6-6.7-14.2-12.2-23.6-12.2
|
c0,33.1-24.7,42-47.1,42C1231.6,398,1220.1,394.6,1208.7,384.4z M1274.4,331v-34.4c-4.6-6.7-14.2-12.2-23.6-12.2
|
||||||
c-16,0-26,11.8-26,29.5c0,17.7,10.1,29.3,26,29.3C1260.1,343.1,1269.8,337.6,1274.4,331z"/>
|
c-16,0-26,11.8-26,29.5c0,17.7,10.1,29.3,26,29.3C1260.1,343.1,1269.8,337.6,1274.4,331z"/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.2 KiB |
@@ -209,5 +209,8 @@
|
|||||||
"exportInboxPopoverBody": "HTML allows easy reading of messages in a browser. For a machine-readable format, use Data > Export Data",
|
"exportInboxPopoverBody": "HTML allows easy reading of messages in a browser. For a machine-readable format, use Data > Export Data",
|
||||||
"to": "To:",
|
"to": "To:",
|
||||||
"from": "From:",
|
"from": "From:",
|
||||||
"desktopNotificationsText": "We need your permission to enable desktop notifications for new messages in party chat! Follow your browser's instructions to turn them on.<br><br>You'll receive these notifications only while you have Habitica open. If you decide you don't like them, they can be disabled in your browser's settings.<br><br>This box will close automatically when a decision is made."
|
"desktopNotificationsText": "We need your permission to enable desktop notifications for new messages in party chat! Follow your browser's instructions to turn them on.<br><br>You'll receive these notifications only while you have Habitica open. If you decide you don't like them, they can be disabled in your browser's settings.<br><br>This box will close automatically when a decision is made.",
|
||||||
|
"confirmAddTag": "Do you really want to add \"<%= tag %>\"?",
|
||||||
|
"confirmRemoveTag": "Do you really want to remove \"<%= tag %>\"?",
|
||||||
|
"assignTask": "Assign Task"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,6 +259,8 @@ api.updateTask = {
|
|||||||
|
|
||||||
if (challenge) {
|
if (challenge) {
|
||||||
challenge.updateTask(savedTask);
|
challenge.updateTask(savedTask);
|
||||||
|
} else if (group && task.group.id && task.group.assignedUsers.length > 0) {
|
||||||
|
await group.updateTask(savedTask);
|
||||||
} else {
|
} else {
|
||||||
taskActivityWebhook.send(user.webhooks, {
|
taskActivityWebhook.send(user.webhooks, {
|
||||||
type: 'updated',
|
type: 'updated',
|
||||||
@@ -435,6 +437,7 @@ api.addChecklistItem = {
|
|||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
let user = res.locals.user;
|
let user = res.locals.user;
|
||||||
let challenge;
|
let challenge;
|
||||||
|
let group;
|
||||||
|
|
||||||
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty();
|
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty();
|
||||||
|
|
||||||
@@ -446,6 +449,10 @@ api.addChecklistItem = {
|
|||||||
|
|
||||||
if (!task) {
|
if (!task) {
|
||||||
throw new NotFound(res.t('taskNotFound'));
|
throw new NotFound(res.t('taskNotFound'));
|
||||||
|
} else if (task.group.id && !task.userId) {
|
||||||
|
group = await Group.getGroup({user, groupId: task.group.id, fields: requiredGroupFields});
|
||||||
|
if (!group) throw new NotFound(res.t('groupNotFound'));
|
||||||
|
if (group.leader !== user._id) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks'));
|
||||||
} else if (task.challenge.id && !task.userId) { // If the task belongs to a challenge make sure the user has rights
|
} else if (task.challenge.id && !task.userId) { // If the task belongs to a challenge make sure the user has rights
|
||||||
challenge = await Challenge.findOne({_id: task.challenge.id}).exec();
|
challenge = await Challenge.findOne({_id: task.challenge.id}).exec();
|
||||||
if (!challenge) throw new NotFound(res.t('challengeNotFound'));
|
if (!challenge) throw new NotFound(res.t('challengeNotFound'));
|
||||||
@@ -461,6 +468,9 @@ api.addChecklistItem = {
|
|||||||
|
|
||||||
res.respond(200, savedTask);
|
res.respond(200, savedTask);
|
||||||
if (challenge) challenge.updateTask(savedTask);
|
if (challenge) challenge.updateTask(savedTask);
|
||||||
|
if (group && task.group.id && task.group.assignedUsers.length > 0) {
|
||||||
|
await group.updateTask(savedTask);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -522,6 +532,7 @@ api.updateChecklistItem = {
|
|||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
let user = res.locals.user;
|
let user = res.locals.user;
|
||||||
let challenge;
|
let challenge;
|
||||||
|
let group;
|
||||||
|
|
||||||
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty();
|
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty();
|
||||||
req.checkParams('itemId', res.t('itemIdRequired')).notEmpty().isUUID();
|
req.checkParams('itemId', res.t('itemIdRequired')).notEmpty().isUUID();
|
||||||
@@ -534,6 +545,10 @@ api.updateChecklistItem = {
|
|||||||
|
|
||||||
if (!task) {
|
if (!task) {
|
||||||
throw new NotFound(res.t('taskNotFound'));
|
throw new NotFound(res.t('taskNotFound'));
|
||||||
|
} else if (task.group.id && !task.userId) {
|
||||||
|
group = await Group.getGroup({user, groupId: task.group.id, fields: requiredGroupFields});
|
||||||
|
if (!group) throw new NotFound(res.t('groupNotFound'));
|
||||||
|
if (group.leader !== user._id) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks'));
|
||||||
} else if (task.challenge.id && !task.userId) { // If the task belongs to a challenge make sure the user has rights
|
} else if (task.challenge.id && !task.userId) { // If the task belongs to a challenge make sure the user has rights
|
||||||
challenge = await Challenge.findOne({_id: task.challenge.id}).exec();
|
challenge = await Challenge.findOne({_id: task.challenge.id}).exec();
|
||||||
if (!challenge) throw new NotFound(res.t('challengeNotFound'));
|
if (!challenge) throw new NotFound(res.t('challengeNotFound'));
|
||||||
@@ -551,6 +566,9 @@ api.updateChecklistItem = {
|
|||||||
|
|
||||||
res.respond(200, savedTask);
|
res.respond(200, savedTask);
|
||||||
if (challenge) challenge.updateTask(savedTask);
|
if (challenge) challenge.updateTask(savedTask);
|
||||||
|
if (group && task.group.id && task.group.assignedUsers.length > 0) {
|
||||||
|
await group.updateTask(savedTask);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -572,6 +590,7 @@ api.removeChecklistItem = {
|
|||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
let user = res.locals.user;
|
let user = res.locals.user;
|
||||||
let challenge;
|
let challenge;
|
||||||
|
let group;
|
||||||
|
|
||||||
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty();
|
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty();
|
||||||
req.checkParams('itemId', res.t('itemIdRequired')).notEmpty().isUUID();
|
req.checkParams('itemId', res.t('itemIdRequired')).notEmpty().isUUID();
|
||||||
@@ -584,6 +603,10 @@ api.removeChecklistItem = {
|
|||||||
|
|
||||||
if (!task) {
|
if (!task) {
|
||||||
throw new NotFound(res.t('taskNotFound'));
|
throw new NotFound(res.t('taskNotFound'));
|
||||||
|
} else if (task.group.id && !task.userId) {
|
||||||
|
group = await Group.getGroup({user, groupId: task.group.id, fields: requiredGroupFields});
|
||||||
|
if (!group) throw new NotFound(res.t('groupNotFound'));
|
||||||
|
if (group.leader !== user._id) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks'));
|
||||||
} else if (task.challenge.id && !task.userId) { // If the task belongs to a challenge make sure the user has rights
|
} else if (task.challenge.id && !task.userId) { // If the task belongs to a challenge make sure the user has rights
|
||||||
challenge = await Challenge.findOne({_id: task.challenge.id}).exec();
|
challenge = await Challenge.findOne({_id: task.challenge.id}).exec();
|
||||||
if (!challenge) throw new NotFound(res.t('challengeNotFound'));
|
if (!challenge) throw new NotFound(res.t('challengeNotFound'));
|
||||||
@@ -599,6 +622,9 @@ api.removeChecklistItem = {
|
|||||||
let savedTask = await task.save();
|
let savedTask = await task.save();
|
||||||
res.respond(200, savedTask);
|
res.respond(200, savedTask);
|
||||||
if (challenge) challenge.updateTask(savedTask);
|
if (challenge) challenge.updateTask(savedTask);
|
||||||
|
if (group && task.group.id && task.group.assignedUsers.length > 0) {
|
||||||
|
await group.updateTask(savedTask);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -90,13 +90,16 @@ export let schema = new Schema({
|
|||||||
todos: [{type: String, ref: 'Task'}],
|
todos: [{type: String, ref: 'Task'}],
|
||||||
rewards: [{type: String, ref: 'Task'}],
|
rewards: [{type: String, ref: 'Task'}],
|
||||||
},
|
},
|
||||||
|
purchased: {
|
||||||
|
active: {type: Boolean, default: false},
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
strict: true,
|
strict: true,
|
||||||
minimize: false, // So empty objects are returned
|
minimize: false, // So empty objects are returned
|
||||||
});
|
});
|
||||||
|
|
||||||
schema.plugin(baseModel, {
|
schema.plugin(baseModel, {
|
||||||
noSet: ['_id', 'balance', 'quest', 'memberCount', 'chat', 'challengeCount', 'tasksOrder'],
|
noSet: ['_id', 'balance', 'quest', 'memberCount', 'chat', 'challengeCount', 'tasksOrder', 'purchased'],
|
||||||
});
|
});
|
||||||
|
|
||||||
// A list of additional fields that cannot be updated (but can be set on creation)
|
// A list of additional fields that cannot be updated (but can be set on creation)
|
||||||
|
|||||||
@@ -147,7 +147,20 @@ a.pull-right.gem-wallet(ng-if='group.type!="party"', popover-trigger='mouseenter
|
|||||||
h3.popover-title {{group.leader.profile.name}}
|
h3.popover-title {{group.leader.profile.name}}
|
||||||
.popover-content
|
.popover-content
|
||||||
markdown(text='group._editing ? groupCopy.leaderMessage : group.leaderMessage')
|
markdown(text='group._editing ? groupCopy.leaderMessage : group.leaderMessage')
|
||||||
div(ng-controller='ChatCtrl')
|
|
||||||
|
ul.options-menu(ng-init="groupPane = 'chat'", ng-show="group.purchased.active")
|
||||||
|
li
|
||||||
|
a(ng-click="groupPane = 'chat'")
|
||||||
|
| Chat
|
||||||
|
li
|
||||||
|
a(ng-click="groupPane = 'tasks'")
|
||||||
|
| Tasks
|
||||||
|
|
||||||
|
|
||||||
|
.tab-content
|
||||||
|
.tab-pane.active
|
||||||
|
|
||||||
|
div(ng-controller='ChatCtrl', ng-show="groupPane == 'chat'")
|
||||||
.alert.alert-info.alert-sm(ng-if='group.memberCount > Shared.constants.LARGE_GROUP_COUNT_MESSAGE_CUTOFF')=env.t('largeGroupNote')
|
.alert.alert-info.alert-sm(ng-if='group.memberCount > Shared.constants.LARGE_GROUP_COUNT_MESSAGE_CUTOFF')=env.t('largeGroupNote')
|
||||||
h3=env.t('chat')
|
h3=env.t('chat')
|
||||||
include ./chat-box
|
include ./chat-box
|
||||||
@@ -155,3 +168,5 @@ a.pull-right.gem-wallet(ng-if='group.type!="party"', popover-trigger='mouseenter
|
|||||||
+chatMessages()
|
+chatMessages()
|
||||||
h4(ng-if='group.chat.length < 1 && group.type === "party"')=env.t('partyChatEmpty')
|
h4(ng-if='group.chat.length < 1 && group.type === "party"')=env.t('partyChatEmpty')
|
||||||
h4(ng-if='group.chat.length < 1 && group.type === "guild"')=env.t('guildChatEmpty')
|
h4(ng-if='group.chat.length < 1 && group.type === "guild"')=env.t('guildChatEmpty')
|
||||||
|
|
||||||
|
group-tasks(ng-show="groupPane == 'tasks'")
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
script(type='text/ng-template', id='partials/groups.members.autocomplete.html')
|
||||||
|
div#taggle.input.textarea.clearfix
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
script(type='text/ng-template', id='partials/groups.tasks.actions.html')
|
||||||
|
div(ng-if="group.leader._id === user._id", class="col-md-12")
|
||||||
|
strong=env.t('assignTask')
|
||||||
|
group-members-autocomplete(ng-model="assignedMembers")
|
||||||
5
website/views/options/social/groups/group-tasks.jade
Normal file
5
website/views/options/social/groups/group-tasks.jade
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
include ./group-tasks-actions
|
||||||
|
include ./group-members-autocomplete
|
||||||
|
|
||||||
|
script(type='text/ng-template', id='partials/groups.tasks.html')
|
||||||
|
habitrpg-tasks(main=false)
|
||||||
@@ -6,6 +6,7 @@ include ./hall
|
|||||||
include ./quests/index
|
include ./quests/index
|
||||||
include ./chat-message
|
include ./chat-message
|
||||||
include ./party
|
include ./party
|
||||||
|
include ./groups/group-tasks
|
||||||
|
|
||||||
script(type='text/ng-template', id='partials/options.social.inbox.html')
|
script(type='text/ng-template', id='partials/options.social.inbox.html')
|
||||||
.options-blurbmenu
|
.options-blurbmenu
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ div(ng-if='::task.type!="reward"')
|
|||||||
a.hint(href='http://habitica.wikia.com/wiki/Task_Alias', target='_blank', popover-trigger='mouseenter', popover="{{::env.t('taskAliasPopover')}} {{::task._edit.alias ? '\n\n\' + env.t('taskAliasPopoverWarning') : ''}}")=env.t('taskAlias')
|
a.hint(href='http://habitica.wikia.com/wiki/Task_Alias', target='_blank', popover-trigger='mouseenter', popover="{{::env.t('taskAliasPopover')}} {{::task._edit.alias ? '\n\n\' + env.t('taskAliasPopoverWarning') : ''}}")=env.t('taskAlias')
|
||||||
input.form-control(ng-model='task._edit.alias' type='text' placeholder=env.t('taskAliasPlaceholder'))
|
input.form-control(ng-model='task._edit.alias' type='text' placeholder=env.t('taskAliasPlaceholder'))
|
||||||
|
|
||||||
|
fieldset.option-group.advanced-option(ng-show="task._edit._advanced")
|
||||||
|
group-tasks-actions(ng-if="obj.type == 'guild'", task='task', group='obj')
|
||||||
|
|
||||||
div(ng-show='task._edit._advanced')
|
div(ng-show='task._edit._advanced')
|
||||||
div(ng-if='::task.type == "daily"')
|
div(ng-if='::task.type == "daily"')
|
||||||
.form-group
|
.form-group
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ include ./task_view/mixins
|
|||||||
script(id='templates/habitrpg-tasks.html', type="text/ng-template")
|
script(id='templates/habitrpg-tasks.html', type="text/ng-template")
|
||||||
.tasks-lists.container-fluid
|
.tasks-lists.container-fluid
|
||||||
.row
|
.row
|
||||||
.col-md-3.col-sm-6(ng-repeat='list in lists', ng-class='::{ "rewards-module": list.type==="reward", "new-row-md": list.type==="todo" }')
|
.col-md-3.col-sm-6(ng-repeat='list in lists', ng-class='::{ "rewards-module": list.type==="reward", "new-row-md": list.type==="todo", "col-md-2": obj.type }')
|
||||||
.task-column(class='{{::list.type}}s')
|
.task-column(class='{{::list.type}}s')
|
||||||
|
|
||||||
include ./task_view/graph
|
include ./task_view/graph
|
||||||
|
|||||||
Reference in New Issue
Block a user