added the ability to remove achievements and changed the process of deleting achievements and types
This commit is contained in:
parent
0d582ec803
commit
df25e1f6fc
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
@ -81,6 +82,12 @@ type SubscriptionAPIRequest struct {
|
|||||||
ChannelID string `json:"channel_id"`
|
ChannelID string `json:"channel_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RevokeOwnershipRequest struct {
|
||||||
|
BadgeID string `json:"badge_id"`
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
Time string `json:"time"`
|
||||||
|
}
|
||||||
|
|
||||||
type TypeWithBadgeCount struct {
|
type TypeWithBadgeCount struct {
|
||||||
*badgesmodel.BadgeTypeDefinition
|
*badgesmodel.BadgeTypeDefinition
|
||||||
BadgeCount int `json:"badge_count"`
|
BadgeCount int `json:"badge_count"`
|
||||||
@ -114,6 +121,7 @@ func (p *Plugin) initializeAPI() {
|
|||||||
apiRouter.HandleFunc("/deleteBadge/{badgeID}", p.extractUserMiddleWare(p.apiDeleteBadge, ResponseTypeJSON)).Methods(http.MethodDelete)
|
apiRouter.HandleFunc("/deleteBadge/{badgeID}", p.extractUserMiddleWare(p.apiDeleteBadge, ResponseTypeJSON)).Methods(http.MethodDelete)
|
||||||
apiRouter.HandleFunc("/deleteType/{typeID}", p.extractUserMiddleWare(p.apiDeleteType, ResponseTypeJSON)).Methods(http.MethodDelete)
|
apiRouter.HandleFunc("/deleteType/{typeID}", p.extractUserMiddleWare(p.apiDeleteType, ResponseTypeJSON)).Methods(http.MethodDelete)
|
||||||
apiRouter.HandleFunc("/grantBadge", p.extractUserMiddleWare(p.apiGrantBadge, ResponseTypeJSON)).Methods(http.MethodPost)
|
apiRouter.HandleFunc("/grantBadge", p.extractUserMiddleWare(p.apiGrantBadge, ResponseTypeJSON)).Methods(http.MethodPost)
|
||||||
|
apiRouter.HandleFunc("/revokeOwnership", p.extractUserMiddleWare(p.apiRevokeOwnership, ResponseTypeJSON)).Methods(http.MethodPost)
|
||||||
apiRouter.HandleFunc("/getChannelSubscriptions/{channelID}", p.extractUserMiddleWare(p.apiGetChannelSubscriptions, ResponseTypeJSON)).Methods(http.MethodGet)
|
apiRouter.HandleFunc("/getChannelSubscriptions/{channelID}", p.extractUserMiddleWare(p.apiGetChannelSubscriptions, ResponseTypeJSON)).Methods(http.MethodGet)
|
||||||
apiRouter.HandleFunc("/createSubscription", p.extractUserMiddleWare(p.apiCreateSubscription, ResponseTypeJSON)).Methods(http.MethodPost)
|
apiRouter.HandleFunc("/createSubscription", p.extractUserMiddleWare(p.apiCreateSubscription, ResponseTypeJSON)).Methods(http.MethodPost)
|
||||||
apiRouter.HandleFunc("/deleteSubscription", p.extractUserMiddleWare(p.apiDeleteSubscription, ResponseTypeJSON)).Methods(http.MethodPost)
|
apiRouter.HandleFunc("/deleteSubscription", p.extractUserMiddleWare(p.apiDeleteSubscription, ResponseTypeJSON)).Methods(http.MethodPost)
|
||||||
@ -1123,6 +1131,10 @@ func (p *Plugin) dialogGrant(w http.ResponseWriter, r *http.Request, userID stri
|
|||||||
reason, _ := req.Submission[DialogFieldGrantReason].(string)
|
reason, _ := req.Submission[DialogFieldGrantReason].(string)
|
||||||
|
|
||||||
shouldNotify, err := p.store.GrantBadge(badgesmodel.BadgeID(badgeIDStr), grantToID, userID, reason)
|
shouldNotify, err := p.store.GrantBadge(badgesmodel.BadgeID(badgeIDStr), grantToID, userID, reason)
|
||||||
|
if err == errAlreadyOwned {
|
||||||
|
dialogError(w, T("badges.error.already_owned", "Это достижение уже выдано этому пользователю"), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.writeAPIError(w, &APIErrorResponse{
|
p.writeAPIError(w, &APIErrorResponse{
|
||||||
ID: "cannot grant badge",
|
ID: "cannot grant badge",
|
||||||
@ -1302,6 +1314,12 @@ func (p *Plugin) grantBadge(w http.ResponseWriter, r *http.Request, pluginID str
|
|||||||
}
|
}
|
||||||
|
|
||||||
shouldNotify, err := p.store.GrantBadge(req.BadgeID, req.UserID, req.BotID, req.Reason)
|
shouldNotify, err := p.store.GrantBadge(req.BadgeID, req.UserID, req.BotID, req.Reason)
|
||||||
|
if err == errAlreadyOwned {
|
||||||
|
p.writeAPIError(w, &APIErrorResponse{
|
||||||
|
ID: "already_owned", Message: "This badge is already owned by this user", StatusCode: http.StatusConflict,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.writeAPIError(w, &APIErrorResponse{
|
p.writeAPIError(w, &APIErrorResponse{
|
||||||
ID: "cannot grant badge",
|
ID: "cannot grant badge",
|
||||||
@ -1670,6 +1688,12 @@ func (p *Plugin) apiGrantBadge(w http.ResponseWriter, r *http.Request, userID st
|
|||||||
}
|
}
|
||||||
|
|
||||||
shouldNotify, err := p.store.GrantBadge(badgesmodel.BadgeID(req.BadgeID), req.UserID, userID, req.Reason)
|
shouldNotify, err := p.store.GrantBadge(badgesmodel.BadgeID(req.BadgeID), req.UserID, userID, req.Reason)
|
||||||
|
if errors.Is(err, errAlreadyOwned) {
|
||||||
|
p.writeAPIError(w, &APIErrorResponse{
|
||||||
|
ID: "already_owned", Message: "This badge is already owned by this user", StatusCode: http.StatusConflict,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.writeAPIError(w, &APIErrorResponse{
|
p.writeAPIError(w, &APIErrorResponse{
|
||||||
ID: "cannot_grant_badge", Message: err.Error(), StatusCode: http.StatusInternalServerError,
|
ID: "cannot_grant_badge", Message: err.Error(), StatusCode: http.StatusInternalServerError,
|
||||||
@ -1811,3 +1835,50 @@ func (p *Plugin) apiGetChannelSubscriptions(w http.ResponseWriter, r *http.Reque
|
|||||||
b, _ := json.Marshal(types)
|
b, _ := json.Marshal(types)
|
||||||
_, _ = w.Write(b)
|
_, _ = w.Write(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) apiRevokeOwnership(w http.ResponseWriter, r *http.Request, userID string) {
|
||||||
|
var req RevokeOwnershipRequest
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
|
p.writeAPIError(w, &APIErrorResponse{
|
||||||
|
ID: "invalid_request", Message: "Invalid request body", StatusCode: http.StatusBadRequest,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.BadgeID = strings.TrimSpace(req.BadgeID)
|
||||||
|
req.UserID = strings.TrimSpace(req.UserID)
|
||||||
|
req.Time = strings.TrimSpace(req.Time)
|
||||||
|
if req.BadgeID == "" || req.UserID == "" || req.Time == "" {
|
||||||
|
p.writeAPIError(w, &APIErrorResponse{
|
||||||
|
ID: "invalid_request", Message: "badge_id, user_id and time are required", StatusCode: http.StatusBadRequest,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ownership, err := p.store.FindOwnership(badgesmodel.BadgeID(req.BadgeID), req.UserID, req.Time)
|
||||||
|
if err != nil {
|
||||||
|
p.writeAPIError(w, &APIErrorResponse{
|
||||||
|
ID: "ownership_not_found", Message: "Ownership not found", StatusCode: http.StatusNotFound,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isAdmin := p.badgeAdminUserIDs[userID]
|
||||||
|
if ownership.GrantedBy != userID && !isAdmin {
|
||||||
|
p.writeAPIError(w, &APIErrorResponse{
|
||||||
|
ID: "no_permission_revoke", Message: "No permission to revoke this ownership", StatusCode: http.StatusForbidden,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.store.RevokeOwnership(badgesmodel.BadgeID(req.BadgeID), req.UserID, req.Time); err != nil {
|
||||||
|
p.writeAPIError(w, &APIErrorResponse{
|
||||||
|
ID: "cannot_revoke", Message: err.Error(), StatusCode: http.StatusInternalServerError,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := map[string]string{"status": "ok"}
|
||||||
|
b, _ := json.Marshal(resp)
|
||||||
|
_, _ = w.Write(b)
|
||||||
|
}
|
||||||
|
|||||||
@ -592,6 +592,9 @@ func (p *Plugin) runGrant(args []string, extra *model.CommandArgs) (bool, *model
|
|||||||
}
|
}
|
||||||
|
|
||||||
shouldNotify, err := p.store.GrantBadge(badgesmodel.BadgeID(badgeStr), user.Id, extra.UserId, "")
|
shouldNotify, err := p.store.GrantBadge(badgesmodel.BadgeID(badgeStr), user.Id, extra.UserId, "")
|
||||||
|
if err == errAlreadyOwned {
|
||||||
|
return commandError(T("badges.error.already_owned", "Это достижение уже выдано этому пользователю"))
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commandError(err.Error())
|
return commandError(err.Error())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
[
|
[
|
||||||
{"id": "badges.dialog.create_badge.title", "translation": "Create badge"},
|
{"id": "badges.dialog.create_badge.title", "translation": "Create achievement"},
|
||||||
{"id": "badges.dialog.create_badge.submit", "translation": "Create"},
|
{"id": "badges.dialog.create_badge.submit", "translation": "Create"},
|
||||||
{"id": "badges.dialog.edit_badge.title", "translation": "Edit badge"},
|
{"id": "badges.dialog.edit_badge.title", "translation": "Edit achievement"},
|
||||||
{"id": "badges.dialog.edit_badge.submit", "translation": "Save"},
|
{"id": "badges.dialog.edit_badge.submit", "translation": "Save"},
|
||||||
{"id": "badges.dialog.create_type.title", "translation": "Create type"},
|
{"id": "badges.dialog.create_type.title", "translation": "Create type"},
|
||||||
{"id": "badges.dialog.create_type.submit", "translation": "Create"},
|
{"id": "badges.dialog.create_type.submit", "translation": "Create"},
|
||||||
{"id": "badges.dialog.edit_type.title", "translation": "Edit type"},
|
{"id": "badges.dialog.edit_type.title", "translation": "Edit type"},
|
||||||
{"id": "badges.dialog.edit_type.submit", "translation": "Save"},
|
{"id": "badges.dialog.edit_type.submit", "translation": "Save"},
|
||||||
{"id": "badges.dialog.grant.title", "translation": "Grant badge"},
|
{"id": "badges.dialog.grant.title", "translation": "Grant achievement"},
|
||||||
{"id": "badges.dialog.grant.submit", "translation": "Grant"},
|
{"id": "badges.dialog.grant.submit", "translation": "Grant"},
|
||||||
{"id": "badges.dialog.grant.intro", "translation": "Grant badge to @%s"},
|
{"id": "badges.dialog.grant.intro", "translation": "Grant achievement to @%s"},
|
||||||
{"id": "badges.dialog.create_subscription.title", "translation": "Create subscription"},
|
{"id": "badges.dialog.create_subscription.title", "translation": "Create subscription"},
|
||||||
{"id": "badges.dialog.create_subscription.submit", "translation": "Add"},
|
{"id": "badges.dialog.create_subscription.submit", "translation": "Add"},
|
||||||
{"id": "badges.dialog.create_subscription.intro", "translation": "Select the badge type you want to subscribe to this channel."},
|
{"id": "badges.dialog.create_subscription.intro", "translation": "Select the achievement type you want to subscribe to this channel."},
|
||||||
{"id": "badges.dialog.delete_subscription.title", "translation": "Delete subscription"},
|
{"id": "badges.dialog.delete_subscription.title", "translation": "Delete subscription"},
|
||||||
{"id": "badges.dialog.delete_subscription.submit", "translation": "Remove"},
|
{"id": "badges.dialog.delete_subscription.submit", "translation": "Remove"},
|
||||||
{"id": "badges.dialog.delete_subscription.intro", "translation": "Select the badge type you want to unsubscribe from this channel."},
|
{"id": "badges.dialog.delete_subscription.intro", "translation": "Select the achievement type you want to unsubscribe from this channel."},
|
||||||
|
|
||||||
{"id": "badges.field.name", "translation": "Name"},
|
{"id": "badges.field.name", "translation": "Name"},
|
||||||
{"id": "badges.field.description", "translation": "Description"},
|
{"id": "badges.field.description", "translation": "Description"},
|
||||||
@ -23,45 +23,46 @@
|
|||||||
{"id": "badges.field.image.help", "translation": "Enter an emoticon name"},
|
{"id": "badges.field.image.help", "translation": "Enter an emoticon name"},
|
||||||
{"id": "badges.field.type", "translation": "Type"},
|
{"id": "badges.field.type", "translation": "Type"},
|
||||||
{"id": "badges.field.multiple", "translation": "Multiple"},
|
{"id": "badges.field.multiple", "translation": "Multiple"},
|
||||||
{"id": "badges.field.multiple.help", "translation": "Whether the badge can be granted multiple times"},
|
{"id": "badges.field.multiple.help", "translation": "Whether the achievement can be granted multiple times"},
|
||||||
{"id": "badges.field.delete_badge", "translation": "Delete badge"},
|
{"id": "badges.field.delete_badge", "translation": "Delete achievement"},
|
||||||
{"id": "badges.field.delete_badge.help", "translation": "WARNING: checking this will remove this badge permanently."},
|
{"id": "badges.field.delete_badge.help", "translation": "WARNING: checking this will remove this achievement permanently."},
|
||||||
{"id": "badges.field.everyone_can_create", "translation": "Everyone can create badge"},
|
{"id": "badges.field.everyone_can_create", "translation": "Everyone can create achievement"},
|
||||||
{"id": "badges.field.everyone_can_create.help", "translation": "Whether any user can create a badge of this type"},
|
{"id": "badges.field.everyone_can_create.help", "translation": "Whether any user can create an achievement of this type"},
|
||||||
{"id": "badges.field.allowlist_create", "translation": "Can create allowlist"},
|
{"id": "badges.field.allowlist_create", "translation": "Can create allowlist"},
|
||||||
{"id": "badges.field.allowlist_create.help", "translation": "Fill the usernames separated by comma (,) of the people that can create badges of this type."},
|
{"id": "badges.field.allowlist_create.help", "translation": "Fill the usernames separated by comma (,) of the people that can create achievements of this type."},
|
||||||
{"id": "badges.field.everyone_can_grant", "translation": "Everyone can grant badge"},
|
{"id": "badges.field.everyone_can_grant", "translation": "Everyone can grant achievement"},
|
||||||
{"id": "badges.field.everyone_can_grant.help", "translation": "Whether any user can grant a badge of this type"},
|
{"id": "badges.field.everyone_can_grant.help", "translation": "Whether any user can grant an achievement of this type"},
|
||||||
{"id": "badges.field.allowlist_grant", "translation": "Can grant allowlist"},
|
{"id": "badges.field.allowlist_grant", "translation": "Can grant allowlist"},
|
||||||
{"id": "badges.field.allowlist_grant.help", "translation": "Fill the usernames separated by comma (,) of the people that can grant badges of this type."},
|
{"id": "badges.field.allowlist_grant.help", "translation": "Fill the usernames separated by comma (,) of the people that can grant achievements of this type."},
|
||||||
{"id": "badges.field.delete_type", "translation": "Remove type"},
|
{"id": "badges.field.delete_type", "translation": "Remove type"},
|
||||||
{"id": "badges.field.delete_type.help", "translation": "WARNING: checking this will remove this type and all associated badges permanently."},
|
{"id": "badges.field.delete_type.help", "translation": "WARNING: checking this will remove this type and all associated achievements permanently."},
|
||||||
{"id": "badges.field.user", "translation": "User"},
|
{"id": "badges.field.user", "translation": "User"},
|
||||||
{"id": "badges.field.badge", "translation": "Badge"},
|
{"id": "badges.field.badge", "translation": "Achievement"},
|
||||||
{"id": "badges.field.reason", "translation": "Reason"},
|
{"id": "badges.field.reason", "translation": "Reason"},
|
||||||
{"id": "badges.field.reason.help", "translation": "Reason why you are granting this badge. This will be seen by the user, and wherever this grant notification is shown (e.g. subscriptions)."},
|
{"id": "badges.field.reason.help", "translation": "Reason why you are granting this achievement. This will be seen by the user, and wherever this grant notification is shown (e.g. subscriptions)."},
|
||||||
{"id": "badges.field.notify_here", "translation": "Notify on this channel"},
|
{"id": "badges.field.notify_here", "translation": "Notify on this channel"},
|
||||||
{"id": "badges.field.notify_here.help", "translation": "If you mark this, the bot will send a message to this channel notifying that you granted this badge to this person."},
|
{"id": "badges.field.notify_here.help", "translation": "If you mark this, the bot will send a message to this channel notifying that you granted this achievement to this person."},
|
||||||
|
|
||||||
{"id": "badges.error.unknown", "translation": "An unknown error occurred. Please talk to your system administrator for help."},
|
{"id": "badges.error.unknown", "translation": "An unknown error occurred. Please talk to your system administrator for help."},
|
||||||
{"id": "badges.error.cannot_get_user", "translation": "Cannot get user."},
|
{"id": "badges.error.cannot_get_user", "translation": "Cannot get user."},
|
||||||
{"id": "badges.error.only_sysadmin_clean", "translation": "Only a system admin can clean the badges database."},
|
{"id": "badges.error.only_sysadmin_clean", "translation": "Only a system admin can clean the achievements database."},
|
||||||
{"id": "badges.error.specify_create", "translation": "Specify what you want to create."},
|
{"id": "badges.error.specify_create", "translation": "Specify what you want to create."},
|
||||||
{"id": "badges.error.create_badge_or_type", "translation": "You can create either badge or type"},
|
{"id": "badges.error.create_badge_or_type", "translation": "You can create either achievement or type"},
|
||||||
{"id": "badges.error.no_types_available", "translation": "You cannot create badges from any type."},
|
{"id": "badges.error.no_types_available", "translation": "You cannot create achievements from any type."},
|
||||||
{"id": "badges.error.must_set_badge_id", "translation": "You must set the badge ID"},
|
{"id": "badges.error.must_set_badge_id", "translation": "You must set the achievement ID"},
|
||||||
{"id": "badges.error.cannot_edit_badge", "translation": "You cannot edit this badge"},
|
{"id": "badges.error.cannot_edit_badge", "translation": "You cannot edit this achievement"},
|
||||||
{"id": "badges.error.specify_edit", "translation": "Specify what you want to edit."},
|
{"id": "badges.error.specify_edit", "translation": "Specify what you want to edit."},
|
||||||
{"id": "badges.error.edit_badge_or_type", "translation": "You can edit either badge or type"},
|
{"id": "badges.error.edit_badge_or_type", "translation": "You can edit either achievement or type"},
|
||||||
{"id": "badges.error.no_permissions_edit_type", "translation": "You have no permissions to edit a badge type."},
|
{"id": "badges.error.no_permissions_edit_type", "translation": "You have no permissions to edit an achievement type."},
|
||||||
{"id": "badges.error.must_provide_type_id", "translation": "You must provide a type id"},
|
{"id": "badges.error.must_provide_type_id", "translation": "You must provide a type id"},
|
||||||
{"id": "badges.error.cannot_edit_type", "translation": "You cannot edit this type"},
|
{"id": "badges.error.cannot_edit_type", "translation": "You cannot edit this type"},
|
||||||
{"id": "badges.error.no_permissions_grant", "translation": "You have no permissions to grant this badge"},
|
{"id": "badges.error.no_permissions_grant", "translation": "You have no permissions to grant this achievement"},
|
||||||
{"id": "badges.error.cannot_grant_badge", "translation": "You cannot grant that badge"},
|
{"id": "badges.error.cannot_grant_badge", "translation": "You cannot grant that achievement"},
|
||||||
{"id": "badges.error.specify_subscription", "translation": "Specify what you want to do."},
|
{"id": "badges.error.specify_subscription", "translation": "Specify what you want to do."},
|
||||||
{"id": "badges.error.create_or_delete_subscription", "translation": "You can either create or delete subscriptions"},
|
{"id": "badges.error.create_or_delete_subscription", "translation": "You can either create or delete subscriptions"},
|
||||||
{"id": "badges.error.cannot_create_subscription", "translation": "You cannot create subscriptions"},
|
{"id": "badges.error.cannot_create_subscription", "translation": "You cannot create subscriptions"},
|
||||||
{"id": "badges.error.no_permissions_create_type", "translation": "You have no permissions to create a badge type."},
|
{"id": "badges.error.no_permissions_create_type", "translation": "You have no permissions to create an achievement type."},
|
||||||
|
{"id": "badges.error.already_owned", "translation": "This achievement is already owned by this user"},
|
||||||
|
|
||||||
{"id": "badges.success.clean", "translation": "Clean"},
|
{"id": "badges.success.clean", "translation": "Clean"},
|
||||||
{"id": "badges.success.granted", "translation": "Granted"},
|
{"id": "badges.success.granted", "translation": "Granted"},
|
||||||
@ -72,8 +73,8 @@
|
|||||||
{"id": "badges.api.empty_emoji", "translation": "Empty emoji"},
|
{"id": "badges.api.empty_emoji", "translation": "Empty emoji"},
|
||||||
{"id": "badges.api.invalid_field", "translation": "Invalid field"},
|
{"id": "badges.api.invalid_field", "translation": "Invalid field"},
|
||||||
{"id": "badges.api.type_not_exist", "translation": "This type does not exist"},
|
{"id": "badges.api.type_not_exist", "translation": "This type does not exist"},
|
||||||
{"id": "badges.api.no_permissions_create_badge", "translation": "You have no permissions to create this badge"},
|
{"id": "badges.api.no_permissions_create_badge", "translation": "You have no permissions to create this achievement"},
|
||||||
{"id": "badges.api.badge_created", "translation": "Badge `%s` created."},
|
{"id": "badges.api.badge_created", "translation": "Achievement `%s` created."},
|
||||||
{"id": "badges.api.no_permissions_create_type", "translation": "You have no permissions to create a type"},
|
{"id": "badges.api.no_permissions_create_type", "translation": "You have no permissions to create a type"},
|
||||||
{"id": "badges.api.cannot_find_user", "translation": "Cannot find user"},
|
{"id": "badges.api.cannot_find_user", "translation": "Cannot find user"},
|
||||||
{"id": "badges.api.error_getting_user", "translation": "Error getting user %s: %v"},
|
{"id": "badges.api.error_getting_user", "translation": "Error getting user %s: %v"},
|
||||||
@ -83,15 +84,15 @@
|
|||||||
{"id": "badges.api.could_not_get_type", "translation": "Could not get the type"},
|
{"id": "badges.api.could_not_get_type", "translation": "Could not get the type"},
|
||||||
{"id": "badges.api.no_permissions_edit_type", "translation": "You have no permissions to edit this type"},
|
{"id": "badges.api.no_permissions_edit_type", "translation": "You have no permissions to edit this type"},
|
||||||
{"id": "badges.api.type_updated", "translation": "Type `%s` updated."},
|
{"id": "badges.api.type_updated", "translation": "Type `%s` updated."},
|
||||||
{"id": "badges.api.cannot_get_badge", "translation": "Cannot get badge"},
|
{"id": "badges.api.cannot_get_badge", "translation": "Cannot get achievement"},
|
||||||
{"id": "badges.api.cannot_edit_badge", "translation": "You cannot edit this badge"},
|
{"id": "badges.api.cannot_edit_badge", "translation": "You cannot edit this achievement"},
|
||||||
{"id": "badges.api.could_not_get_badge", "translation": "Could not get the badge"},
|
{"id": "badges.api.could_not_get_badge", "translation": "Could not get the achievement"},
|
||||||
{"id": "badges.api.no_permissions_edit_badge", "translation": "You have no permissions to edit this badge"},
|
{"id": "badges.api.no_permissions_edit_badge", "translation": "You have no permissions to edit this achievement"},
|
||||||
{"id": "badges.api.badge_updated", "translation": "Badge `%s` updated."},
|
{"id": "badges.api.badge_updated", "translation": "Achievement `%s` updated."},
|
||||||
{"id": "badges.api.badge_not_found", "translation": "Badge not found"},
|
{"id": "badges.api.badge_not_found", "translation": "Achievement not found"},
|
||||||
{"id": "badges.api.no_permissions_grant", "translation": "You have no permissions to grant this badge"},
|
{"id": "badges.api.no_permissions_grant", "translation": "You have no permissions to grant this achievement"},
|
||||||
{"id": "badges.api.user_not_found", "translation": "User not found"},
|
{"id": "badges.api.user_not_found", "translation": "User not found"},
|
||||||
{"id": "badges.api.badge_granted", "translation": "Badge `%s` granted to @%s."},
|
{"id": "badges.api.badge_granted", "translation": "Achievement `%s` granted to @%s."},
|
||||||
{"id": "badges.api.cannot_create_subscription", "translation": "You cannot create a subscription"},
|
{"id": "badges.api.cannot_create_subscription", "translation": "You cannot create a subscription"},
|
||||||
{"id": "badges.api.subscription_added", "translation": "Subscription added"},
|
{"id": "badges.api.subscription_added", "translation": "Subscription added"},
|
||||||
{"id": "badges.api.cannot_delete_subscription", "translation": "You cannot delete a subscription"},
|
{"id": "badges.api.cannot_delete_subscription", "translation": "You cannot delete a subscription"},
|
||||||
@ -99,9 +100,9 @@
|
|||||||
{"id": "badges.api.cannot_delete_default_type", "translation": "Cannot delete the default type"},
|
{"id": "badges.api.cannot_delete_default_type", "translation": "Cannot delete the default type"},
|
||||||
{"id": "badges.api.not_authorized", "translation": "Not authorized"},
|
{"id": "badges.api.not_authorized", "translation": "Not authorized"},
|
||||||
|
|
||||||
{"id": "badges.notify.dm_text", "translation": "@%s granted you the %s`%s` badge."},
|
{"id": "badges.notify.dm_text", "translation": "@%s granted you the %s`%s` achievement."},
|
||||||
{"id": "badges.notify.dm_reason", "translation": "\nWhy? "},
|
{"id": "badges.notify.dm_reason", "translation": "\nWhy? "},
|
||||||
{"id": "badges.notify.title", "translation": "%sbadge granted!"},
|
{"id": "badges.notify.title", "translation": "%sachievement granted!"},
|
||||||
{"id": "badges.notify.channel_text", "translation": "@%s granted @%s the %s`%s` badge."},
|
{"id": "badges.notify.channel_text", "translation": "@%s granted @%s the %s`%s` achievement."},
|
||||||
{"id": "badges.notify.no_permission_channel", "translation": "You don't have permissions to notify the grant on this channel."}
|
{"id": "badges.notify.no_permission_channel", "translation": "You don't have permissions to notify the grant on this channel."}
|
||||||
]
|
]
|
||||||
@ -18,6 +18,7 @@ type Bundle i18n.Bundle
|
|||||||
func Init() *Bundle {
|
func Init() *Bundle {
|
||||||
bundle := i18n.NewBundle(language.Russian)
|
bundle := i18n.NewBundle(language.Russian)
|
||||||
_, _ = bundle.LoadMessageFileFS(i18nFiles, "en.json")
|
_, _ = bundle.LoadMessageFileFS(i18nFiles, "en.json")
|
||||||
|
_, _ = bundle.LoadMessageFileFS(i18nFiles, "ru.json")
|
||||||
|
|
||||||
return (*Bundle)(bundle)
|
return (*Bundle)(bundle)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
[
|
[
|
||||||
{"id": "badges.dialog.create_badge.title", "translation": "Создать значок"},
|
{"id": "badges.dialog.create_badge.title", "translation": "Создать достижение"},
|
||||||
{"id": "badges.dialog.create_badge.submit", "translation": "Создать"},
|
{"id": "badges.dialog.create_badge.submit", "translation": "Создать"},
|
||||||
{"id": "badges.dialog.edit_badge.title", "translation": "Редактировать значок"},
|
{"id": "badges.dialog.edit_badge.title", "translation": "Редактировать достижение"},
|
||||||
{"id": "badges.dialog.edit_badge.submit", "translation": "Сохранить"},
|
{"id": "badges.dialog.edit_badge.submit", "translation": "Сохранить"},
|
||||||
{"id": "badges.dialog.create_type.title", "translation": "Создать тип"},
|
{"id": "badges.dialog.create_type.title", "translation": "Создать тип"},
|
||||||
{"id": "badges.dialog.create_type.submit", "translation": "Создать"},
|
{"id": "badges.dialog.create_type.submit", "translation": "Создать"},
|
||||||
{"id": "badges.dialog.edit_type.title", "translation": "Редактировать тип"},
|
{"id": "badges.dialog.edit_type.title", "translation": "Редактировать тип"},
|
||||||
{"id": "badges.dialog.edit_type.submit", "translation": "Сохранить"},
|
{"id": "badges.dialog.edit_type.submit", "translation": "Сохранить"},
|
||||||
{"id": "badges.dialog.grant.title", "translation": "Выдать значок"},
|
{"id": "badges.dialog.grant.title", "translation": "Выдать достижение"},
|
||||||
{"id": "badges.dialog.grant.submit", "translation": "Выдать"},
|
{"id": "badges.dialog.grant.submit", "translation": "Выдать"},
|
||||||
{"id": "badges.dialog.grant.intro", "translation": "Выдать значок пользователю @%s"},
|
{"id": "badges.dialog.grant.intro", "translation": "Выдать достижение пользователю @%s"},
|
||||||
{"id": "badges.dialog.create_subscription.title", "translation": "Создать подписку"},
|
{"id": "badges.dialog.create_subscription.title", "translation": "Создать подписку"},
|
||||||
{"id": "badges.dialog.create_subscription.submit", "translation": "Добавить"},
|
{"id": "badges.dialog.create_subscription.submit", "translation": "Добавить"},
|
||||||
{"id": "badges.dialog.create_subscription.intro", "translation": "Выберите тип значка, на который хотите подписать этот канал."},
|
{"id": "badges.dialog.create_subscription.intro", "translation": "Выберите тип достижения, на который хотите подписать этот канал."},
|
||||||
{"id": "badges.dialog.delete_subscription.title", "translation": "Удалить подписку"},
|
{"id": "badges.dialog.delete_subscription.title", "translation": "Удалить подписку"},
|
||||||
{"id": "badges.dialog.delete_subscription.submit", "translation": "Удалить"},
|
{"id": "badges.dialog.delete_subscription.submit", "translation": "Удалить"},
|
||||||
{"id": "badges.dialog.delete_subscription.intro", "translation": "Выберите тип значка, подписку на который хотите удалить из этого канала."},
|
{"id": "badges.dialog.delete_subscription.intro", "translation": "Выберите тип достижения, подписку на который хотите удалить из этого канала."},
|
||||||
|
|
||||||
{"id": "badges.field.name", "translation": "Название"},
|
{"id": "badges.field.name", "translation": "Название"},
|
||||||
{"id": "badges.field.description", "translation": "Описание"},
|
{"id": "badges.field.description", "translation": "Описание"},
|
||||||
@ -23,45 +23,46 @@
|
|||||||
{"id": "badges.field.image.help", "translation": "Введите название эмодзи"},
|
{"id": "badges.field.image.help", "translation": "Введите название эмодзи"},
|
||||||
{"id": "badges.field.type", "translation": "Тип"},
|
{"id": "badges.field.type", "translation": "Тип"},
|
||||||
{"id": "badges.field.multiple", "translation": "Многократный"},
|
{"id": "badges.field.multiple", "translation": "Многократный"},
|
||||||
{"id": "badges.field.multiple.help", "translation": "Можно ли выдавать этот значок несколько раз"},
|
{"id": "badges.field.multiple.help", "translation": "Можно ли выдавать это достижение несколько раз"},
|
||||||
{"id": "badges.field.delete_badge", "translation": "Удалить значок"},
|
{"id": "badges.field.delete_badge", "translation": "Удалить достижение"},
|
||||||
{"id": "badges.field.delete_badge.help", "translation": "ВНИМАНИЕ: если отметить, значок будет удалён безвозвратно."},
|
{"id": "badges.field.delete_badge.help", "translation": "ВНИМАНИЕ: если отметить, достижение будет удалён безвозвратно."},
|
||||||
{"id": "badges.field.everyone_can_create", "translation": "Все могут создавать значки"},
|
{"id": "badges.field.everyone_can_create", "translation": "Все могут создавать достижения"},
|
||||||
{"id": "badges.field.everyone_can_create.help", "translation": "Любой пользователь может создать значок этого типа"},
|
{"id": "badges.field.everyone_can_create.help", "translation": "Любой пользователь может создать достижение этого типа"},
|
||||||
{"id": "badges.field.allowlist_create", "translation": "Список допущенных к созданию"},
|
{"id": "badges.field.allowlist_create", "translation": "Список допущенных к созданию"},
|
||||||
{"id": "badges.field.allowlist_create.help", "translation": "Укажите имена пользователей через запятую (,), которые могут создавать значки этого типа."},
|
{"id": "badges.field.allowlist_create.help", "translation": "Укажите имена пользователей через запятую (,), которые могут создавать достижения этого типа."},
|
||||||
{"id": "badges.field.everyone_can_grant", "translation": "Все могут выдавать значки"},
|
{"id": "badges.field.everyone_can_grant", "translation": "Все могут выдавать достижения"},
|
||||||
{"id": "badges.field.everyone_can_grant.help", "translation": "Любой пользователь может выдать значок этого типа"},
|
{"id": "badges.field.everyone_can_grant.help", "translation": "Любой пользователь может выдать достижение этого типа"},
|
||||||
{"id": "badges.field.allowlist_grant", "translation": "Список допущенных к выдаче"},
|
{"id": "badges.field.allowlist_grant", "translation": "Список допущенных к выдаче"},
|
||||||
{"id": "badges.field.allowlist_grant.help", "translation": "Укажите имена пользователей через запятую (,), которые могут выдавать значки этого типа."},
|
{"id": "badges.field.allowlist_grant.help", "translation": "Укажите имена пользователей через запятую (,), которые могут выдавать достижения этого типа."},
|
||||||
{"id": "badges.field.delete_type", "translation": "Удалить тип"},
|
{"id": "badges.field.delete_type", "translation": "Удалить тип"},
|
||||||
{"id": "badges.field.delete_type.help", "translation": "ВНИМАНИЕ: если отметить, этот тип и все связанные значки будут удалены безвозвратно."},
|
{"id": "badges.field.delete_type.help", "translation": "ВНИМАНИЕ: если отметить, этот тип и все связанные достижения будут удалены безвозвратно."},
|
||||||
{"id": "badges.field.user", "translation": "Пользователь"},
|
{"id": "badges.field.user", "translation": "Пользователь"},
|
||||||
{"id": "badges.field.badge", "translation": "Значок"},
|
{"id": "badges.field.badge", "translation": "Достижение"},
|
||||||
{"id": "badges.field.reason", "translation": "Причина"},
|
{"id": "badges.field.reason", "translation": "Причина"},
|
||||||
{"id": "badges.field.reason.help", "translation": "Причина выдачи значка. Будет видна пользователю и в уведомлениях о выдаче (например, в подписках)."},
|
{"id": "badges.field.reason.help", "translation": "Причина выдачи достижения. Будет видна пользователю и в уведомлениях о выдаче (например, в подписках)."},
|
||||||
{"id": "badges.field.notify_here", "translation": "Уведомить в этом канале"},
|
{"id": "badges.field.notify_here", "translation": "Уведомить в этом канале"},
|
||||||
{"id": "badges.field.notify_here.help", "translation": "Если отметить, бот отправит сообщение в этот канал о том, что вы выдали значок этому пользователю."},
|
{"id": "badges.field.notify_here.help", "translation": "Если отметить, бот отправит сообщение в этот канал о том, что вы выдали достижение этому пользователю."},
|
||||||
|
|
||||||
{"id": "badges.error.unknown", "translation": "Произошла неизвестная ошибка. Обратитесь к системному администратору."},
|
{"id": "badges.error.unknown", "translation": "Произошла неизвестная ошибка. Обратитесь к системному администратору."},
|
||||||
{"id": "badges.error.cannot_get_user", "translation": "Не удалось получить пользователя."},
|
{"id": "badges.error.cannot_get_user", "translation": "Не удалось получить пользователя."},
|
||||||
{"id": "badges.error.only_sysadmin_clean", "translation": "Только системный администратор может очистить базу значков."},
|
{"id": "badges.error.only_sysadmin_clean", "translation": "Только системный администратор может очистить базу достижений."},
|
||||||
{"id": "badges.error.specify_create", "translation": "Укажите, что вы хотите создать."},
|
{"id": "badges.error.specify_create", "translation": "Укажите, что вы хотите создать."},
|
||||||
{"id": "badges.error.create_badge_or_type", "translation": "Можно создать badge или type"},
|
{"id": "badges.error.create_badge_or_type", "translation": "Можно создать badge или type"},
|
||||||
{"id": "badges.error.no_types_available", "translation": "Вы не можете создать значки ни одного типа."},
|
{"id": "badges.error.no_types_available", "translation": "Вы не можете создать достижения ни одного типа."},
|
||||||
{"id": "badges.error.must_set_badge_id", "translation": "Необходимо указать ID значка"},
|
{"id": "badges.error.must_set_badge_id", "translation": "Необходимо указать ID достижения"},
|
||||||
{"id": "badges.error.cannot_edit_badge", "translation": "У вас нет прав на редактирование этого значка"},
|
{"id": "badges.error.cannot_edit_badge", "translation": "У вас нет прав на редактирование этого достижения"},
|
||||||
{"id": "badges.error.specify_edit", "translation": "Укажите, что вы хотите отредактировать."},
|
{"id": "badges.error.specify_edit", "translation": "Укажите, что вы хотите отредактировать."},
|
||||||
{"id": "badges.error.edit_badge_or_type", "translation": "Можно редактировать badge или type"},
|
{"id": "badges.error.edit_badge_or_type", "translation": "Можно редактировать badge или type"},
|
||||||
{"id": "badges.error.no_permissions_edit_type", "translation": "У вас нет прав на редактирование типа значков."},
|
{"id": "badges.error.no_permissions_edit_type", "translation": "У вас нет прав на редактирование типа достижений."},
|
||||||
{"id": "badges.error.must_provide_type_id", "translation": "Необходимо указать ID типа"},
|
{"id": "badges.error.must_provide_type_id", "translation": "Необходимо указать ID типа"},
|
||||||
{"id": "badges.error.cannot_edit_type", "translation": "У вас нет прав на редактирование этого типа"},
|
{"id": "badges.error.cannot_edit_type", "translation": "У вас нет прав на редактирование этого типа"},
|
||||||
{"id": "badges.error.no_permissions_grant", "translation": "У вас нет прав на выдачу этого значка"},
|
{"id": "badges.error.no_permissions_grant", "translation": "У вас нет прав на выдачу этого достижения"},
|
||||||
{"id": "badges.error.cannot_grant_badge", "translation": "Вы не можете выдать этот значок"},
|
{"id": "badges.error.cannot_grant_badge", "translation": "Вы не можете выдать это достижение"},
|
||||||
{"id": "badges.error.specify_subscription", "translation": "Укажите, что вы хотите сделать."},
|
{"id": "badges.error.specify_subscription", "translation": "Укажите, что вы хотите сделать."},
|
||||||
{"id": "badges.error.create_or_delete_subscription", "translation": "Можно создать или удалить подписку"},
|
{"id": "badges.error.create_or_delete_subscription", "translation": "Можно создать или удалить подписку"},
|
||||||
{"id": "badges.error.cannot_create_subscription", "translation": "Вы не можете создавать подписки"},
|
{"id": "badges.error.cannot_create_subscription", "translation": "Вы не можете создавать подписки"},
|
||||||
{"id": "badges.error.no_permissions_create_type", "translation": "У вас нет прав на создание типа значков."},
|
{"id": "badges.error.no_permissions_create_type", "translation": "У вас нет прав на создание типа достижений."},
|
||||||
|
{"id": "badges.error.already_owned", "translation": "Это достижение уже выдано этому пользователю"},
|
||||||
|
|
||||||
{"id": "badges.success.clean", "translation": "Очищено"},
|
{"id": "badges.success.clean", "translation": "Очищено"},
|
||||||
{"id": "badges.success.granted", "translation": "Выдано"},
|
{"id": "badges.success.granted", "translation": "Выдано"},
|
||||||
@ -72,8 +73,8 @@
|
|||||||
{"id": "badges.api.empty_emoji", "translation": "Пустой эмодзи"},
|
{"id": "badges.api.empty_emoji", "translation": "Пустой эмодзи"},
|
||||||
{"id": "badges.api.invalid_field", "translation": "Некорректное поле"},
|
{"id": "badges.api.invalid_field", "translation": "Некорректное поле"},
|
||||||
{"id": "badges.api.type_not_exist", "translation": "Этот тип не существует"},
|
{"id": "badges.api.type_not_exist", "translation": "Этот тип не существует"},
|
||||||
{"id": "badges.api.no_permissions_create_badge", "translation": "У вас нет прав на создание этого значка"},
|
{"id": "badges.api.no_permissions_create_badge", "translation": "У вас нет прав на создание этого достижения"},
|
||||||
{"id": "badges.api.badge_created", "translation": "Значок `%s` создан."},
|
{"id": "badges.api.badge_created", "translation": "Достижение `%s` создано."},
|
||||||
{"id": "badges.api.no_permissions_create_type", "translation": "У вас нет прав на создание типа"},
|
{"id": "badges.api.no_permissions_create_type", "translation": "У вас нет прав на создание типа"},
|
||||||
{"id": "badges.api.cannot_find_user", "translation": "Не удалось найти пользователя"},
|
{"id": "badges.api.cannot_find_user", "translation": "Не удалось найти пользователя"},
|
||||||
{"id": "badges.api.error_getting_user", "translation": "Ошибка получения пользователя %s: %v"},
|
{"id": "badges.api.error_getting_user", "translation": "Ошибка получения пользователя %s: %v"},
|
||||||
@ -83,15 +84,15 @@
|
|||||||
{"id": "badges.api.could_not_get_type", "translation": "Не удалось получить тип"},
|
{"id": "badges.api.could_not_get_type", "translation": "Не удалось получить тип"},
|
||||||
{"id": "badges.api.no_permissions_edit_type", "translation": "У вас нет прав на редактирование этого типа"},
|
{"id": "badges.api.no_permissions_edit_type", "translation": "У вас нет прав на редактирование этого типа"},
|
||||||
{"id": "badges.api.type_updated", "translation": "Тип `%s` обновлён."},
|
{"id": "badges.api.type_updated", "translation": "Тип `%s` обновлён."},
|
||||||
{"id": "badges.api.cannot_get_badge", "translation": "Не удалось получить значок"},
|
{"id": "badges.api.cannot_get_badge", "translation": "Не удалось получить достижение"},
|
||||||
{"id": "badges.api.cannot_edit_badge", "translation": "Вы не можете редактировать этот значок"},
|
{"id": "badges.api.cannot_edit_badge", "translation": "Вы не можете редактировать это достижение"},
|
||||||
{"id": "badges.api.could_not_get_badge", "translation": "Не удалось получить значок"},
|
{"id": "badges.api.could_not_get_badge", "translation": "Не удалось получить достижение"},
|
||||||
{"id": "badges.api.no_permissions_edit_badge", "translation": "У вас нет прав на редактирование этого значка"},
|
{"id": "badges.api.no_permissions_edit_badge", "translation": "У вас нет прав на редактирование этого достижения"},
|
||||||
{"id": "badges.api.badge_updated", "translation": "Значок `%s` обновлён."},
|
{"id": "badges.api.badge_updated", "translation": "Достижение `%s` обновлёно."},
|
||||||
{"id": "badges.api.badge_not_found", "translation": "Значок не найден"},
|
{"id": "badges.api.badge_not_found", "translation": "Достижение не найдено"},
|
||||||
{"id": "badges.api.no_permissions_grant", "translation": "У вас нет прав на выдачу этого значка"},
|
{"id": "badges.api.no_permissions_grant", "translation": "У вас нет прав на выдачу этого достижения"},
|
||||||
{"id": "badges.api.user_not_found", "translation": "Пользователь не найден"},
|
{"id": "badges.api.user_not_found", "translation": "Пользователь не найден"},
|
||||||
{"id": "badges.api.badge_granted", "translation": "Значок `%s` выдан @%s."},
|
{"id": "badges.api.badge_granted", "translation": "Достижение `%s` выдан @%s."},
|
||||||
{"id": "badges.api.cannot_create_subscription", "translation": "Вы не можете создать подписку"},
|
{"id": "badges.api.cannot_create_subscription", "translation": "Вы не можете создать подписку"},
|
||||||
{"id": "badges.api.subscription_added", "translation": "Подписка добавлена"},
|
{"id": "badges.api.subscription_added", "translation": "Подписка добавлена"},
|
||||||
{"id": "badges.api.cannot_delete_subscription", "translation": "Вы не можете удалить подписку"},
|
{"id": "badges.api.cannot_delete_subscription", "translation": "Вы не можете удалить подписку"},
|
||||||
@ -99,9 +100,9 @@
|
|||||||
{"id": "badges.api.cannot_delete_default_type", "translation": "Нельзя удалить тип по умолчанию"},
|
{"id": "badges.api.cannot_delete_default_type", "translation": "Нельзя удалить тип по умолчанию"},
|
||||||
{"id": "badges.api.not_authorized", "translation": "Не авторизован"},
|
{"id": "badges.api.not_authorized", "translation": "Не авторизован"},
|
||||||
|
|
||||||
{"id": "badges.notify.dm_text", "translation": "@%s выдал вам значок %s`%s`."},
|
{"id": "badges.notify.dm_text", "translation": "@%s выдал вам достижение %s`%s`."},
|
||||||
{"id": "badges.notify.dm_reason", "translation": "\nПочему? "},
|
{"id": "badges.notify.dm_reason", "translation": "\nПочему? "},
|
||||||
{"id": "badges.notify.title", "translation": "%sзначок выдан!"},
|
{"id": "badges.notify.title", "translation": "%sдостижение выдано!"},
|
||||||
{"id": "badges.notify.channel_text", "translation": "@%s выдал @%s значок %s`%s`."},
|
{"id": "badges.notify.channel_text", "translation": "@%s выдал @%s достижение %s`%s`."},
|
||||||
{"id": "badges.notify.no_permission_channel", "translation": "У вас нет прав на отправку уведомления о выдаче в этот канал."}
|
{"id": "badges.notify.no_permission_channel", "translation": "У вас нет прав на отправку уведомления о выдаче в этот канал."}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import (
|
|||||||
|
|
||||||
var errInvalidBadge = errors.New("invalid badge")
|
var errInvalidBadge = errors.New("invalid badge")
|
||||||
var errBadgeNotFound = errors.New("badge not found")
|
var errBadgeNotFound = errors.New("badge not found")
|
||||||
|
var errAlreadyOwned = errors.New("already owned")
|
||||||
|
|
||||||
type Store interface {
|
type Store interface {
|
||||||
// Interface
|
// Interface
|
||||||
@ -33,6 +34,8 @@ type Store interface {
|
|||||||
UpdateBadge(b *badgesmodel.Badge) error
|
UpdateBadge(b *badgesmodel.Badge) error
|
||||||
DeleteType(tID badgesmodel.BadgeType) error
|
DeleteType(tID badgesmodel.BadgeType) error
|
||||||
DeleteBadge(bID badgesmodel.BadgeID) error
|
DeleteBadge(bID badgesmodel.BadgeID) error
|
||||||
|
RevokeOwnership(badgeID badgesmodel.BadgeID, userID string, grantTime string) error
|
||||||
|
FindOwnership(badgeID badgesmodel.BadgeID, userID string, grantTime string) (*badgesmodel.Ownership, error)
|
||||||
|
|
||||||
AddSubscription(tID badgesmodel.BadgeType, cID string) error
|
AddSubscription(tID badgesmodel.BadgeType, cID string) error
|
||||||
RemoveSubscriptions(tID badgesmodel.BadgeType, cID string) error
|
RemoveSubscriptions(tID badgesmodel.BadgeType, cID string) error
|
||||||
@ -503,6 +506,25 @@ func (s *store) AddSubscription(tID badgesmodel.BadgeType, cID string) error {
|
|||||||
return s.doAtomic(func() (bool, error) { return s.atomicAddSubscription(toAdd) })
|
return s.doAtomic(func() (bool, error) { return s.atomicAddSubscription(toAdd) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *store) FindOwnership(badgeID badgesmodel.BadgeID, userID string, grantTime string) (*badgesmodel.Ownership, error) {
|
||||||
|
ownership, _, err := s.getOwnershipList()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, o := range ownership {
|
||||||
|
if o.Badge == badgeID && o.User == userID && o.Time.Format(time.RFC3339Nano) == grantTime {
|
||||||
|
return &o, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("ownership not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *store) RevokeOwnership(badgeID badgesmodel.BadgeID, userID string, grantTime string) error {
|
||||||
|
return s.doAtomic(func() (bool, error) { return s.atomicRevokeOwnership(badgeID, userID, grantTime) })
|
||||||
|
}
|
||||||
|
|
||||||
func (s *store) RemoveSubscriptions(tID badgesmodel.BadgeType, cID string) error {
|
func (s *store) RemoveSubscriptions(tID badgesmodel.BadgeType, cID string) error {
|
||||||
toRemove := badgesmodel.Subscription{ChannelID: cID, TypeID: tID}
|
toRemove := badgesmodel.Subscription{ChannelID: cID, TypeID: tID}
|
||||||
return s.doAtomic(func() (bool, error) { return s.atomicRemoveSubscription(toRemove) })
|
return s.doAtomic(func() (bool, error) { return s.atomicRemoveSubscription(toRemove) })
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/larkox/mattermost-plugin-badges/badgesmodel"
|
"github.com/larkox/mattermost-plugin-badges/badgesmodel"
|
||||||
)
|
)
|
||||||
@ -107,7 +108,7 @@ func (s *store) atomicAddBadgeToOwnership(o badgesmodel.Ownership, isMultiple bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !isMultiple && ownership.IsOwned(o.User, o.Badge) {
|
if !isMultiple && ownership.IsOwned(o.User, o.Badge) {
|
||||||
return false, true, nil
|
return false, true, errAlreadyOwned
|
||||||
}
|
}
|
||||||
|
|
||||||
ownership = append(ownership, o)
|
ownership = append(ownership, o)
|
||||||
@ -159,6 +160,28 @@ func (s *store) atomicUpdateBadge(b *badgesmodel.Badge) (bool, error) {
|
|||||||
return s.compareAndSet(KVKeyBadges, data, bb)
|
return s.compareAndSet(KVKeyBadges, data, bb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *store) atomicRevokeOwnership(badgeID badgesmodel.BadgeID, userID string, grantTime string) (bool, error) {
|
||||||
|
ownership, data, err := s.getOwnershipList()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for i, o := range ownership {
|
||||||
|
if o.Badge == badgeID && o.User == userID && o.Time.Format(time.RFC3339Nano) == grantTime {
|
||||||
|
ownership = append(ownership[:i], ownership[i+1:]...)
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.compareAndSet(KVKeyOwnership, data, ownership)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *store) atomicAddSubscription(toAdd badgesmodel.Subscription) (bool, error) {
|
func (s *store) atomicAddSubscription(toAdd badgesmodel.Subscription) (bool, error) {
|
||||||
subs, data, err := s.getAllSubscriptions()
|
subs, data, err := s.getAllSubscriptions()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"badges.loading": "Loading...",
|
"badges.loading": "Loading...",
|
||||||
"badges.no_badges_yet": "No badges yet.",
|
"badges.no_badges_yet": "No achievements yet.",
|
||||||
"badges.empty.title": "No badges yet",
|
"badges.empty.title": "No achievements yet",
|
||||||
"badges.empty.description": "Create your first badge to recognize achievements and contributions of your team members.",
|
"badges.empty.description": "Create your first achievement to recognize contributions of your team members.",
|
||||||
"badges.badge_not_found": "Badge not found.",
|
"badges.badge_not_found": "Achievement not found.",
|
||||||
"badges.user_not_found": "User not found.",
|
"badges.user_not_found": "User not found.",
|
||||||
"badges.unknown": "unknown",
|
"badges.unknown": "unknown",
|
||||||
|
|
||||||
"badges.rhs.all_badges": "All badges",
|
"badges.rhs.all_badges": "All achievements",
|
||||||
"badges.rhs.my_badges": "My badges",
|
"badges.rhs.my_badges": "My achievements",
|
||||||
"badges.rhs.user_badges": "@{username}'s badges",
|
"badges.rhs.user_badges": "@{username}'s achievements",
|
||||||
"badges.rhs.badge_details": "Badge Details",
|
"badges.rhs.badge_details": "Achievement Details",
|
||||||
|
|
||||||
"badges.label.name": "Name:",
|
"badges.label.name": "Name:",
|
||||||
"badges.label.description": "Description:",
|
"badges.label.description": "Description:",
|
||||||
@ -27,55 +27,56 @@
|
|||||||
"badges.granted_to": "Granted to:",
|
"badges.granted_to": "Granted to:",
|
||||||
"badges.not_granted_yet": "Not granted to anyone yet",
|
"badges.not_granted_yet": "Not granted to anyone yet",
|
||||||
|
|
||||||
"badges.set_status": "Set status to this badge",
|
"badges.set_status": "Set status to this achievement",
|
||||||
"badges.grant_badge": "Grant badge",
|
"badges.grant_badge": "Grant achievement",
|
||||||
"badges.and_more": "and {count} more. Click to see all.",
|
"badges.and_more": "and {count} more. Click to see all.",
|
||||||
|
|
||||||
"badges.menu.open_list": "Open the list of all badges.",
|
"badges.menu.open_list": "Open the list of all achievements.",
|
||||||
"badges.menu.create_badge": "Create badge",
|
"badges.menu.create_badge": "Create achievement",
|
||||||
"badges.menu.create_type": "Create badge type",
|
"badges.menu.create_type": "Create achievement type",
|
||||||
"badges.menu.add_subscription": "Add badge subscription",
|
"badges.menu.add_subscription": "Add achievement subscription",
|
||||||
"badges.menu.remove_subscription": "Remove badge subscription",
|
"badges.menu.remove_subscription": "Remove achievement subscription",
|
||||||
|
|
||||||
"badges.sidebar.title": "Badges",
|
"badges.sidebar.title": "Achievements",
|
||||||
"badges.popover.title": "Badges",
|
"badges.popover.title": "Achievements",
|
||||||
|
|
||||||
"badges.admin.label": "Achievements Administrators:",
|
"badges.admin.label": "Achievements Administrators:",
|
||||||
"badges.admin.placeholder": "Start typing a name...",
|
"badges.admin.placeholder": "Start typing a name...",
|
||||||
"badges.admin.help_text": "These users will be considered achievements plugin administrators. They can create types, as well as modify and grant any badges.",
|
"badges.admin.help_text": "These users will be considered achievements plugin administrators. They can create types, as well as modify and grant any achievements.",
|
||||||
"badges.admin.no_results": "No users found",
|
"badges.admin.no_results": "No users found",
|
||||||
|
|
||||||
"badges.rhs.create_badge": "+ Create badge",
|
"badges.rhs.create_badge": "+ Create achievement",
|
||||||
"badges.rhs.edit_badge": "Edit",
|
"badges.rhs.edit_badge": "Edit",
|
||||||
"badges.rhs.types": "Types",
|
"badges.rhs.types": "Types",
|
||||||
"badges.rhs.create_type": "+ Create type",
|
"badges.rhs.create_type": "+ Create type",
|
||||||
|
|
||||||
"badges.modal.create_badge_title": "Create Badge",
|
"badges.modal.create_badge_title": "Create Achievement",
|
||||||
"badges.modal.edit_badge_title": "Edit Badge",
|
"badges.modal.edit_badge_title": "Edit Achievement",
|
||||||
"badges.modal.field_name": "Name",
|
"badges.modal.field_name": "Name",
|
||||||
"badges.modal.field_name_placeholder": "Badge name (max 20 chars)",
|
"badges.modal.field_name_placeholder": "Achievement name (max 20 chars)",
|
||||||
"badges.modal.field_description": "Description",
|
"badges.modal.field_description": "Description",
|
||||||
"badges.modal.field_description_placeholder": "Badge description (max 120 chars)",
|
"badges.modal.field_description_placeholder": "Achievement description (max 120 chars)",
|
||||||
"badges.modal.field_image": "Emoji",
|
"badges.modal.field_image": "Emoji",
|
||||||
"badges.modal.field_image_placeholder": "Emoji name (e.g. star)",
|
"badges.modal.field_image_placeholder": "Emoji name (e.g. star)",
|
||||||
"badges.modal.field_type": "Type",
|
"badges.modal.field_type": "Type",
|
||||||
"badges.modal.field_type_placeholder": "Select badge type",
|
"badges.modal.field_type_placeholder": "Select achievement type",
|
||||||
"badges.modal.field_multiple": "Can be granted multiple times",
|
"badges.modal.field_multiple": "Can be granted multiple times",
|
||||||
"badges.modal.create_new_type": "+ Create new type",
|
"badges.modal.create_new_type": "+ Create new type",
|
||||||
"badges.modal.new_type_name": "Type name",
|
"badges.modal.new_type_name": "Type name",
|
||||||
"badges.modal.new_type_name_placeholder": "Type name (max 20 chars)",
|
"badges.modal.new_type_name_placeholder": "Type name (max 20 chars)",
|
||||||
"badges.modal.new_type_everyone_create": "Everyone can create badges",
|
"badges.modal.new_type_everyone_create": "Everyone can create achievements",
|
||||||
"badges.modal.new_type_everyone_grant": "Everyone can grant badges",
|
"badges.modal.new_type_everyone_grant": "Everyone can grant achievements",
|
||||||
"badges.modal.btn_cancel": "Cancel",
|
"badges.modal.btn_cancel": "Cancel",
|
||||||
"badges.modal.btn_create": "Create",
|
"badges.modal.btn_create": "Create",
|
||||||
"badges.modal.btn_save": "Save",
|
"badges.modal.btn_save": "Save",
|
||||||
"badges.modal.btn_creating": "Saving...",
|
"badges.modal.btn_creating": "Saving...",
|
||||||
"badges.modal.btn_delete": "Delete badge",
|
"badges.modal.btn_delete": "Delete achievement",
|
||||||
"badges.modal.btn_confirm_delete": "Yes, delete",
|
"badges.modal.btn_confirm_delete": "Yes, delete",
|
||||||
"badges.modal.confirm_delete": "Are you sure?",
|
"badges.modal.confirm_delete": "Are you sure?",
|
||||||
|
"badges.modal.confirm_delete_badge": "Delete achievement \"{name}\"?",
|
||||||
"badges.modal.error_generic": "An error occurred",
|
"badges.modal.error_generic": "An error occurred",
|
||||||
"badges.modal.error_type_name_required": "Enter type name",
|
"badges.modal.error_type_name_required": "Enter type name",
|
||||||
"badges.modal.error_type_required": "Select badge type",
|
"badges.modal.error_type_required": "Select achievement type",
|
||||||
"badges.modal.create_type_title": "Create Type",
|
"badges.modal.create_type_title": "Create Type",
|
||||||
"badges.modal.edit_type_title": "Edit Type",
|
"badges.modal.edit_type_title": "Edit Type",
|
||||||
"badges.modal.btn_delete_type": "Delete type",
|
"badges.modal.btn_delete_type": "Delete type",
|
||||||
@ -83,65 +84,73 @@
|
|||||||
"badges.modal.confirm_delete_type": "Delete type \"{name}\"?",
|
"badges.modal.confirm_delete_type": "Delete type \"{name}\"?",
|
||||||
"badges.modal.btn_confirm_delete_type": "Yes, delete",
|
"badges.modal.btn_confirm_delete_type": "Yes, delete",
|
||||||
|
|
||||||
"badges.types.badge_count": "{count, plural, one {# badge} other {# badges}}",
|
"badges.types.badge_count": "{count, plural, one {# achievement} other {# achievements}}",
|
||||||
"badges.types.everyone_can_create": "Everyone creates",
|
"badges.types.everyone_can_create": "Everyone creates",
|
||||||
"badges.types.everyone_can_grant": "Everyone grants",
|
"badges.types.everyone_can_grant": "Everyone grants",
|
||||||
"badges.types.is_default": "Default",
|
"badges.types.is_default": "Default",
|
||||||
"badges.types.confirm_delete": "Delete type \"{name}\" and all its badges?",
|
"badges.types.confirm_delete": "Delete type \"{name}\" and all its achievements?",
|
||||||
"badges.types.empty": "No types yet",
|
"badges.types.empty": "No types yet",
|
||||||
"badges.types.no_badges": "No badges in this type",
|
"badges.types.no_badges": "No achievements in this type",
|
||||||
"badges.rhs.back_to_types": "Back to types",
|
"badges.rhs.back_to_types": "Back to types",
|
||||||
|
|
||||||
"badges.modal.allowlist_create": "Allowlist for creation",
|
"badges.modal.allowlist_create": "Allowlist for creation",
|
||||||
"badges.modal.allowlist_create_help": "Users who can create badges of this type.",
|
"badges.modal.allowlist_create_help": "Users who can create achievements of this type.",
|
||||||
"badges.modal.allowlist_grant": "Allowlist for granting",
|
"badges.modal.allowlist_grant": "Allowlist for granting",
|
||||||
"badges.modal.allowlist_grant_help": "Users who can grant badges of this type.",
|
"badges.modal.allowlist_grant_help": "Users who can grant achievements of this type.",
|
||||||
"badges.modal.allowlist_placeholder": "user-1, user-2, user-3",
|
"badges.modal.allowlist_placeholder": "user-1, user-2, user-3",
|
||||||
|
|
||||||
"badges.grant.title": "Grant Badge",
|
"badges.grant.title": "Grant Achievement",
|
||||||
"badges.grant.intro": "Grant badge to @{username}",
|
"badges.grant.intro": "Grant achievement to @{username}",
|
||||||
"badges.grant.field_badge": "Badge",
|
"badges.grant.field_badge": "Achievement",
|
||||||
"badges.grant.field_badge_placeholder": "Select a badge",
|
"badges.grant.field_badge_placeholder": "Select an achievement",
|
||||||
"badges.grant.no_badges": "No badges available",
|
"badges.grant.no_badges": "No achievements available",
|
||||||
"badges.grant.field_reason": "Reason",
|
"badges.grant.field_reason": "Reason",
|
||||||
"badges.grant.field_reason_placeholder": "Why is this badge being granted? (optional)",
|
"badges.grant.field_reason_placeholder": "Why is this achievement being granted? (optional)",
|
||||||
"badges.grant.notify_here": "Notify in channel",
|
"badges.grant.notify_here": "Notify in channel",
|
||||||
"badges.grant.btn_grant": "Grant",
|
"badges.grant.btn_grant": "Grant",
|
||||||
|
|
||||||
|
"badges.revoke.btn": "Revoke",
|
||||||
|
"badges.revoke.confirm": "Revoke achievement?",
|
||||||
|
"badges.revoke.confirm_yes": "Yes",
|
||||||
|
|
||||||
"badges.subscription.title_create": "Add Subscription",
|
"badges.subscription.title_create": "Add Subscription",
|
||||||
"badges.subscription.title_delete": "Remove Subscription",
|
"badges.subscription.title_delete": "Remove Subscription",
|
||||||
"badges.subscription.field_type": "Badge Type",
|
"badges.subscription.field_type": "Achievement Type",
|
||||||
"badges.subscription.field_type_placeholder": "Select badge type",
|
"badges.subscription.field_type_placeholder": "Select achievement type",
|
||||||
"badges.subscription.no_types": "No types available",
|
"badges.subscription.no_types": "No types available",
|
||||||
"badges.subscription.btn_create": "Add",
|
"badges.subscription.btn_create": "Add",
|
||||||
"badges.subscription.btn_delete": "Remove",
|
"badges.subscription.btn_delete": "Remove",
|
||||||
|
|
||||||
"badges.error.invalid_badge_id": "Badge not specified",
|
"badges.error.invalid_badge_id": "Achievement not specified",
|
||||||
"badges.error.invalid_user_id": "User not specified",
|
"badges.error.invalid_user_id": "User not specified",
|
||||||
"badges.error.no_permission_grant": "Insufficient permissions to grant this badge",
|
"badges.error.no_permission_grant": "Insufficient permissions to grant this achievement",
|
||||||
"badges.error.cannot_grant_badge": "Failed to grant badge",
|
"badges.error.cannot_grant_badge": "Failed to grant achievement",
|
||||||
"badges.error.user_not_found": "User not found",
|
"badges.error.user_not_found": "User not found",
|
||||||
"badges.error.invalid_type_id": "Badge type not specified",
|
"badges.error.invalid_type_id": "Achievement type not specified",
|
||||||
"badges.error.no_permission_subscription": "Insufficient permissions to manage subscriptions",
|
"badges.error.no_permission_subscription": "Insufficient permissions to manage subscriptions",
|
||||||
"badges.error.cannot_create_subscription": "Failed to create subscription",
|
"badges.error.cannot_create_subscription": "Failed to create subscription",
|
||||||
"badges.error.cannot_delete_subscription": "Failed to delete subscription",
|
"badges.error.cannot_delete_subscription": "Failed to delete subscription",
|
||||||
|
"badges.error.ownership_not_found": "Ownership not found",
|
||||||
|
"badges.error.no_permission_revoke": "Insufficient permissions to revoke",
|
||||||
|
"badges.error.cannot_revoke": "Failed to revoke",
|
||||||
|
"badges.error.already_owned": "This achievement is already owned by this user",
|
||||||
|
|
||||||
"badges.error.unknown": "An error occurred",
|
"badges.error.unknown": "An error occurred",
|
||||||
"badges.error.cannot_get_user": "Failed to get user data",
|
"badges.error.cannot_get_user": "Failed to get user data",
|
||||||
"badges.error.cannot_get_types": "Failed to load types",
|
"badges.error.cannot_get_types": "Failed to load types",
|
||||||
"badges.error.cannot_get_badges": "Failed to load badges",
|
"badges.error.cannot_get_badges": "Failed to load achievements",
|
||||||
"badges.error.invalid_request": "Invalid request format",
|
"badges.error.invalid_request": "Invalid request format",
|
||||||
"badges.error.invalid_name": "Name is required",
|
"badges.error.invalid_name": "Name is required",
|
||||||
"badges.error.invalid_image": "Emoji is required",
|
"badges.error.invalid_image": "Emoji is required",
|
||||||
"badges.error.type_not_found": "Badge type not found",
|
"badges.error.type_not_found": "Achievement type not found",
|
||||||
"badges.error.badge_not_found": "Badge not found",
|
"badges.error.badge_not_found": "Achievement not found",
|
||||||
"badges.error.no_permission": "Insufficient permissions",
|
"badges.error.no_permission": "Insufficient permissions",
|
||||||
"badges.error.missing_badge_id": "Badge ID is missing",
|
"badges.error.missing_badge_id": "Achievement ID is missing",
|
||||||
"badges.error.missing_type_id": "Type ID is missing",
|
"badges.error.missing_type_id": "Type ID is missing",
|
||||||
"badges.error.cannot_create_badge": "Failed to create badge",
|
"badges.error.cannot_create_badge": "Failed to create achievement",
|
||||||
"badges.error.cannot_create_type": "Failed to create type",
|
"badges.error.cannot_create_type": "Failed to create type",
|
||||||
"badges.error.cannot_update_badge": "Failed to update badge",
|
"badges.error.cannot_update_badge": "Failed to update achievement",
|
||||||
"badges.error.cannot_delete_badge": "Failed to delete badge",
|
"badges.error.cannot_delete_badge": "Failed to delete achievement",
|
||||||
"badges.error.cannot_update_type": "Failed to update type",
|
"badges.error.cannot_update_type": "Failed to update type",
|
||||||
"badges.error.cannot_delete_type": "Failed to delete type",
|
"badges.error.cannot_delete_type": "Failed to delete type",
|
||||||
|
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"badges.loading": "Загрузка...",
|
"badges.loading": "Загрузка...",
|
||||||
"badges.no_badges_yet": "Значков пока нет.",
|
"badges.no_badges_yet": "Достижений пока нет.",
|
||||||
"badges.empty.title": "Значков пока нет",
|
"badges.empty.title": "Достижений пока нет",
|
||||||
"badges.empty.description": "Создайте первый значок, чтобы отмечать достижения и заслуги участников команды.",
|
"badges.empty.description": "Создайте первое достижение, чтобы отмечать заслуги участников команды.",
|
||||||
"badges.badge_not_found": "Значок не найден.",
|
"badges.badge_not_found": "Достижение не найдено.",
|
||||||
"badges.user_not_found": "Пользователь не найден.",
|
"badges.user_not_found": "Пользователь не найден.",
|
||||||
"badges.unknown": "неизвестно",
|
"badges.unknown": "неизвестно",
|
||||||
|
|
||||||
"badges.rhs.all_badges": "Все значки",
|
"badges.rhs.all_badges": "Все достижения",
|
||||||
"badges.rhs.my_badges": "Мои значки",
|
"badges.rhs.my_badges": "Мои достижения",
|
||||||
"badges.rhs.user_badges": "Значки @{username}",
|
"badges.rhs.user_badges": "Достижения @{username}",
|
||||||
"badges.rhs.badge_details": "Детали значка",
|
"badges.rhs.badge_details": "Детали достижения",
|
||||||
|
|
||||||
"badges.label.name": "Название:",
|
"badges.label.name": "Название:",
|
||||||
"badges.label.description": "Описание:",
|
"badges.label.description": "Описание:",
|
||||||
@ -28,54 +28,55 @@
|
|||||||
"badges.not_granted_yet": "Ещё никому не выдан",
|
"badges.not_granted_yet": "Ещё никому не выдан",
|
||||||
|
|
||||||
"badges.set_status": "Установить как статус",
|
"badges.set_status": "Установить как статус",
|
||||||
"badges.grant_badge": "Выдать значок",
|
"badges.grant_badge": "Выдать достижение",
|
||||||
"badges.and_more": "и ещё {count}. Нажмите, чтобы увидеть все.",
|
"badges.and_more": "и ещё {count}. Нажмите, чтобы увидеть все.",
|
||||||
|
|
||||||
"badges.menu.open_list": "Открыть список всех значков.",
|
"badges.menu.open_list": "Открыть список всех достижений.",
|
||||||
"badges.menu.create_badge": "Создать значок",
|
"badges.menu.create_badge": "Создать достижение",
|
||||||
"badges.menu.create_type": "Создать тип значков",
|
"badges.menu.create_type": "Создать тип достижений",
|
||||||
"badges.menu.add_subscription": "Добавить подписку на значки",
|
"badges.menu.add_subscription": "Добавить подписку на достижения",
|
||||||
"badges.menu.remove_subscription": "Удалить подписку на значки",
|
"badges.menu.remove_subscription": "Удалить подписку на достижения",
|
||||||
|
|
||||||
"badges.sidebar.title": "Значки",
|
"badges.sidebar.title": "Достижения",
|
||||||
"badges.popover.title": "Значки",
|
"badges.popover.title": "Достижения",
|
||||||
|
|
||||||
"badges.admin.label": "Администраторы достижений:",
|
"badges.admin.label": "Администраторы достижений:",
|
||||||
"badges.admin.placeholder": "Начните вводить имя...",
|
"badges.admin.placeholder": "Начните вводить имя...",
|
||||||
"badges.admin.help_text": "Эти пользователи будут считаться администраторами плагина достижений. Они могут создавать типы, а также изменять и выдавать любые значки.",
|
"badges.admin.help_text": "Эти пользователи будут считаться администраторами плагина достижений. Они могут создавать типы, а также изменять и выдавать любые достижения.",
|
||||||
"badges.admin.no_results": "Пользователь не найден",
|
"badges.admin.no_results": "Пользователь не найден",
|
||||||
|
|
||||||
"badges.rhs.create_badge": "+ Создать значок",
|
"badges.rhs.create_badge": "+ Создать достижение",
|
||||||
"badges.rhs.edit_badge": "Редактировать",
|
"badges.rhs.edit_badge": "Редактировать",
|
||||||
"badges.rhs.types": "Типы",
|
"badges.rhs.types": "Типы",
|
||||||
"badges.rhs.create_type": "+ Создать тип",
|
"badges.rhs.create_type": "+ Создать тип",
|
||||||
|
|
||||||
"badges.modal.create_badge_title": "Создать значок",
|
"badges.modal.create_badge_title": "Создать достижение",
|
||||||
"badges.modal.edit_badge_title": "Редактировать значок",
|
"badges.modal.edit_badge_title": "Редактировать достижение",
|
||||||
"badges.modal.field_name": "Название",
|
"badges.modal.field_name": "Название",
|
||||||
"badges.modal.field_name_placeholder": "Название значка (макс. 20 символов)",
|
"badges.modal.field_name_placeholder": "Название достижения (макс. 20 символов)",
|
||||||
"badges.modal.field_description": "Описание",
|
"badges.modal.field_description": "Описание",
|
||||||
"badges.modal.field_description_placeholder": "Описание значка (макс. 120 символов)",
|
"badges.modal.field_description_placeholder": "Описание достижения (макс. 120 символов)",
|
||||||
"badges.modal.field_image": "Эмодзи",
|
"badges.modal.field_image": "Эмодзи",
|
||||||
"badges.modal.field_image_placeholder": "Название эмодзи (напр. star)",
|
"badges.modal.field_image_placeholder": "Название эмодзи (напр. star)",
|
||||||
"badges.modal.field_type": "Тип",
|
"badges.modal.field_type": "Тип",
|
||||||
"badges.modal.field_type_placeholder": "Выберите тип значка",
|
"badges.modal.field_type_placeholder": "Выберите тип достижения",
|
||||||
"badges.modal.field_multiple": "Можно выдавать несколько раз",
|
"badges.modal.field_multiple": "Можно выдавать несколько раз",
|
||||||
"badges.modal.create_new_type": "+ Создать новый тип",
|
"badges.modal.create_new_type": "+ Создать новый тип",
|
||||||
"badges.modal.new_type_name": "Название типа",
|
"badges.modal.new_type_name": "Название типа",
|
||||||
"badges.modal.new_type_name_placeholder": "Название типа (макс. 20 символов)",
|
"badges.modal.new_type_name_placeholder": "Название типа (макс. 20 символов)",
|
||||||
"badges.modal.new_type_everyone_create": "Все могут создавать значки",
|
"badges.modal.new_type_everyone_create": "Все могут создавать достижения",
|
||||||
"badges.modal.new_type_everyone_grant": "Все могут выдавать значки",
|
"badges.modal.new_type_everyone_grant": "Все могут выдавать достижения",
|
||||||
"badges.modal.btn_cancel": "Отмена",
|
"badges.modal.btn_cancel": "Отмена",
|
||||||
"badges.modal.btn_create": "Создать",
|
"badges.modal.btn_create": "Создать",
|
||||||
"badges.modal.btn_save": "Сохранить",
|
"badges.modal.btn_save": "Сохранить",
|
||||||
"badges.modal.btn_creating": "Сохранение...",
|
"badges.modal.btn_creating": "Сохранение...",
|
||||||
"badges.modal.btn_delete": "Удалить значок",
|
"badges.modal.btn_delete": "Удалить достижение",
|
||||||
"badges.modal.btn_confirm_delete": "Да, удалить",
|
"badges.modal.btn_confirm_delete": "Да, удалить",
|
||||||
"badges.modal.confirm_delete": "Вы уверены?",
|
"badges.modal.confirm_delete": "Вы уверены?",
|
||||||
|
"badges.modal.confirm_delete_badge": "Удалить достижение «{name}»?",
|
||||||
"badges.modal.error_generic": "Произошла ошибка",
|
"badges.modal.error_generic": "Произошла ошибка",
|
||||||
"badges.modal.error_type_name_required": "Введите название типа",
|
"badges.modal.error_type_name_required": "Введите название типа",
|
||||||
"badges.modal.error_type_required": "Выберите тип значка",
|
"badges.modal.error_type_required": "Выберите тип достижения",
|
||||||
"badges.modal.create_type_title": "Создать тип",
|
"badges.modal.create_type_title": "Создать тип",
|
||||||
"badges.modal.edit_type_title": "Редактировать тип",
|
"badges.modal.edit_type_title": "Редактировать тип",
|
||||||
"badges.modal.btn_delete_type": "Удалить тип",
|
"badges.modal.btn_delete_type": "Удалить тип",
|
||||||
@ -83,65 +84,73 @@
|
|||||||
"badges.modal.confirm_delete_type": "Удалить тип «{name}»?",
|
"badges.modal.confirm_delete_type": "Удалить тип «{name}»?",
|
||||||
"badges.modal.btn_confirm_delete_type": "Да, удалить",
|
"badges.modal.btn_confirm_delete_type": "Да, удалить",
|
||||||
|
|
||||||
"badges.types.badge_count": "{count, plural, one {# значок} few {# значка} many {# значков} other {# значков}}",
|
"badges.types.badge_count": "{count, plural, one {# достижение} few {# достижения} many {# достижений} other {# достижений}}",
|
||||||
"badges.types.everyone_can_create": "Все создают",
|
"badges.types.everyone_can_create": "Все создают",
|
||||||
"badges.types.everyone_can_grant": "Все выдают",
|
"badges.types.everyone_can_grant": "Все выдают",
|
||||||
"badges.types.is_default": "По умолчанию",
|
"badges.types.is_default": "По умолчанию",
|
||||||
"badges.types.confirm_delete": "Удалить тип «{name}» и все его значки?",
|
"badges.types.confirm_delete": "Удалить тип «{name}» и все его достижения?",
|
||||||
"badges.types.empty": "Типов пока нет",
|
"badges.types.empty": "Типов пока нет",
|
||||||
"badges.types.no_badges": "В этом типе нет значков",
|
"badges.types.no_badges": "В этом типе нет достижений",
|
||||||
"badges.rhs.back_to_types": "Назад к типам",
|
"badges.rhs.back_to_types": "Назад к типам",
|
||||||
|
|
||||||
"badges.modal.allowlist_create": "Список допущенных к созданию",
|
"badges.modal.allowlist_create": "Список допущенных к созданию",
|
||||||
"badges.modal.allowlist_create_help": "Пользователи, которые могут создавать значки этого типа.",
|
"badges.modal.allowlist_create_help": "Пользователи, которые могут создавать достижения этого типа.",
|
||||||
"badges.modal.allowlist_grant": "Список допущенных к выдаче",
|
"badges.modal.allowlist_grant": "Список допущенных к выдаче",
|
||||||
"badges.modal.allowlist_grant_help": "Пользователи, которые могут выдавать значки этого типа.",
|
"badges.modal.allowlist_grant_help": "Пользователи, которые могут выдавать достижения этого типа.",
|
||||||
"badges.modal.allowlist_placeholder": "user-1, user-2, user-3",
|
"badges.modal.allowlist_placeholder": "user-1, user-2, user-3",
|
||||||
|
|
||||||
"badges.grant.title": "Выдать значок",
|
"badges.grant.title": "Выдать достижение",
|
||||||
"badges.grant.intro": "Выдать значок пользователю @{username}",
|
"badges.grant.intro": "Выдать достижение пользователю @{username}",
|
||||||
"badges.grant.field_badge": "Значок",
|
"badges.grant.field_badge": "Достижение",
|
||||||
"badges.grant.field_badge_placeholder": "Выберите значок",
|
"badges.grant.field_badge_placeholder": "Выберите достижение",
|
||||||
"badges.grant.no_badges": "Нет доступных значков",
|
"badges.grant.no_badges": "Нет доступных достижений",
|
||||||
"badges.grant.field_reason": "Причина",
|
"badges.grant.field_reason": "Причина",
|
||||||
"badges.grant.field_reason_placeholder": "За что выдаётся значок? (необязательно)",
|
"badges.grant.field_reason_placeholder": "За что выдаётся достижение? (необязательно)",
|
||||||
"badges.grant.notify_here": "Уведомить в канале",
|
"badges.grant.notify_here": "Уведомить в канале",
|
||||||
"badges.grant.btn_grant": "Выдать",
|
"badges.grant.btn_grant": "Выдать",
|
||||||
|
|
||||||
|
"badges.revoke.btn": "Снять достижение",
|
||||||
|
"badges.revoke.confirm": "Снять достижение?",
|
||||||
|
"badges.revoke.confirm_yes": "Да",
|
||||||
|
|
||||||
"badges.subscription.title_create": "Добавить подписку",
|
"badges.subscription.title_create": "Добавить подписку",
|
||||||
"badges.subscription.title_delete": "Удалить подписку",
|
"badges.subscription.title_delete": "Удалить подписку",
|
||||||
"badges.subscription.field_type": "Тип значков",
|
"badges.subscription.field_type": "Тип достижений",
|
||||||
"badges.subscription.field_type_placeholder": "Выберите тип значков",
|
"badges.subscription.field_type_placeholder": "Выберите тип достижений",
|
||||||
"badges.subscription.no_types": "Нет доступных типов",
|
"badges.subscription.no_types": "Нет доступных типов",
|
||||||
"badges.subscription.btn_create": "Добавить",
|
"badges.subscription.btn_create": "Добавить",
|
||||||
"badges.subscription.btn_delete": "Удалить",
|
"badges.subscription.btn_delete": "Удалить",
|
||||||
|
|
||||||
"badges.error.invalid_badge_id": "Не указан значок",
|
"badges.error.invalid_badge_id": "Не указано достижение",
|
||||||
"badges.error.invalid_user_id": "Не указан пользователь",
|
"badges.error.invalid_user_id": "Не указан пользователь",
|
||||||
"badges.error.no_permission_grant": "Недостаточно прав для выдачи этого значка",
|
"badges.error.no_permission_grant": "Недостаточно прав для выдачи этого достижения",
|
||||||
"badges.error.cannot_grant_badge": "Не удалось выдать значок",
|
"badges.error.cannot_grant_badge": "Не удалось выдать достижение",
|
||||||
"badges.error.user_not_found": "Пользователь не найден",
|
"badges.error.user_not_found": "Пользователь не найден",
|
||||||
"badges.error.invalid_type_id": "Не указан тип значков",
|
"badges.error.invalid_type_id": "Не указан тип достижений",
|
||||||
"badges.error.no_permission_subscription": "Недостаточно прав для управления подписками",
|
"badges.error.no_permission_subscription": "Недостаточно прав для управления подписками",
|
||||||
"badges.error.cannot_create_subscription": "Не удалось создать подписку",
|
"badges.error.cannot_create_subscription": "Не удалось создать подписку",
|
||||||
"badges.error.cannot_delete_subscription": "Не удалось удалить подписку",
|
"badges.error.cannot_delete_subscription": "Не удалось удалить подписку",
|
||||||
|
"badges.error.ownership_not_found": "Выдача не найдена",
|
||||||
|
"badges.error.no_permission_revoke": "Недостаточно прав для снятия этого достижения",
|
||||||
|
"badges.error.cannot_revoke": "Не удалось снять достижение",
|
||||||
|
"badges.error.already_owned": "Это достижение уже выдано этому пользователю",
|
||||||
|
|
||||||
"badges.error.unknown": "Произошла ошибка",
|
"badges.error.unknown": "Произошла ошибка",
|
||||||
"badges.error.cannot_get_user": "Не удалось получить данные пользователя",
|
"badges.error.cannot_get_user": "Не удалось получить данные пользователя",
|
||||||
"badges.error.cannot_get_types": "Не удалось загрузить типы",
|
"badges.error.cannot_get_types": "Не удалось загрузить типы",
|
||||||
"badges.error.cannot_get_badges": "Не удалось загрузить значки",
|
"badges.error.cannot_get_badges": "Не удалось загрузить достижения",
|
||||||
"badges.error.invalid_request": "Неверный формат запроса",
|
"badges.error.invalid_request": "Неверный формат запроса",
|
||||||
"badges.error.invalid_name": "Необходимо указать название",
|
"badges.error.invalid_name": "Необходимо указать название",
|
||||||
"badges.error.invalid_image": "Необходимо указать эмодзи",
|
"badges.error.invalid_image": "Необходимо указать эмодзи",
|
||||||
"badges.error.type_not_found": "Тип значка не найден",
|
"badges.error.type_not_found": "Тип достижения не найден",
|
||||||
"badges.error.badge_not_found": "Значок не найден",
|
"badges.error.badge_not_found": "Достижение не найдено",
|
||||||
"badges.error.no_permission": "Недостаточно прав для выполнения действия",
|
"badges.error.no_permission": "Недостаточно прав для выполнения действия",
|
||||||
"badges.error.missing_badge_id": "Не указан ID значка",
|
"badges.error.missing_badge_id": "Не указан ID достижения",
|
||||||
"badges.error.missing_type_id": "Не указан ID типа",
|
"badges.error.missing_type_id": "Не указан ID типа",
|
||||||
"badges.error.cannot_create_badge": "Не удалось создать значок",
|
"badges.error.cannot_create_badge": "Не удалось создать достижение",
|
||||||
"badges.error.cannot_create_type": "Не удалось создать тип",
|
"badges.error.cannot_create_type": "Не удалось создать тип",
|
||||||
"badges.error.cannot_update_badge": "Не удалось обновить значок",
|
"badges.error.cannot_update_badge": "Не удалось обновить достижение",
|
||||||
"badges.error.cannot_delete_badge": "Не удалось удалить значок",
|
"badges.error.cannot_delete_badge": "Не удалось удалить достижение",
|
||||||
"badges.error.cannot_update_type": "Не удалось обновить тип",
|
"badges.error.cannot_update_type": "Не удалось обновить тип",
|
||||||
"badges.error.cannot_delete_type": "Не удалось удалить тип",
|
"badges.error.cannot_delete_type": "Не удалось удалить тип",
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {Client4} from 'mattermost-redux/client';
|
|||||||
import {ClientError} from 'mattermost-redux/client/client4';
|
import {ClientError} from 'mattermost-redux/client/client4';
|
||||||
|
|
||||||
import manifest from 'manifest';
|
import manifest from 'manifest';
|
||||||
import {AllBadgesBadge, Badge, BadgeDetails, BadgeID, BadgeTypeDefinition, CreateBadgeRequest, CreateTypeRequest, GetTypesResponse, GrantBadgeRequest, SubscriptionRequest, UpdateBadgeRequest, UpdateTypeRequest, UserBadge} from 'types/badges';
|
import {AllBadgesBadge, Badge, BadgeDetails, BadgeID, BadgeTypeDefinition, CreateBadgeRequest, CreateTypeRequest, GetTypesResponse, GrantBadgeRequest, RevokeOwnershipRequest, SubscriptionRequest, UpdateBadgeRequest, UpdateTypeRequest, UserBadge} from 'types/badges';
|
||||||
|
|
||||||
export default class Client {
|
export default class Client {
|
||||||
private url: string;
|
private url: string;
|
||||||
@ -86,6 +86,10 @@ export default class Client {
|
|||||||
await this.doPost(`${this.url}/deleteSubscription`, req);
|
await this.doPost(`${this.url}/deleteSubscription`, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async revokeOwnership(req: RevokeOwnershipRequest): Promise<void> {
|
||||||
|
await this.doPost(`${this.url}/revokeOwnership`, req);
|
||||||
|
}
|
||||||
|
|
||||||
async getChannelSubscriptions(channelID: string): Promise<BadgeTypeDefinition[]> {
|
async getChannelSubscriptions(channelID: string): Promise<BadgeTypeDefinition[]> {
|
||||||
try {
|
try {
|
||||||
const res = await this.doGet(`${this.url}/getChannelSubscriptions/${channelID}`);
|
const res = await this.doGet(`${this.url}/getChannelSubscriptions/${channelID}`);
|
||||||
|
|||||||
@ -39,7 +39,7 @@ const BadgesAdminSetting: React.FC<Props> = ({id, value, disabled, onChange, set
|
|||||||
<div className='help-text'>
|
<div className='help-text'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.admin.help_text'
|
id='badges.admin.help_text'
|
||||||
defaultMessage='Эти пользователи будут считаться администраторами плагина достижений. Они могут создавать типы, а также изменять и выдавать любые значки.'
|
defaultMessage='Эти пользователи будут считаться администраторами плагина достижений. Они могут создавать типы, а также изменять и выдавать любые достижения.'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import InlineTypeForm from './inline_type_form';
|
|||||||
import TypeSelect from './type_select';
|
import TypeSelect from './type_select';
|
||||||
|
|
||||||
import './badge_modal.scss';
|
import './badge_modal.scss';
|
||||||
|
import ConfirmDialog from 'components/confirm_dialog/confirm_dialog';
|
||||||
|
|
||||||
const NEW_TYPE_VALUE = '__new__';
|
const NEW_TYPE_VALUE = '__new__';
|
||||||
|
|
||||||
@ -193,7 +194,7 @@ const BadgeModal: React.FC = () => {
|
|||||||
typeID = String(createdType.id);
|
typeID = String(createdType.id);
|
||||||
}
|
}
|
||||||
if (!typeID) {
|
if (!typeID) {
|
||||||
setError(intl.formatMessage({id: 'badges.modal.error_type_required', defaultMessage: 'Выберите тип значка'}));
|
setError(intl.formatMessage({id: 'badges.modal.error_type_required', defaultMessage: 'Выберите тип достижения'}));
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -252,8 +253,8 @@ const BadgeModal: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const title = isEditMode
|
const title = isEditMode
|
||||||
? intl.formatMessage({id: 'badges.modal.edit_badge_title', defaultMessage: 'Редактировать значок'})
|
? intl.formatMessage({id: 'badges.modal.edit_badge_title', defaultMessage: 'Редактировать достижение'})
|
||||||
: intl.formatMessage({id: 'badges.modal.create_badge_title', defaultMessage: 'Создать значок'});
|
: intl.formatMessage({id: 'badges.modal.create_badge_title', defaultMessage: 'Создать достижение'});
|
||||||
const submitLabel = isEditMode
|
const submitLabel = isEditMode
|
||||||
? intl.formatMessage({id: 'badges.modal.btn_save', defaultMessage: 'Сохранить'})
|
? intl.formatMessage({id: 'badges.modal.btn_save', defaultMessage: 'Сохранить'})
|
||||||
: intl.formatMessage({id: 'badges.modal.btn_create', defaultMessage: 'Создать'});
|
: intl.formatMessage({id: 'badges.modal.btn_create', defaultMessage: 'Создать'});
|
||||||
@ -294,7 +295,7 @@ const BadgeModal: React.FC = () => {
|
|||||||
value={form.name}
|
value={form.name}
|
||||||
onChange={(e) => updateForm({name: e.target.value})}
|
onChange={(e) => updateForm({name: e.target.value})}
|
||||||
maxLength={20}
|
maxLength={20}
|
||||||
placeholder={intl.formatMessage({id: 'badges.modal.field_name_placeholder', defaultMessage: 'Название значка (макс. 20 символов)'})}
|
placeholder={intl.formatMessage({id: 'badges.modal.field_name_placeholder', defaultMessage: 'Название достижения (макс. 20 символов)'})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='form-group'>
|
<div className='form-group'>
|
||||||
@ -308,7 +309,7 @@ const BadgeModal: React.FC = () => {
|
|||||||
value={form.description}
|
value={form.description}
|
||||||
onChange={(e) => updateForm({description: e.target.value})}
|
onChange={(e) => updateForm({description: e.target.value})}
|
||||||
maxLength={120}
|
maxLength={120}
|
||||||
placeholder={intl.formatMessage({id: 'badges.modal.field_description_placeholder', defaultMessage: 'Описание значка (макс. 120 символов)'})}
|
placeholder={intl.formatMessage({id: 'badges.modal.field_description_placeholder', defaultMessage: 'Описание достижения (макс. 120 символов)'})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='form-group'>
|
<div className='form-group'>
|
||||||
@ -398,35 +399,6 @@ const BadgeModal: React.FC = () => {
|
|||||||
{error && <div className='error-message'>{error}</div>}
|
{error && <div className='error-message'>{error}</div>}
|
||||||
{isEditMode && (
|
{isEditMode && (
|
||||||
<div className='delete-section'>
|
<div className='delete-section'>
|
||||||
{confirmDelete ? (
|
|
||||||
<div className='confirm-delete'>
|
|
||||||
<span>
|
|
||||||
<FormattedMessage
|
|
||||||
id='badges.modal.confirm_delete'
|
|
||||||
defaultMessage='Вы уверены?'
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
className='btn btn--danger'
|
|
||||||
onClick={handleDelete}
|
|
||||||
disabled={loading}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='badges.modal.btn_confirm_delete'
|
|
||||||
defaultMessage='Да, удалить'
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className='btn btn--cancel'
|
|
||||||
onClick={() => setConfirmDelete(false)}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='badges.modal.btn_cancel'
|
|
||||||
defaultMessage='Отмена'
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<button
|
<button
|
||||||
className='btn btn--danger'
|
className='btn btn--danger'
|
||||||
onClick={handleDelete}
|
onClick={handleDelete}
|
||||||
@ -434,9 +406,20 @@ const BadgeModal: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.modal.btn_delete'
|
id='badges.modal.btn_delete'
|
||||||
defaultMessage='Удалить значок'
|
defaultMessage='Удалить достижение'
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
{confirmDelete && (
|
||||||
|
<ConfirmDialog
|
||||||
|
onConfirm={handleDelete}
|
||||||
|
onCancel={() => setConfirmDelete(false)}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='badges.modal.confirm_delete_badge'
|
||||||
|
defaultMessage='Удалить достижение «{name}»?'
|
||||||
|
values={{name: form.name || editData?.name}}
|
||||||
|
/>
|
||||||
|
</ConfirmDialog>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -41,7 +41,7 @@ const InlineTypeForm: React.FC<Props> = ({form, onChange}) => {
|
|||||||
<label htmlFor='newTypeEveryoneCanCreate'>
|
<label htmlFor='newTypeEveryoneCanCreate'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.modal.new_type_everyone_create'
|
id='badges.modal.new_type_everyone_create'
|
||||||
defaultMessage='Все могут создавать значки'
|
defaultMessage='Все могут создавать достижения'
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -69,7 +69,7 @@ const InlineTypeForm: React.FC<Props> = ({form, onChange}) => {
|
|||||||
<label htmlFor='newTypeEveryoneCanGrant'>
|
<label htmlFor='newTypeEveryoneCanGrant'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.modal.new_type_everyone_grant'
|
id='badges.modal.new_type_everyone_grant'
|
||||||
defaultMessage='Все могут выдавать значки'
|
defaultMessage='Все могут выдавать достижения'
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -36,7 +36,7 @@ const TypeSelect: React.FC<Props> = ({
|
|||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const selectedTypeName = types.find((t) => String(t.id) === badgeType)?.name ||
|
const selectedTypeName = types.find((t) => String(t.id) === badgeType)?.name ||
|
||||||
intl.formatMessage({id: 'badges.modal.field_type_placeholder', defaultMessage: 'Выберите тип значка'});
|
intl.formatMessage({id: 'badges.modal.field_type_placeholder', defaultMessage: 'Выберите тип достижения'});
|
||||||
const triggerLabel = showCreateType ? intl.formatMessage({id: 'badges.modal.create_new_type', defaultMessage: '+ Создать новый тип'}) : selectedTypeName;
|
const triggerLabel = showCreateType ? intl.formatMessage({id: 'badges.modal.create_new_type', defaultMessage: '+ Создать новый тип'}) : selectedTypeName;
|
||||||
const confirmType = confirmDeleteTypeId ? types.find((t) => String(t.id) === confirmDeleteTypeId) : null;
|
const confirmType = confirmDeleteTypeId ? types.find((t) => String(t.id) === confirmDeleteTypeId) : null;
|
||||||
|
|
||||||
|
|||||||
@ -30,5 +30,31 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
|
.btn--cancel {
|
||||||
|
background: var(--center-channel-bg, #fff);
|
||||||
|
color: var(--center-channel-color, #3d3c40);
|
||||||
|
border: 1px solid rgba(var(--center-channel-color-rgb, 61, 60, 64), 0.16);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(var(--center-channel-color-rgb, 61, 60, 64), 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn--danger {
|
||||||
|
background: var(--error-text, #d24b4e);
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: color-mix(in srgb, var(--error-text, #d24b4e) 85%, #000);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,10 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ConfirmDialog: React.FC<Props> = ({children, onConfirm, onCancel}) => (
|
const ConfirmDialog: React.FC<Props> = ({children, onConfirm, onCancel}) => (
|
||||||
<div className='ConfirmDialog__overlay'>
|
<div
|
||||||
|
className='ConfirmDialog__overlay'
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
<div className='ConfirmDialog'>
|
<div className='ConfirmDialog'>
|
||||||
<p className='ConfirmDialog__text'>
|
<p className='ConfirmDialog__text'>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ const GrantModal: React.FC = () => {
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [closing, setClosing] = useState(false);
|
const [closing, setClosing] = useState(false);
|
||||||
|
|
||||||
// Выбор значка
|
// Выбор достижения
|
||||||
const [badgeDropdownOpen, setBadgeDropdownOpen] = useState(false);
|
const [badgeDropdownOpen, setBadgeDropdownOpen] = useState(false);
|
||||||
const badgeDropdownRef = useRef<HTMLDivElement>(null);
|
const badgeDropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ const GrantModal: React.FC = () => {
|
|||||||
};
|
};
|
||||||
fetchBadges();
|
fetchBadges();
|
||||||
|
|
||||||
// Prefill значка, если передан
|
// Prefill достижения, если передан
|
||||||
if (modalData?.prefillBadgeId) {
|
if (modalData?.prefillBadgeId) {
|
||||||
setForm((prev) => ({...prev, badgeId: modalData.prefillBadgeId || ''}));
|
setForm((prev) => ({...prev, badgeId: modalData.prefillBadgeId || ''}));
|
||||||
}
|
}
|
||||||
@ -153,7 +153,7 @@ const GrantModal: React.FC = () => {
|
|||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.grant.title'
|
id='badges.grant.title'
|
||||||
defaultMessage='Выдать значок'
|
defaultMessage='Выдать достижение'
|
||||||
/>
|
/>
|
||||||
</h4>
|
</h4>
|
||||||
<button
|
<button
|
||||||
@ -168,7 +168,7 @@ const GrantModal: React.FC = () => {
|
|||||||
<p className='grant-intro'>
|
<p className='grant-intro'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.grant.intro'
|
id='badges.grant.intro'
|
||||||
defaultMessage='Выдать значок пользователю @{username}'
|
defaultMessage='Выдать достижение пользователю @{username}'
|
||||||
values={{username: modalData?.prefillUser || ''}}
|
values={{username: modalData?.prefillUser || ''}}
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
@ -177,7 +177,7 @@ const GrantModal: React.FC = () => {
|
|||||||
<label>
|
<label>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.grant.field_badge'
|
id='badges.grant.field_badge'
|
||||||
defaultMessage='Значок'
|
defaultMessage='Достижение'
|
||||||
/>
|
/>
|
||||||
<span className='required'>{'*'}</span>
|
<span className='required'>{'*'}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -199,7 +199,7 @@ const GrantModal: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
{' '}{selectedBadge.name}
|
{' '}{selectedBadge.name}
|
||||||
</>
|
</>
|
||||||
) : intl.formatMessage({id: 'badges.grant.field_badge_placeholder', defaultMessage: 'Выберите значок'})}
|
) : intl.formatMessage({id: 'badges.grant.field_badge_placeholder', defaultMessage: 'Выберите достижение'})}
|
||||||
</span>
|
</span>
|
||||||
<span className='type-select__arrow'>{'▾'}</span>
|
<span className='type-select__arrow'>{'▾'}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -209,7 +209,7 @@ const GrantModal: React.FC = () => {
|
|||||||
<div className='type-select__option'>
|
<div className='type-select__option'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.grant.no_badges'
|
id='badges.grant.no_badges'
|
||||||
defaultMessage='Нет доступных значков'
|
defaultMessage='Нет доступных достижений'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -244,7 +244,7 @@ const GrantModal: React.FC = () => {
|
|||||||
value={form.reason}
|
value={form.reason}
|
||||||
onChange={(e) => updateForm({reason: e.target.value})}
|
onChange={(e) => updateForm({reason: e.target.value})}
|
||||||
maxLength={200}
|
maxLength={200}
|
||||||
placeholder={intl.formatMessage({id: 'badges.grant.field_reason_placeholder', defaultMessage: 'За что выдаётся значок? (необязательно)'})}
|
placeholder={intl.formatMessage({id: 'badges.grant.field_reason_placeholder', defaultMessage: 'За что выдаётся достижение? (необязательно)'})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='checkbox-group'>
|
<div className='checkbox-group'>
|
||||||
|
|||||||
@ -112,13 +112,13 @@ const AllBadges: React.FC<Props> = ({filterTypeId, filterTypeName, actions}) =>
|
|||||||
<div className='AllBadges__emptyTitle'>
|
<div className='AllBadges__emptyTitle'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.empty.title'
|
id='badges.empty.title'
|
||||||
defaultMessage='Значков пока нет'
|
defaultMessage='Достижений пока нет'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='AllBadges__emptyDescription'>
|
<div className='AllBadges__emptyDescription'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.empty.description'
|
id='badges.empty.description'
|
||||||
defaultMessage='Создайте первый значок, чтобы отмечать достижения и заслуги участников команды.'
|
defaultMessage='Создайте первое достижение, чтобы отмечать заслуги участников команды.'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -127,7 +127,7 @@ const AllBadges: React.FC<Props> = ({filterTypeId, filterTypeName, actions}) =>
|
|||||||
<div className='AllBadges__empty'>
|
<div className='AllBadges__empty'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.types.no_badges'
|
id='badges.types.no_badges'
|
||||||
defaultMessage='В этом типе нет значков'
|
defaultMessage='В этом типе нет достижений'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import React, {useState} from 'react';
|
|||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
import {BadgeTypeDefinition} from '../../types/badges';
|
import {BadgeTypeDefinition} from '../../types/badges';
|
||||||
|
import ConfirmDialog from '../confirm_dialog/confirm_dialog';
|
||||||
|
|
||||||
import './all_types_row.scss';
|
import './all_types_row.scss';
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ const AllTypesRow: React.FC<Props> = ({badgeType, onEdit, onDelete, onClick}: Pr
|
|||||||
<div className='AllTypesRow__meta'>
|
<div className='AllTypesRow__meta'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.types.badge_count'
|
id='badges.types.badge_count'
|
||||||
defaultMessage='{count, plural, one {# значок} few {# значка} many {# значков} other {# значков}}'
|
defaultMessage='{count, plural, one {# достижение} few {# достижения} many {# достижений} other {# достижений}}'
|
||||||
values={{count: badgeType.badge_count}}
|
values={{count: badgeType.badge_count}}
|
||||||
/>
|
/>
|
||||||
{badgeType.can_create?.everyone && (
|
{badgeType.can_create?.everyone && (
|
||||||
@ -71,7 +72,6 @@ const AllTypesRow: React.FC<Props> = ({badgeType, onEdit, onDelete, onClick}: Pr
|
|||||||
className='AllTypesRow__actions'
|
className='AllTypesRow__actions'
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
{!confirmDelete && (
|
|
||||||
<button
|
<button
|
||||||
className='AllTypesRow__btn AllTypesRow__btn--edit'
|
className='AllTypesRow__btn AllTypesRow__btn--edit'
|
||||||
onClick={() => onEdit(badgeType)}
|
onClick={() => onEdit(badgeType)}
|
||||||
@ -81,37 +81,7 @@ const AllTypesRow: React.FC<Props> = ({badgeType, onEdit, onDelete, onClick}: Pr
|
|||||||
defaultMessage='Редактировать'
|
defaultMessage='Редактировать'
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
)}
|
|
||||||
{!badgeType.is_default && (
|
{!badgeType.is_default && (
|
||||||
<>
|
|
||||||
{confirmDelete ? (
|
|
||||||
<div className='AllTypesRow__confirmDelete'>
|
|
||||||
<span className='AllTypesRow__confirmText'>
|
|
||||||
<FormattedMessage
|
|
||||||
id='badges.modal.confirm_delete'
|
|
||||||
defaultMessage='Вы уверены?'
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
className='AllTypesRow__btn AllTypesRow__btn--danger'
|
|
||||||
onClick={handleDelete}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='badges.modal.btn_confirm_delete_type'
|
|
||||||
defaultMessage='Да, удалить'
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className='AllTypesRow__btn AllTypesRow__btn--cancel'
|
|
||||||
onClick={() => setConfirmDelete(false)}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='badges.modal.btn_cancel'
|
|
||||||
defaultMessage='Отмена'
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<button
|
<button
|
||||||
className='AllTypesRow__btn AllTypesRow__btn--danger'
|
className='AllTypesRow__btn AllTypesRow__btn--danger'
|
||||||
onClick={handleDelete}
|
onClick={handleDelete}
|
||||||
@ -122,7 +92,17 @@ const AllTypesRow: React.FC<Props> = ({badgeType, onEdit, onDelete, onClick}: Pr
|
|||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</>
|
{confirmDelete && (
|
||||||
|
<ConfirmDialog
|
||||||
|
onConfirm={() => onDelete(badgeType)}
|
||||||
|
onCancel={() => setConfirmDelete(false)}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='badges.modal.confirm_delete_type'
|
||||||
|
defaultMessage='Удалить тип «{name}»?'
|
||||||
|
values={{name: badgeType.name}}
|
||||||
|
/>
|
||||||
|
</ConfirmDialog>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -93,7 +93,7 @@ class BadgeDetailsComponent extends React.PureComponent<Props, State> {
|
|||||||
return (<div>
|
return (<div>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.badge_not_found'
|
id='badges.badge_not_found'
|
||||||
defaultMessage='Значок не найден.'
|
defaultMessage='Достижение не найдено.'
|
||||||
/>
|
/>
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
@ -108,7 +108,7 @@ class BadgeDetailsComponent extends React.PureComponent<Props, State> {
|
|||||||
return (<div>
|
return (<div>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.badge_not_found'
|
id='badges.badge_not_found'
|
||||||
defaultMessage='Значок не найден.'
|
defaultMessage='Достижение не найдено.'
|
||||||
/>
|
/>
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -76,7 +76,7 @@ const RHS: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.rhs.all_badges'
|
id='badges.rhs.all_badges'
|
||||||
defaultMessage='Все значки'
|
defaultMessage='Все достижения'
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
{canEditType && (
|
{canEditType && (
|
||||||
@ -98,7 +98,7 @@ const RHS: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.rhs.create_badge'
|
id='badges.rhs.create_badge'
|
||||||
defaultMessage='+ Создать значок'
|
defaultMessage='+ Создать достижение'
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
@ -163,6 +163,7 @@ const RHS: React.FC = () => {
|
|||||||
<UserBadges
|
<UserBadges
|
||||||
user={currentUser}
|
user={currentUser}
|
||||||
isCurrentUser={false}
|
isCurrentUser={false}
|
||||||
|
currentUserID={myUser.id}
|
||||||
actions={{
|
actions={{
|
||||||
setRHSView: (view: RHSState) => dispatch(setRHSView(view)),
|
setRHSView: (view: RHSState) => dispatch(setRHSView(view)),
|
||||||
setRHSBadge: (badge: BadgeID | null) => dispatch(setRHSBadge(badge)),
|
setRHSBadge: (badge: BadgeID | null) => dispatch(setRHSBadge(badge)),
|
||||||
@ -176,6 +177,7 @@ const RHS: React.FC = () => {
|
|||||||
<UserBadges
|
<UserBadges
|
||||||
user={myUser}
|
user={myUser}
|
||||||
isCurrentUser={true}
|
isCurrentUser={true}
|
||||||
|
currentUserID={myUser.id}
|
||||||
actions={{
|
actions={{
|
||||||
setRHSView: (view: RHSState) => dispatch(setRHSView(view)),
|
setRHSView: (view: RHSState) => dispatch(setRHSView(view)),
|
||||||
setRHSBadge: (badge: BadgeID | null) => dispatch(setRHSBadge(badge)),
|
setRHSBadge: (badge: BadgeID | null) => dispatch(setRHSBadge(badge)),
|
||||||
|
|||||||
@ -73,4 +73,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-badge-revoke {
|
||||||
|
margin-top: 4px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--error-text, #d24b4e);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--confirm {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--error-text, #d24b4e);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__yes {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__no {
|
||||||
|
color: rgba(var(--center-channel-color-rgb), 0.56) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, {useState} from 'react';
|
||||||
|
|
||||||
import {FormattedMessage, useIntl} from 'react-intl';
|
import {FormattedMessage, useIntl} from 'react-intl';
|
||||||
|
|
||||||
@ -7,18 +7,42 @@ import Client4 from 'mattermost-redux/client/client4';
|
|||||||
import {UserBadge} from '../../types/badges';
|
import {UserBadge} from '../../types/badges';
|
||||||
import BadgeImage from '../utils/badge_image';
|
import BadgeImage from '../utils/badge_image';
|
||||||
import {markdown} from 'utils/markdown';
|
import {markdown} from 'utils/markdown';
|
||||||
|
import Client from '../../client/api';
|
||||||
|
import ConfirmDialog from '../confirm_dialog/confirm_dialog';
|
||||||
|
|
||||||
import './user_badge_row.scss';
|
import './user_badge_row.scss';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
badge: UserBadge;
|
badge: UserBadge;
|
||||||
isCurrentUser: boolean;
|
isCurrentUser: boolean;
|
||||||
|
currentUserID: string;
|
||||||
onClick: (badge: UserBadge) => void;
|
onClick: (badge: UserBadge) => void;
|
||||||
|
onRevoke?: (badge: UserBadge) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserBadgeRow: React.FC<Props> = ({badge, onClick, isCurrentUser}: Props) => {
|
const UserBadgeRow: React.FC<Props> = ({badge, onClick, isCurrentUser, currentUserID, onRevoke}: Props) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const time = new Date(badge.time);
|
const time = new Date(badge.time);
|
||||||
|
const [confirmingRevoke, setConfirmingRevoke] = useState(false);
|
||||||
|
|
||||||
|
const canRevoke = badge.granted_by === currentUserID;
|
||||||
|
|
||||||
|
const handleRevoke = async () => {
|
||||||
|
try {
|
||||||
|
const client = new Client();
|
||||||
|
await client.revokeOwnership({
|
||||||
|
badge_id: String(badge.id),
|
||||||
|
user_id: badge.user,
|
||||||
|
time: String(badge.time),
|
||||||
|
});
|
||||||
|
onRevoke?.(badge);
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
} finally {
|
||||||
|
setConfirmingRevoke(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let reason = null;
|
let reason = null;
|
||||||
if (badge.reason) {
|
if (badge.reason) {
|
||||||
reason = (
|
reason = (
|
||||||
@ -50,6 +74,42 @@ const UserBadgeRow: React.FC<Props> = ({badge, onClick, isCurrentUser}: Props) =
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let revokeAction = null;
|
||||||
|
if (canRevoke && onRevoke) {
|
||||||
|
revokeAction = (
|
||||||
|
<div className='user-badge-revoke'>
|
||||||
|
<a
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setConfirmingRevoke(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='badges.revoke.btn'
|
||||||
|
defaultMessage='Снять достижение'
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
if (confirmingRevoke) {
|
||||||
|
revokeAction = (
|
||||||
|
<>
|
||||||
|
{revokeAction}
|
||||||
|
<ConfirmDialog
|
||||||
|
onConfirm={handleRevoke}
|
||||||
|
onCancel={() => setConfirmingRevoke(false)}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='badges.revoke.confirm'
|
||||||
|
defaultMessage='Снять достижение?'
|
||||||
|
/>
|
||||||
|
</ConfirmDialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='UserBadgesRow'
|
className='UserBadgesRow'
|
||||||
@ -105,6 +165,7 @@ const UserBadgeRow: React.FC<Props> = ({badge, onClick, isCurrentUser}: Props) =
|
|||||||
</div>
|
</div>
|
||||||
{reason}
|
{reason}
|
||||||
{setStatus}
|
{setStatus}
|
||||||
|
{revokeAction}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import './user_badges.scss';
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isCurrentUser: boolean;
|
isCurrentUser: boolean;
|
||||||
|
currentUserID: string;
|
||||||
user: UserProfile | null;
|
user: UserProfile | null;
|
||||||
actions: {
|
actions: {
|
||||||
setRHSView: (view: RHSState) => void;
|
setRHSView: (view: RHSState) => void;
|
||||||
@ -84,6 +85,17 @@ class UserBadges extends React.PureComponent<Props, State> {
|
|||||||
this.props.actions.setRHSView(RHS_STATE_DETAIL);
|
this.props.actions.setRHSView(RHS_STATE_DETAIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onRevoke = () => {
|
||||||
|
if (!this.props.user) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const c = new Client();
|
||||||
|
this.setState({loading: true});
|
||||||
|
c.getUserBadges(this.props.user.id).then((badges) => {
|
||||||
|
this.setState({badges, loading: false});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.props.user) {
|
if (!this.props.user) {
|
||||||
return (<div>
|
return (<div>
|
||||||
@ -104,7 +116,7 @@ class UserBadges extends React.PureComponent<Props, State> {
|
|||||||
return (<div>
|
return (<div>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.no_badges_yet'
|
id='badges.no_badges_yet'
|
||||||
defaultMessage='Значков пока нет.'
|
defaultMessage='Достижений пока нет.'
|
||||||
/>
|
/>
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
@ -113,9 +125,11 @@ class UserBadges extends React.PureComponent<Props, State> {
|
|||||||
return (
|
return (
|
||||||
<UserBadgeRow
|
<UserBadgeRow
|
||||||
isCurrentUser={this.props.isCurrentUser}
|
isCurrentUser={this.props.isCurrentUser}
|
||||||
|
currentUserID={this.props.currentUserID}
|
||||||
key={badge.time}
|
key={badge.time}
|
||||||
badge={badge}
|
badge={badge}
|
||||||
onClick={this.onBadgeClick}
|
onClick={this.onBadgeClick}
|
||||||
|
onRevoke={this.onRevoke}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -123,12 +137,12 @@ class UserBadges extends React.PureComponent<Props, State> {
|
|||||||
const title = this.props.isCurrentUser ? (
|
const title = this.props.isCurrentUser ? (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.rhs.my_badges'
|
id='badges.rhs.my_badges'
|
||||||
defaultMessage='Мои значки'
|
defaultMessage='Мои достижения'
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.rhs.user_badges'
|
id='badges.rhs.user_badges'
|
||||||
defaultMessage='Значки @{username}'
|
defaultMessage='Достижения @{username}'
|
||||||
values={{username: this.props.user.username}}
|
values={{username: this.props.user.username}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -128,7 +128,7 @@ const SubscriptionModal: React.FC = () => {
|
|||||||
<label>
|
<label>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.subscription.field_type'
|
id='badges.subscription.field_type'
|
||||||
defaultMessage='Тип значков'
|
defaultMessage='Тип достижений'
|
||||||
/>
|
/>
|
||||||
<span className='required'>{'*'}</span>
|
<span className='required'>{'*'}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -144,7 +144,7 @@ const SubscriptionModal: React.FC = () => {
|
|||||||
<span className='type-select__value'>
|
<span className='type-select__value'>
|
||||||
{selectedType
|
{selectedType
|
||||||
? selectedType.name
|
? selectedType.name
|
||||||
: intl.formatMessage({id: 'badges.subscription.field_type_placeholder', defaultMessage: 'Выберите тип значков'})
|
: intl.formatMessage({id: 'badges.subscription.field_type_placeholder', defaultMessage: 'Выберите тип достижений'})
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
<span className='type-select__arrow'>{'▾'}</span>
|
<span className='type-select__arrow'>{'▾'}</span>
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import Client from 'client/api';
|
|||||||
import {getServerErrorId} from 'utils/helpers';
|
import {getServerErrorId} from 'utils/helpers';
|
||||||
import CloseIcon from 'components/icons/close_icon';
|
import CloseIcon from 'components/icons/close_icon';
|
||||||
import UserMultiSelect from 'components/user_multi_select';
|
import UserMultiSelect from 'components/user_multi_select';
|
||||||
|
import ConfirmDialog from 'components/confirm_dialog/confirm_dialog';
|
||||||
|
|
||||||
const emptyTypeForm: TypeFormData = {
|
const emptyTypeForm: TypeFormData = {
|
||||||
name: '',
|
name: '',
|
||||||
@ -173,7 +174,7 @@ const TypeModal: React.FC = () => {
|
|||||||
<label htmlFor='typeEveryoneCanCreate'>
|
<label htmlFor='typeEveryoneCanCreate'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.modal.new_type_everyone_create'
|
id='badges.modal.new_type_everyone_create'
|
||||||
defaultMessage='Все могут создавать значки'
|
defaultMessage='Все могут создавать достижения'
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -192,7 +193,7 @@ const TypeModal: React.FC = () => {
|
|||||||
<span className='form-group__help'>
|
<span className='form-group__help'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.modal.allowlist_create_help'
|
id='badges.modal.allowlist_create_help'
|
||||||
defaultMessage='Пользователи, которые могут создавать значки этого типа.'
|
defaultMessage='Пользователи, которые могут создавать достижения этого типа.'
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -207,7 +208,7 @@ const TypeModal: React.FC = () => {
|
|||||||
<label htmlFor='typeEveryoneCanGrant'>
|
<label htmlFor='typeEveryoneCanGrant'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.modal.new_type_everyone_grant'
|
id='badges.modal.new_type_everyone_grant'
|
||||||
defaultMessage='Все могут выдавать значки'
|
defaultMessage='Все могут выдавать достижения'
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -226,7 +227,7 @@ const TypeModal: React.FC = () => {
|
|||||||
<span className='form-group__help'>
|
<span className='form-group__help'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.modal.allowlist_grant_help'
|
id='badges.modal.allowlist_grant_help'
|
||||||
defaultMessage='Пользователи, которые могут выдавать значки этого типа.'
|
defaultMessage='Пользователи, которые могут выдавать достижения этого типа.'
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -234,36 +235,6 @@ const TypeModal: React.FC = () => {
|
|||||||
{error && <div className='error-message'>{error}</div>}
|
{error && <div className='error-message'>{error}</div>}
|
||||||
{isEditMode && !editData?.is_default && (
|
{isEditMode && !editData?.is_default && (
|
||||||
<div className='delete-section'>
|
<div className='delete-section'>
|
||||||
{confirmDelete ? (
|
|
||||||
<div className='confirm-delete'>
|
|
||||||
<span>
|
|
||||||
<FormattedMessage
|
|
||||||
id='badges.types.confirm_delete'
|
|
||||||
defaultMessage='Удалить тип «{name}» и все его значки?'
|
|
||||||
values={{name: editData?.name}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
className='btn btn--danger'
|
|
||||||
onClick={handleDelete}
|
|
||||||
disabled={loading}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='badges.modal.btn_confirm_delete'
|
|
||||||
defaultMessage='Да, удалить'
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className='btn btn--cancel'
|
|
||||||
onClick={() => setConfirmDelete(false)}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='badges.modal.btn_cancel'
|
|
||||||
defaultMessage='Отмена'
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<button
|
<button
|
||||||
className='btn btn--danger'
|
className='btn btn--danger'
|
||||||
onClick={handleDelete}
|
onClick={handleDelete}
|
||||||
@ -274,6 +245,17 @@ const TypeModal: React.FC = () => {
|
|||||||
defaultMessage='Удалить тип'
|
defaultMessage='Удалить тип'
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
{confirmDelete && (
|
||||||
|
<ConfirmDialog
|
||||||
|
onConfirm={handleDelete}
|
||||||
|
onCancel={() => setConfirmDelete(false)}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='badges.types.confirm_delete'
|
||||||
|
defaultMessage='Удалить тип «{name}» и все его достижения?'
|
||||||
|
values={{name: editData?.name}}
|
||||||
|
/>
|
||||||
|
</ConfirmDialog>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -42,7 +42,7 @@ type State = {
|
|||||||
loaded?: boolean;
|
loaded?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_BADGES = 7;
|
const MAX_BADGES = 6;
|
||||||
const BADGE_SIZE = 24;
|
const BADGE_SIZE = 24;
|
||||||
|
|
||||||
class BadgeList extends React.PureComponent<Props, State> {
|
class BadgeList extends React.PureComponent<Props, State> {
|
||||||
@ -229,7 +229,7 @@ class BadgeList extends React.PureComponent<Props, State> {
|
|||||||
<div><b>
|
<div><b>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.popover.title'
|
id='badges.popover.title'
|
||||||
defaultMessage='Значки'
|
defaultMessage='Достижения'
|
||||||
/>
|
/>
|
||||||
</b></div>
|
</b></div>
|
||||||
<div id='contentContainer' >
|
<div id='contentContainer' >
|
||||||
@ -244,7 +244,7 @@ class BadgeList extends React.PureComponent<Props, State> {
|
|||||||
<span className={'fa fa-plus-circle'}/>
|
<span className={'fa fa-plus-circle'}/>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='badges.grant_badge'
|
id='badges.grant_badge'
|
||||||
defaultMessage='Выдать значок'
|
defaultMessage='Выдать достижение'
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<hr className='divider divider--expanded'/>
|
<hr className='divider divider--expanded'/>
|
||||||
|
|||||||
@ -128,3 +128,9 @@ export type SubscriptionRequest = {
|
|||||||
type_id: string;
|
type_id: string;
|
||||||
channel_id: string;
|
channel_id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RevokeOwnershipRequest = {
|
||||||
|
badge_id: string;
|
||||||
|
user_id: string;
|
||||||
|
time: string;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user