Skip to content

Commit cd132ba

Browse files
jonas-chowchownces
andauthored
Upgrades to Achievements UI and removal of Achievement categories (#1886)
Co-authored-by: Chow En Rong <[email protected]>
1 parent 6b54628 commit cd132ba

24 files changed

+169
-181
lines changed

src/commons/achievement/AchievementCard.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ function AchievementCard(props: AchievementCardProps) {
2222

2323
const [focusUuid, setFocusUuid] = focusState;
2424

25-
const { ability, cardBackground, title } = inferencer.getAchievement(uuid);
25+
const { cardBackground, title } = inferencer.getAchievement(uuid);
2626
const displayDeadline = inferencer.getDisplayDeadline(uuid);
2727
const displayXp = inferencer.getAchievementXp(uuid);
2828
const progressFrac = inferencer.getProgressFrac(uuid);
@@ -38,7 +38,7 @@ function AchievementCard(props: AchievementCardProps) {
3838
onClick={() => setFocusUuid(uuid)}
3939
onClickCapture={toggleDropdown}
4040
style={{
41-
...handleGlow(uuid, focusUuid, ability),
41+
...handleGlow(uuid, focusUuid),
4242
opacity: shouldRender ? '100%' : '20%',
4343
background: `url(${cardBackground}) center/cover`
4444
}}
@@ -58,10 +58,7 @@ function AchievementCard(props: AchievementCardProps) {
5858
</div>
5959

6060
<div className="details">
61-
<div className="ability">
62-
<p>{ability}</p>
63-
</div>
64-
<AchievementDeadline ability={ability} deadline={displayDeadline} />
61+
<AchievementDeadline deadline={displayDeadline} />
6562
<AchievementXp isBonus={hasDropdown} xp={displayXp} />
6663
</div>
6764

src/commons/achievement/AchievementManualEditor.tsx

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Button, MenuItem, NumericInput } from '@blueprintjs/core';
2-
import { ItemRenderer, Select } from '@blueprintjs/select';
1+
import { Button, Checkbox, MenuItem, NumericInput } from '@blueprintjs/core';
2+
import { ItemPredicate, ItemRenderer, Select } from '@blueprintjs/select';
33
import { useContext, useEffect, useState } from 'react';
44
import { AchievementContext } from 'src/features/achievement/AchievementConstants';
55
import {
@@ -8,7 +8,11 @@ import {
88
GoalProgress
99
} from 'src/features/achievement/AchievementTypes';
1010

11+
import { showSuccessMessage, showWarningMessage } from '../utils/NotificationsHelper';
12+
1113
type AchievementManualEditorProps = {
14+
hiddenState: [boolean, any];
15+
userState: [AchievementUser | undefined, any];
1216
studio: string;
1317
users: AchievementUser[];
1418
getUsers: () => void;
@@ -19,9 +23,22 @@ const GoalSelect = Select.ofType<AchievementGoal>();
1923
const goalRenderer: ItemRenderer<AchievementGoal> = (goal, { handleClick }) => (
2024
<MenuItem key={goal.uuid} onClick={handleClick} text={goal.text} />
2125
);
26+
const goalPredicate: ItemPredicate<AchievementGoal> = (query, item) =>
27+
item.text.toLowerCase().includes(query.toLowerCase());
28+
29+
const UserSelect = Select.ofType<AchievementUser>();
30+
const userRenderer: ItemRenderer<AchievementUser> = (user, { handleClick }) => (
31+
<MenuItem key={user.courseRegId} onClick={handleClick} text={user.name} />
32+
);
33+
const userPredicate: ItemPredicate<AchievementUser> = (query, item) =>
34+
item.name.toLowerCase().includes(query.toLowerCase());
35+
36+
export function updateGoalProcessed() {
37+
showSuccessMessage('Goal updated');
38+
}
2239

2340
function AchievementManualEditor(props: AchievementManualEditorProps) {
24-
const { studio, getUsers, updateGoalProgress } = props;
41+
const { userState, hiddenState, studio, getUsers, updateGoalProgress } = props;
2542
const users =
2643
studio === 'Staff'
2744
? // The name can be null for users who have yet to log in. We push these to the back of the array.
@@ -39,35 +56,36 @@ function AchievementManualEditor(props: AchievementManualEditorProps) {
3956
.getAllGoals()
4057
.filter(goals => goals.meta.type === 'Manual');
4158

42-
const [goal, changeGoal] = useState(manualAchievements[0]);
43-
const [selectedUser, changeSelectedUser] = useState(users[0]);
44-
const [count, changeCount] = useState(0);
45-
46-
const UserSelect = Select.ofType<AchievementUser>();
47-
const userRenderer: ItemRenderer<AchievementUser> = (user, { handleClick }) => (
48-
<MenuItem key={user.courseRegId} onClick={handleClick} text={user.name} />
49-
);
59+
const [goal, changeGoal] = useState<AchievementGoal | undefined>(undefined);
60+
const [selectedUser, changeSelectedUser] = userState;
61+
const [count, changeCount] = useState<number>(0);
62+
const [viewHidden, changeViewHidden] = hiddenState;
5063

5164
const updateGoal = () => {
52-
if (goal) {
65+
if (goal && selectedUser) {
5366
const progress: GoalProgress = {
5467
uuid: goal.uuid,
55-
count: count,
68+
count: count < 0 ? 0 : Math.floor(count),
5669
targetCount: goal.targetCount,
5770
completed: count >= goal.targetCount
5871
};
5972
updateGoalProgress(selectedUser.courseRegId, progress);
73+
} else {
74+
!goal && showWarningMessage('Goal not selected');
75+
!selectedUser && showWarningMessage('User not selected');
6076
}
6177
};
6278

6379
return (
6480
<div className="achievement-manual-editor">
6581
<h3>User: </h3>
6682
<UserSelect
67-
filterable={false}
83+
filterable={true}
6884
items={users}
6985
itemRenderer={userRenderer}
86+
itemPredicate={userPredicate}
7087
onItemSelect={changeSelectedUser}
88+
noResults={<MenuItem disabled={true} text="No matching user" />}
7189
>
7290
<Button
7391
outlined={true}
@@ -78,10 +96,12 @@ function AchievementManualEditor(props: AchievementManualEditorProps) {
7896

7997
<h3>Goal: </h3>
8098
<GoalSelect
81-
filterable={false}
99+
filterable={true}
82100
items={manualAchievements}
83101
itemRenderer={goalRenderer}
102+
itemPredicate={goalPredicate}
84103
onItemSelect={changeGoal}
104+
noResults={<MenuItem disabled={true} text="No matching goal" />}
85105
>
86106
<Button outlined={true} text={goal ? goal.text : 'No Goal Selected'} color="White" />
87107
</GoalSelect>
@@ -91,12 +111,20 @@ function AchievementManualEditor(props: AchievementManualEditorProps) {
91111
value={count}
92112
min={0}
93113
allowNumericCharactersOnly={true}
114+
minorStepSize={null}
94115
placeholder="Count"
95116
onValueChange={changeCount}
96117
/>
97118

98119
<h3> </h3>
99120
<Button outlined={true} text="Update Goal" onClick={updateGoal} intent="primary" />
121+
122+
<h3> </h3>
123+
<Checkbox
124+
checked={viewHidden}
125+
label="View Hidden Achievements"
126+
onChange={() => changeViewHidden(!viewHidden)}
127+
/>
100128
</div>
101129
);
102130
}

src/commons/achievement/AchievementTask.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ function AchievementTask(props: AchievementTaskProps) {
1919

2020
const inferencer = useContext(AchievementContext);
2121
const prerequisiteUuids = [...inferencer.getImmediateChildren(uuid)];
22-
const taskColor = getAbilityColor(inferencer.getAchievement(uuid).ability);
22+
const taskColor = getAbilityColor();
2323

2424
const [isDropdownOpen, setIsDropdownOpen] = useState<boolean>(false);
2525
const toggleDropdown = () => setIsDropdownOpen(!isDropdownOpen);

src/commons/achievement/AchievementView.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,20 @@ function AchievementView(props: AchievementViewProps) {
3232
}
3333

3434
const achievement = inferencer.getAchievement(focusUuid);
35-
const { ability, deadline, title, view } = achievement;
35+
const { deadline, title, view } = achievement;
3636
const { coverImage, completionText, description } = view;
3737
const awardedXp = inferencer.getAchievementXp(focusUuid);
3838
const goals = inferencer.listGoals(focusUuid);
3939
const prereqGoals = inferencer.listPrerequisiteGoals(focusUuid);
4040
const status = inferencer.getStatus(focusUuid);
4141

4242
return (
43-
<div className="view" style={{ ...getAbilityGlow(ability), ...getAbilityBackground(ability) }}>
43+
<div className="view" style={{ ...getAbilityGlow(), ...getAbilityBackground() }}>
4444
<div
4545
className="cover"
4646
style={{
47-
background: `url(${coverImage}) center/cover`
47+
background: `rgba(0, 0, 0, 0.5) url(${coverImage}) center/cover`,
48+
backgroundBlendMode: `darken`
4849
}}
4950
>
5051
<h1>{title.toUpperCase()}</h1>

src/commons/achievement/card/AchievementDeadline.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,20 @@ import { Icon } from '@blueprintjs/core';
22
import { IconNames } from '@blueprintjs/icons';
33

44
import { DeadlineColors } from '../../../features/achievement/AchievementConstants';
5-
import { AchievementAbility } from '../../../features/achievement/AchievementTypes';
65
import { isExpired, prettifyDeadline, timeFromExpired } from '../utils/DateHelper';
76

87
type AchievementDeadlineProps = {
9-
ability: AchievementAbility;
108
deadline?: Date;
119
};
1210

1311
const twoDays = new Date(0, 0, 2).getTime() - new Date(0, 0, 0).getTime();
1412

1513
function AchievementDeadline(props: AchievementDeadlineProps) {
16-
const { ability, deadline } = props;
14+
const { deadline } = props;
1715

1816
// red deadline color for core achievements that are expiring in less than 2 days
1917
const deadlineColor =
20-
ability === AchievementAbility.CORE &&
21-
deadline !== undefined &&
22-
!isExpired(deadline) &&
23-
timeFromExpired(deadline) <= twoDays
18+
deadline !== undefined && !isExpired(deadline) && timeFromExpired(deadline) <= twoDays
2419
? DeadlineColors.RED
2520
: DeadlineColors.BLACK;
2621

src/commons/achievement/control/achievementEditor/AchievementSettings.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Button, Dialog, EditableText } from '@blueprintjs/core';
1+
import { Button, Checkbox, Dialog, EditableText } from '@blueprintjs/core';
22
import { IconNames } from '@blueprintjs/icons';
33
import { Tooltip2 } from '@blueprintjs/popover2';
44
import { useState } from 'react';
@@ -58,9 +58,13 @@ function AchievementSettings(props: AchievementSettingsProps) {
5858
/>
5959
<h3>Goals</h3>
6060
<EditableGoalUuids changeGoalUuids={changeGoalUuids} goalUuids={goalUuids} />
61-
<Tooltip2 content="The rewarded XP will be equal to the sum of 'count' of goals">
62-
<Button text={'Variable XP?'} active={isVariableXp} onClick={changeIsVariableXp} />
63-
</Tooltip2>
61+
62+
<h3>Variable XP</h3>
63+
<Checkbox
64+
label={"The rewarded XP will be equal to the sum of 'count' of goals"}
65+
checked={isVariableXp}
66+
onChange={changeIsVariableXp}
67+
/>
6468
</div>
6569
</Dialog>
6670
</>

src/commons/achievement/control/achievementEditor/AchievementTemplate.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
coverImageUrl
44
} from '../../../../features/achievement/AchievementConstants';
55
import {
6-
AchievementAbility,
76
AchievementItem,
87
AchievementView
98
} from '../../../../features/achievement/AchievementTypes';
@@ -17,7 +16,6 @@ export const viewTemplate: AchievementView = {
1716
export const achievementTemplate: AchievementItem = {
1817
uuid: '',
1918
title: 'Achievement Title Here',
20-
ability: AchievementAbility.CORE,
2119
xp: 0,
2220
isVariableXp: false,
2321
isTask: false,

src/commons/achievement/control/achievementEditor/EditableAbility.tsx

Lines changed: 0 additions & 34 deletions
This file was deleted.

src/commons/achievement/control/achievementEditor/EditableCard.tsx

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,12 @@ import { useContext, useMemo, useReducer, useState } from 'react';
66

77
import { AchievementContext } from '../../../../features/achievement/AchievementConstants';
88
import {
9-
AchievementAbility,
109
AchievementItem,
1110
AchievementView
1211
} from '../../../../features/achievement/AchievementTypes';
1312
import ItemDeleter from '../common/ItemDeleter';
1413
import ItemSaver from '../common/ItemSaver';
1514
import AchievementSettings from './AchievementSettings';
16-
import EditableAbility from './EditableAbility';
1715
import {
1816
EditableCardAction as Action,
1917
EditableCardActionType as ActionType,
@@ -51,14 +49,6 @@ const reducer = (state: State, action: Action) => {
5149
...state,
5250
isDirty: false
5351
};
54-
case ActionType.CHANGE_ABILITY:
55-
return {
56-
editableAchievement: {
57-
...state.editableAchievement,
58-
ability: action.payload
59-
},
60-
isDirty: true
61-
};
6252
case ActionType.CHANGE_CARD_BACKGROUND:
6353
return {
6454
editableAchievement: {
@@ -155,7 +145,7 @@ function EditableCard(props: EditableCardProps) {
155145
const [state, dispatch] = useReducer(reducer, achievementClone, init);
156146
const [isNew, setIsNew] = useState<boolean>(isNewAchievement);
157147
const { editableAchievement, isDirty } = state;
158-
const { ability, cardBackground, deadline, release, title, view, xp } = editableAchievement;
148+
const { cardBackground, deadline, release, title, view, xp } = editableAchievement;
159149

160150
const saveChanges = () => {
161151
dispatch({ type: ActionType.SAVE_CHANGES });
@@ -181,9 +171,6 @@ function EditableCard(props: EditableCardProps) {
181171
requestPublish();
182172
};
183173

184-
const changeAbility = (ability: AchievementAbility) =>
185-
dispatch({ type: ActionType.CHANGE_ABILITY, payload: ability });
186-
187174
const changeCardBackground = (cardBackground: string) =>
188175
dispatch({ type: ActionType.CHANGE_CARD_BACKGROUND, payload: cardBackground });
189176

@@ -245,7 +232,6 @@ function EditableCard(props: EditableCardProps) {
245232
</Tooltip2>
246233
</div>
247234
<div className="details">
248-
<EditableAbility ability={ability} changeAbility={changeAbility} />
249235
<EditableDate changeDate={changeRelease} date={release} type="Release" />
250236
<EditableDate changeDate={changeDeadline} date={deadline} type="Deadline" />
251237
</div>

src/commons/achievement/control/achievementEditor/EditableCardTypes.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
import {
2-
AchievementAbility,
3-
AchievementItem,
4-
AchievementView
5-
} from 'src/features/achievement/AchievementTypes';
1+
import { AchievementItem, AchievementView } from 'src/features/achievement/AchievementTypes';
62

73
export enum EditableCardActionType {
8-
CHANGE_ABILITY = 'CHANGE_ABILITY',
94
CHANGE_CARD_BACKGROUND = 'CHANGE_CARD_BACKGROUND',
105
CHANGE_DEADLINE = 'CHANGE_DEADLINE',
116
CHANGE_GOAL_UUIDS = 'CHANGE_GOAL_UUIDS',
@@ -22,10 +17,6 @@ export enum EditableCardActionType {
2217
}
2318

2419
export type EditableCardAction =
25-
| {
26-
type: EditableCardActionType.CHANGE_ABILITY;
27-
payload: AchievementAbility;
28-
}
2920
| {
3021
type: EditableCardActionType.CHANGE_CARD_BACKGROUND;
3122
payload: string;

0 commit comments

Comments
 (0)