mirror of
https://github.com/discourse/discourse.git
synced 2026-09-05 04:40:41 -05:00
Adds a WarpDrive store (`service:warp-store`, LegacyMode + JSON:API cache) alongside the existing `service:store`, routing requests through Discourse's `ajax()` helper. `RestCompatModel` bridges legacy `RestModel` callsites (`get`/`set`/`setProperties`, `store.createRecord`, `save`, `destroyRecord`) onto it. Converts **badge, user-badge, topic-details, bookmark, tag, tag-group, tag-info, tag-notification, tag-settings and archetype**, with per-model schemas, request builders and payload normalizers. Attributes outside a schema (plugin `add_to_serializer` fields, ad-hoc `create` keys) are retained separately so nothing the server sends is dropped.
133 lines
3.7 KiB
Plaintext
133 lines
3.7 KiB
Plaintext
import Component from "@glimmer/component";
|
|
import { tracked } from "@glimmer/tracking";
|
|
import { fn, hash } from "@ember/helper";
|
|
import { action } from "@ember/object";
|
|
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
|
import { extractError } from "discourse/lib/ajax-error";
|
|
import getURL from "discourse/lib/get-url";
|
|
import {
|
|
grantableBadgeOptions,
|
|
grantableBadges,
|
|
isBadgeGrantable,
|
|
} from "discourse/lib/grant-badge-utils";
|
|
import { autoTrackedArray } from "discourse/lib/tracked-tools";
|
|
import Badge from "discourse/models/badge";
|
|
import UserBadge from "discourse/models/user-badge";
|
|
import ComboBox from "discourse/select-kit/components/combo-box";
|
|
import DButton from "discourse/ui-kit/d-button";
|
|
import DConditionalLoadingSpinner from "discourse/ui-kit/d-conditional-loading-spinner";
|
|
import DModal from "discourse/ui-kit/d-modal";
|
|
import { i18n } from "discourse-i18n";
|
|
|
|
export default class GrantBadgeModal extends Component {
|
|
@tracked loading = true;
|
|
@tracked saving = false;
|
|
@tracked selectedBadgeId = null;
|
|
@tracked flash = null;
|
|
@tracked flashType = null;
|
|
@tracked allBadges = [];
|
|
@tracked availableBadges = [];
|
|
@autoTrackedArray userBadges = [];
|
|
|
|
get noAvailableBadges() {
|
|
!this.availableBadges.length;
|
|
}
|
|
|
|
get badgeOptions() {
|
|
return grantableBadgeOptions(this.availableBadges);
|
|
}
|
|
|
|
get post() {
|
|
return this.args.model.selectedPost;
|
|
}
|
|
|
|
get buttonDisabled() {
|
|
return (
|
|
this.saving ||
|
|
!isBadgeGrantable(this.selectedBadgeId, this.availableBadges)
|
|
);
|
|
}
|
|
|
|
#updateAvailableBadges() {
|
|
this.availableBadges = grantableBadges(this.allBadges, this.userBadges);
|
|
}
|
|
|
|
@action
|
|
async loadBadges() {
|
|
this.loading = true;
|
|
try {
|
|
this.allBadges = await Badge.findAll();
|
|
this.userBadges = await UserBadge.findByUsername(this.post.username);
|
|
this.#updateAvailableBadges();
|
|
} catch (e) {
|
|
this.flash = extractError(e);
|
|
this.flashType = "error";
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
}
|
|
|
|
@action
|
|
async performGrantBadge() {
|
|
try {
|
|
this.saving = true;
|
|
const username = this.post.username;
|
|
const newBadge = await UserBadge.grant(
|
|
this.selectedBadgeId,
|
|
username,
|
|
getURL(this.post.url)
|
|
);
|
|
this.userBadges.push(newBadge);
|
|
this.#updateAvailableBadges();
|
|
this.selectedBadgeId = null;
|
|
this.flash = i18n("badges.successfully_granted", {
|
|
username,
|
|
badge: newBadge.get("badge.name"),
|
|
});
|
|
this.flashType = "success";
|
|
} catch (e) {
|
|
this.flash = extractError(e);
|
|
this.flashType = "error";
|
|
} finally {
|
|
this.saving = false;
|
|
}
|
|
}
|
|
|
|
<template>
|
|
<DModal
|
|
@bodyClass="grant-badge"
|
|
@closeModal={{@closeModal}}
|
|
@flash={{this.flash}}
|
|
@flashType={{this.flashType}}
|
|
@title={{i18n "admin.badges.grant_badge"}}
|
|
class="grant-badge-modal"
|
|
{{didInsert this.loadBadges}}
|
|
>
|
|
<:body>
|
|
<DConditionalLoadingSpinner @condition={{this.loading}}>
|
|
{{#if this.noAvailableBadges}}
|
|
<p>{{i18n "admin.badges.no_badges"}}</p>
|
|
{{else}}
|
|
<p>
|
|
<ComboBox
|
|
@value={{this.selectedBadgeId}}
|
|
@content={{this.badgeOptions}}
|
|
@onChange={{fn (mut this.selectedBadgeId)}}
|
|
@options={{hash filterable=true none="badges.none"}}
|
|
/>
|
|
</p>
|
|
{{/if}}
|
|
</DConditionalLoadingSpinner>
|
|
</:body>
|
|
<:footer>
|
|
<DButton
|
|
@disabled={{this.buttonDisabled}}
|
|
@action={{this.performGrantBadge}}
|
|
@label="admin.badges.grant"
|
|
class="btn-primary"
|
|
/>
|
|
</:footer>
|
|
</DModal>
|
|
</template>
|
|
}
|