Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Report Player Profile #14849

Closed
wants to merge 73 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
dda6180
fix(lint): remove console.info
Jul 6, 2023
55848c5
WIP(members): basic report a user API
Jul 10, 2023
c50cee0
fix(flagging): debug params issue
Jul 12, 2023
1b25394
Merge branch 'develop' into report-profile-modal
CuriousMagpie Jul 26, 2023
eb4021f
feat(content): upgrade profile page
CuriousMagpie Jul 26, 2023
e531985
WIP(user profile): one infinitesimal change that's hardly worth the e…
CuriousMagpie Jul 27, 2023
aaca48b
WIP(user profile): mostly css updates
CuriousMagpie Jul 28, 2023
ded4eee
WIP(user profile): start flex grid & tidy up CSS
CuriousMagpie Aug 2, 2023
dfdb305
WIP(user profile): layout
CuriousMagpie Aug 2, 2023
19a9fe3
WIP(user profile): adding buttons
CuriousMagpie Aug 3, 2023
4551dbf
WIP(user profile): styling the top portion of the modal
CuriousMagpie Aug 4, 2023
010f229
fix(lint): eof and const
Aug 7, 2023
f2be588
WIP(user profile): options menu
CuriousMagpie Aug 7, 2023
3834093
WIP(user profiles): working on the drop down menu
CuriousMagpie Aug 8, 2023
dd55cbc
WIP(user profile): workin on the hamburger (kebab?) menu
CuriousMagpie Aug 9, 2023
1da6af1
WIP(user profile): moved some CSS classes out of unscoped and into th…
CuriousMagpie Aug 10, 2023
c301a2b
Merge branch 'develop' into report-profile-modal
CuriousMagpie Aug 11, 2023
3ea48ab
WIP(user profile): dropdown menu and toggles and colors oh my
CuriousMagpie Aug 11, 2023
190fe04
Merge branch 'develop' into report-profile-modal
CuriousMagpie Aug 23, 2023
8fe263a
update(dependencies): ran npm install to update dependencies
CuriousMagpie Aug 23, 2023
1e05297
package updates
CuriousMagpie Aug 23, 2023
4a24d9b
Merge branch 'develop' into report-profile-modal
CuriousMagpie Aug 23, 2023
a1ceb2e
WIP(profile): create achievements component
CuriousMagpie Aug 25, 2023
b3ea48c
WIP(profile): work on achievement component
CuriousMagpie Aug 25, 2023
941194b
feat(content): add September pet quest bundle
CuriousMagpie Aug 29, 2023
9387b3a
WIP(profile): buttons
CuriousMagpie Aug 30, 2023
18b41ac
WIP(profile): moar buttonz
CuriousMagpie Aug 31, 2023
ce7d51a
Merge branch 'release' into sabrecat/report-profile
Aug 31, 2023
b01f323
WIP(profile): removed refactoring crud, located where close icon shou…
CuriousMagpie Aug 31, 2023
a8f5e25
feat(community): basic "report profile"
Aug 31, 2023
0aec386
Merge branch 'HabitRPG:develop' into report-profile-modal
CuriousMagpie Sep 1, 2023
d340f06
WIP(profile): fixed user not found error
CuriousMagpie Sep 1, 2023
8957c5c
Merge remote-tracking branch 'origin/report-profile-modal' into repor…
CuriousMagpie Sep 1, 2023
31f8210
Merge branch 'report-profile-modal' into sabrecat/report-profile
Sep 1, 2023
e143d36
WIP(profile): close icon moved to profile.vue
CuriousMagpie Sep 1, 2023
321a01b
WIP(profile): close button finally workinating
CuriousMagpie Sep 1, 2023
08d71cc
5.4.0
Sep 5, 2023
e072d7c
Merge branch 'develop' into release
Sep 5, 2023
d1177c3
Merge branch 'sabrecat/report-profile' into report-profile-modal
CuriousMagpie Sep 5, 2023
7b1de37
WIP(profile): remove shadowban tooltip
CuriousMagpie Sep 5, 2023
af6575e
WIP(profile): comment cleanup
CuriousMagpie Sep 6, 2023
a2989b2
Merge branch 'release' into report-profile-modal
Sep 6, 2023
7e9d57d
feat(profile): functional dropdown buttons
Sep 6, 2023
0691483
WIP(profile): Styling and string updates
CuriousMagpie Sep 6, 2023
edcf811
WIP(profile): dropdown draft
Sep 6, 2023
2e0723f
feat(moderation): unflag profile
Sep 7, 2023
ae2b614
WIP(profile): styling updates
CuriousMagpie Sep 7, 2023
ea2b007
fix(profile): focus behavior
Sep 7, 2023
6262a9b
Merge remote-tracking branch 'origin/report-profile-modal' into repor…
CuriousMagpie Sep 8, 2023
9986082
WIP(profile): fixed a comment, woohoo
CuriousMagpie Sep 8, 2023
ec85159
feat(profiles): load modal instead of page?
Sep 12, 2023
cdd87ab
Merge branch 'release' into 2023-09-pet-quest-bundle
Sep 12, 2023
ce70c73
fix(test): temporarily use real timer
Sep 12, 2023
9e25360
5.4.1
Sep 12, 2023
f8632bf
Merge branch 'release' into report-profile-modal
Sep 15, 2023
311c743
refactor(profile): remove page view
Sep 15, 2023
f55d74a
refactor(profiles): remove email feature
Sep 15, 2023
1d552f7
fix(import): remove empty import
Sep 15, 2023
f80928a
update(node): update node modules
CuriousMagpie Sep 18, 2023
ee9709a
WIP(profile): add banned banner, toggle switches now toggle, add "day…
CuriousMagpie Sep 18, 2023
f72eef6
feat(content): prebuild Fall Festival (#14869)
CuriousMagpie Sep 21, 2023
a06974d
chore(subproj): update images
Sep 21, 2023
a63cc84
5.5.0
Sep 21, 2023
b1d23ec
Merge branch 'release' into report-profile-modal
Sep 25, 2023
8f211ee
fix(profile): fix admin actions
Sep 25, 2023
bfefe5d
fix(profiles): moar layout fixes
Sep 26, 2023
9b644e9
fix(profile): adjust margin
Sep 26, 2023
1d3006a
chore(event): schedule spooky gems
Sep 27, 2023
c0bb56c
test(profiles): add integrations
Sep 27, 2023
b8b0d66
feat(content): October sub items
CuriousMagpie Sep 19, 2023
89fff49
5.6.0
Sep 29, 2023
541eadd
Merge branch 'release' into report-profile-modal
Oct 3, 2023
3aba0ab
fix(router): use state to pass modal launch info
Oct 3, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion habitica-images
Submodule habitica-images updated 55 files
+ achievements/achievement-duneBuddy2x.png
+ backgrounds/background_jack_o_lantern_stacks.png
+ backgrounds/background_monstrous_cave.png
+ backgrounds/background_spectral_candle_room.png
+ backgrounds/icons/icon_background_jack_o_lantern_stacks.png
+ backgrounds/icons/icon_background_monstrous_cave.png
+ backgrounds/icons/icon_background_spectral_candle_room.png
+ gear/armoire/head_armoire_blackSpookySorceryHat.png
+ gear/armoire/head_armoire_purpleSpookySorceryHat.png
+ gear/armoire/shop/shop_head_armoire_blackSpookySorceryHat.png
+ gear/armoire/shop/shop_head_armoire_purpleSpookySorceryHat.png
+ gear/armoire/shop/shop_weapon_armoire_ridingBroom.png
+ gear/armoire/weapon_armoire_ridingBroom.png
+ gear/events/fall/broad_armor_special_fall2023Healer.png
+ gear/events/fall/broad_armor_special_fall2023Mage.png
+ gear/events/fall/broad_armor_special_fall2023Rogue.png
+ gear/events/fall/broad_armor_special_fall2023Warrior.png
+ gear/events/fall/head_special_fall2023Healer.png
+ gear/events/fall/head_special_fall2023Mage.png
+ gear/events/fall/head_special_fall2023Rogue.png
+ gear/events/fall/head_special_fall2023Warrior.png
+ gear/events/fall/shield_special_fall2023Healer.png
+ gear/events/fall/shield_special_fall2023Rogue.png
+ gear/events/fall/shield_special_fall2023Warrior.png
+ gear/events/fall/shop/shop_armor_special_fall2023Healer.png
+ gear/events/fall/shop/shop_armor_special_fall2023Mage.png
+ gear/events/fall/shop/shop_armor_special_fall2023Rogue.png
+ gear/events/fall/shop/shop_armor_special_fall2023Warrior.png
+ gear/events/fall/shop/shop_head_special_fall2023Healer.png
+ gear/events/fall/shop/shop_head_special_fall2023Mage.png
+ gear/events/fall/shop/shop_head_special_fall2023Warrior.png
+ gear/events/fall/shop/shop_shield_special_fall2023Healer.png
+ gear/events/fall/shop/shop_shield_special_fall2023Rogue.png
+ gear/events/fall/shop/shop_shield_special_fall2023Warrior.png
+ gear/events/fall/shop/shop_weapon_special_fall2023Healer.png
+ gear/events/fall/shop/shop_weapon_special_fall2023Rogue.png
+ gear/events/fall/shop_head_special_fall2023Rogue.png
+ gear/events/fall/shop_weapon_special_fall2023Mage.png
+ gear/events/fall/shop_weapon_special_fall2023Warrior.png
+ gear/events/fall/slim_armor_special_fall2023Healer.png
+ gear/events/fall/slim_armor_special_fall2023Mage.png
+ gear/events/fall/slim_armor_special_fall2023Rogue.png
+ gear/events/fall/slim_armor_special_fall2023Warrior.png
+ gear/events/fall/weapon_special_fall2023Healer.png
+ gear/events/fall/weapon_special_fall2023Mage.png
+ gear/events/fall/weapon_special_fall2023Rogue.png
+ gear/events/fall/weapon_special_fall2023Warrior.png
+ gear/events/mystery_202310/broad_armor_mystery_202310.png
+ gear/events/mystery_202310/headAccessory_mystery_202310.png
+ gear/events/mystery_202310/head_mystery_202310.png
+ gear/events/mystery_202310/shop_armor_mystery_202310.png
+ gear/events/mystery_202310/shop_headAccessory_mystery_202310.png
+ gear/events/mystery_202310/shop_head_mystery_202310.png
+ gear/events/mystery_202310/shop_set_mystery_202310.png
+ gear/events/mystery_202310/slim_armor_mystery_202310.png
465 changes: 312 additions & 153 deletions package-lock.json

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"name": "habitica",
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
"version": "5.3.0",
"version": "5.6.0",
"main": "./website/server/index.js",
"dependencies": {
"@babel/core": "^7.22.5",
"@babel/core": "^7.22.10",
"@babel/preset-env": "^7.22.10",
"@babel/register": "^7.22.5",
"@google-cloud/trace-agent": "^7.1.2",
"@parse/node-apn": "^5.1.3",
"@parse/node-apn": "^5.2.3",
"@slack/webhook": "^6.1.0",
"accepts": "^1.3.8",
"amazon-payments": "^0.2.9",
Expand All @@ -17,7 +17,7 @@
"apple-auth": "^1.0.9",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"bootstrap": "^4.6.0",
"bootstrap": "^4.6.2",
"compression": "^1.7.4",
"cookie-session": "^2.0.0",
"coupon-code": "^0.4.5",
Expand All @@ -31,7 +31,7 @@
"express-basic-auth": "^1.2.1",
"express-validator": "^5.2.0",
"glob": "^8.1.0",
"got": "^11.8.3",
"got": "^11.8.6",
"gulp": "^4.0.0",
"gulp-babel": "^8.0.0",
"gulp-imagemin": "^7.1.0",
Expand All @@ -49,12 +49,12 @@
"method-override": "^3.0.0",
"moment": "^2.29.4",
"moment-recur": "^1.0.7",
"mongoose": "^5.13.7",
"mongoose": "^5.13.20",
"morgan": "^1.10.0",
"nconf": "^0.12.0",
"node-gcm": "^1.0.5",
"on-headers": "^1.0.2",
"passport": "^0.5.0",
"passport": "^0.5.3",
"passport-facebook": "^3.0.0",
"passport-google-oauth2": "^0.2.0",
"passport-google-oauth20": "2.0.0",
Expand All @@ -67,12 +67,12 @@
"remove-markdown": "^0.5.0",
"rimraf": "^3.0.2",
"short-uuid": "^4.2.2",
"stripe": "^12.9.0",
"stripe": "^12.18.0",
"superagent": "^8.1.2",
"universal-analytics": "^0.5.3",
"useragent": "^2.1.9",
"uuid": "^9.0.0",
"validator": "^13.9.0",
"validator": "^13.11.0",
"vinyl-buffer": "^1.0.1",
"winston": "^3.10.0",
"winston-loggly-bulk": "^3.2.1",
Expand Down Expand Up @@ -110,7 +110,7 @@
"apidoc": "gulp apidoc"
},
"devDependencies": {
"axios": "^1.3.6",
"axios": "^1.4.0",
"chai": "^4.3.7",
"chai-as-promised": "^7.1.1",
"chai-moment": "^0.1.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { v4 as generateUUID } from 'uuid';
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';

describe('POST /members/:memberId/clear-flags', () => {
let reporter;
let admin;
let moderator;

beforeEach(async () => {
reporter = await generateUser();
admin = await generateUser({ permissions: { userSupport: true } });
moderator = await generateUser({ permissions: { moderator: true } });
await reporter.post(`/members/${admin._id}/flag`);
});

context('error cases', () => {
it('returns error when memberId is not a UUID', async () => {
await expect(moderator.post('/members/gribbly/clear-flags'))
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('invalidReqParams'),
});
});

Check failure on line 28 in test/api/v3/integration/members/POST-members_memberId_clear-flags.js

View workflow job for this annotation

GitHub Actions / lint (14.x)

Trailing spaces not allowed
it('returns error when member with UUID is not found', async () => {
const randomId = generateUUID();

Check failure on line 31 in test/api/v3/integration/members/POST-members_memberId_clear-flags.js

View workflow job for this annotation

GitHub Actions / lint (14.x)

Trailing spaces not allowed
await expect(moderator.post(`/members/${randomId}/clear-flags`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('userWithIDNotFound', { userId: randomId }),
});
});

it('returns error when requesting user is not a moderator', async () => {
await expect(reporter.post(`/members/${admin._id}/clear-flags`))
.to.eventually.be.rejected.and.eql({

Check failure on line 42 in test/api/v3/integration/members/POST-members_memberId_clear-flags.js

View workflow job for this annotation

GitHub Actions / lint (14.x)

Expected indentation of 8 spaces but found 6
code: 400,

Check failure on line 43 in test/api/v3/integration/members/POST-members_memberId_clear-flags.js

View workflow job for this annotation

GitHub Actions / lint (14.x)

Expected indentation of 10 spaces but found 8
error: 'BadRequest',

Check failure on line 44 in test/api/v3/integration/members/POST-members_memberId_clear-flags.js

View workflow job for this annotation

GitHub Actions / lint (14.x)

Expected indentation of 10 spaces but found 8
message: 'Only a moderator may clear reports from a profile.',

Check failure on line 45 in test/api/v3/integration/members/POST-members_memberId_clear-flags.js

View workflow job for this annotation

GitHub Actions / lint (14.x)

Expected indentation of 10 spaces but found 8
});

Check failure on line 46 in test/api/v3/integration/members/POST-members_memberId_clear-flags.js

View workflow job for this annotation

GitHub Actions / lint (14.x)

Expected indentation of 8 spaces but found 6
});
});

Check failure on line 49 in test/api/v3/integration/members/POST-members_memberId_clear-flags.js

View workflow job for this annotation

GitHub Actions / lint (14.x)

Trailing spaces not allowed
context('valid request', () => {
it('removes a single flag from user', async () => {
await expect(moderator.post(`/members/${admin._id}/clear-flags`)).to.eventually.be.ok;
const updatedTarget = await admin.get(`/hall/heroes/${admin._id}`);
expect(updatedTarget.profile.flags).to.eql({});
});

it('removes multiple flags from user', async () => {
await moderator.post(`/members/${admin._id}/flag`);
await expect(moderator.post(`/members/${admin._id}/clear-flags`)).to.eventually.be.ok;
const updatedTarget = await admin.get(`/hall/heroes/${admin._id}`);
expect(updatedTarget.profile.flags).to.eql({});
});
});
});

Check failure on line 64 in test/api/v3/integration/members/POST-members_memberId_clear-flags.js

View workflow job for this annotation

GitHub Actions / lint (14.x)

Newline required at end of file but not found
151 changes: 151 additions & 0 deletions test/api/v3/integration/members/POST-members_memberId_flag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { v4 as generateUUID } from 'uuid';
import moment from 'moment';
import nconf from 'nconf';
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';
import { IncomingWebhook } from '@slack/webhook';

Check failure on line 8 in test/api/v3/integration/members/POST-members_memberId_flag.js

View workflow job for this annotation

GitHub Actions / lint (14.x)

`@slack/webhook` import should occur before import of `../../../../helpers/api-integration/v3`

describe('POST /members/:memberId/flag', () => {
let reporter;
let target;

beforeEach(async () => {
reporter = await generateUser();
target = await generateUser({
'profile.blurb': 'Naughty Text',
'profile.imageUrl': 'https://evil.com/',
});
});

context('error cases', () => {
it('returns error when memberId is not a UUID', async () => {
await expect(reporter.post('/members/gribbly/flag'))
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('invalidReqParams'),
});
});

it('returns error when member with UUID is not found', async () => {
const randomId = generateUUID();

await expect(reporter.post(`/members/${randomId}/flag`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('userWithIDNotFound', { userId: randomId }),
});
});

it('returns error when non-admin flags same profile twice', async () => {
await reporter.post(`/members/${target._id}/flag`);
await expect (reporter.post(`/members/${target._id}/flag`))
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: 'A profile can not be flagged more than once by the same user.',
});
});
});

context('valid request', () => {
let admin;
const comment = 'this profile is bad';
const source = 'Third Party Script';

beforeEach(async () => {
admin = await generateUser({ 'permissions.userSupport': true });
sandbox.stub(IncomingWebhook.prototype, 'send').returns(Promise.resolve());
});

afterEach(() => {
sandbox.restore();
});

it('adds flags object to target user', async () => {
await reporter.post(`/members/${target._id}/flag`);
const updatedTarget = await admin.get(`/hall/heroes/${target._id}`);
expect(updatedTarget.profile.flags[reporter._id]).to.have.all.keys([
'comment',
'source',
'timestamp',
]);
expect(moment(updatedTarget.profile.flags[reporter._id].timestamp).toDate()).to.be.a('date');
});

it('allows addition of a comment and source', async () => {
await reporter.post(`/members/${target._id}/flag`, {
comment,
source,
});
const updatedTarget = await admin.get(`/hall/heroes/${target._id}`);
expect(updatedTarget.profile.flags[reporter._id].comment).to.eql(comment);
expect(updatedTarget.profile.flags[reporter._id].source).to.eql(source);
});

it('allows moderator to flag twice', async () => {
const moderator = await generateUser({ 'permissions.moderator': true });
await moderator.post(`/members/${target._id}/flag`);
await expect(moderator.post(`/members/${target._id}/flag`)).to.eventually.be.ok;
});

it('allows multiple non-moderators to flag individually', async () => {
await admin.post(`/members/${target._id}/flag`);
await reporter.post(`/members/${target._id}/flag`);
const updatedTarget = await admin.get(`/hall/heroes/${target._id}`);
expect(updatedTarget.profile.flags[admin._id]).to.exist;
expect(updatedTarget.profile.flags[reporter._id]).to.exist;
});

it('sends a flag report to moderation Slack', async () => {
const BASE_URL = nconf.get('BASE_URL');
await reporter.post(`/members/${target._id}/flag`, {
comment,
source,
});

/* eslint-disable camelcase */
expect(IncomingWebhook.prototype.send).to.be.calledWith({
text: `@${reporter.auth.local.username} (${reporter._id}; language: ${reporter.preferences.language}) flagged @${target.auth.local.username}'s profile from ${source} and commented: ${comment}`,
attachments: [{
fallback: 'Flag Profile',
color: 'danger',
title: 'User Profile Report',
title_link: `${BASE_URL}/profile/${target._id}`,
text: `Display Name: ${target.profile.name}\n\nImage URL: ${target.profile.imageUrl}\n\nAbout: ${target.profile.blurb}`,
mrkdwn_in: [
'text',
],
}],
});
/* eslint-enable camelcase */
});

it('excludes empty fields when sending Slack message', async () => {
const BASE_URL = nconf.get('BASE_URL');
await reporter.post(`/members/${admin._id}/flag`, {
comment,
source,
});

/* eslint-disable camelcase */
expect(IncomingWebhook.prototype.send).to.be.calledWith({
text: `@${reporter.auth.local.username} (${reporter._id}; language: ${reporter.preferences.language}) flagged @${admin.auth.local.username}'s profile from ${source} and commented: ${comment}`,
attachments: [{
fallback: 'Flag Profile',
color: 'danger',
title: 'User Profile Report',
title_link: `${BASE_URL}/profile/${admin._id}`,
text: `Display Name: ${admin.profile.name}`,
mrkdwn_in: [
'text',
],
}],
});
/* eslint-enable camelcase */
});
});
});
4 changes: 2 additions & 2 deletions test/common/ops/buy/purchase.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ describe('shared.ops.purchase', () => {

it('purchases quest bundles', async () => {
const startingBalance = user.balance;
const clock = sandbox.useFakeTimers(moment('2019-05-20').valueOf());
// const clock = sandbox.useFakeTimers(moment('2019-05-20').valueOf());
const type = 'bundles';
const key = 'featheredFriends';
const price = 1.75;
Expand All @@ -216,7 +216,7 @@ describe('shared.ops.purchase', () => {
expect(user.balance).to.equal(startingBalance - price);

expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
clock.restore();
// clock.restore();
});
});

Expand Down
Loading
Loading