mirror of
https://github.com/discourse/discourse.git
synced 2025-02-06 23:42:59 +08:00
FEATURE: lets users favorite 2 badges to show on user-card (#13151)
This commit is contained in:
parent
c5174e6982
commit
1cd0424ccd
|
@ -31,4 +31,9 @@ export default Component.extend({
|
||||||
}
|
}
|
||||||
return sanitize(description);
|
return sanitize(description);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@discourseComputed("badge.id")
|
||||||
|
showFavorite(badgeId) {
|
||||||
|
return ![1, 2, 3, 4].includes(badgeId);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,14 +1,27 @@
|
||||||
import Controller, { inject as controller } from "@ember/controller";
|
import Controller, { inject as controller } from "@ember/controller";
|
||||||
import { alias, sort } from "@ember/object/computed";
|
import { action, computed } from "@ember/object";
|
||||||
|
import { alias, filterBy, sort } from "@ember/object/computed";
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
user: controller(),
|
user: controller(),
|
||||||
username: alias("user.model.username_lower"),
|
username: alias("user.model.username_lower"),
|
||||||
sortedBadges: sort("model", "badgeSortOrder"),
|
sortedBadges: sort("model", "badgeSortOrder"),
|
||||||
|
favoriteBadges: filterBy("model", "is_favorite", true),
|
||||||
|
canFavoriteMoreBadges: computed(
|
||||||
|
"favoriteBadges.length",
|
||||||
|
"model.meta.max_favorites",
|
||||||
|
function () {
|
||||||
|
return this.favoriteBadges.length < this.model.meta.max_favorites;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
this.badgeSortOrder = ["badge.badge_type.sort_order:desc", "badge.name"];
|
this.badgeSortOrder = ["badge.badge_type.sort_order:desc", "badge.name"];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
favorite(badge) {
|
||||||
|
return badge.favorite();
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,8 +4,11 @@ import { Promise } from "rsvp";
|
||||||
import Topic from "discourse/models/topic";
|
import Topic from "discourse/models/topic";
|
||||||
import User from "discourse/models/user";
|
import User from "discourse/models/user";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
|
const DEFAULT_USER_BADGES_META = { max_favorites: 2 };
|
||||||
|
|
||||||
const UserBadge = EmberObject.extend({
|
const UserBadge = EmberObject.extend({
|
||||||
@discourseComputed
|
@discourseComputed
|
||||||
postUrl: function () {
|
postUrl: function () {
|
||||||
|
@ -19,6 +22,15 @@ const UserBadge = EmberObject.extend({
|
||||||
type: "DELETE",
|
type: "DELETE",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
favorite() {
|
||||||
|
return ajax(`/user_badges/${this.id}/toggle_favorite`, { type: "PUT" })
|
||||||
|
.then((json) => {
|
||||||
|
this.set("is_favorite", json.user_badge.is_favorite);
|
||||||
|
return this;
|
||||||
|
})
|
||||||
|
.catch(popupAjaxError);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
UserBadge.reopenClass({
|
UserBadge.reopenClass({
|
||||||
|
@ -86,6 +98,7 @@ UserBadge.reopenClass({
|
||||||
userBadges.grant_count = json.user_badge_info.grant_count;
|
userBadges.grant_count = json.user_badge_info.grant_count;
|
||||||
userBadges.username = json.user_badge_info.username;
|
userBadges.username = json.user_badge_info.username;
|
||||||
}
|
}
|
||||||
|
userBadges.meta = json.meta || DEFAULT_USER_BADGES_META;
|
||||||
return userBadges;
|
return userBadges;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,7 +4,26 @@
|
||||||
{{#if badge.has_badge}}
|
{{#if badge.has_badge}}
|
||||||
<a href={{url}} class="check-display status-checked">{{d-icon "check"}}</a>
|
<a href={{url}} class="check-display status-checked">{{d-icon "check"}}</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="badge-contents">
|
|
||||||
|
{{#if canFavorite}}
|
||||||
|
{{#if isFavorite}}
|
||||||
|
{{d-button
|
||||||
|
icon="star"
|
||||||
|
class="favorite-btn"
|
||||||
|
action=onFavoriteClick
|
||||||
|
}}
|
||||||
|
{{else}}
|
||||||
|
{{d-button
|
||||||
|
icon="far-star"
|
||||||
|
class="favorite-btn"
|
||||||
|
action=onFavoriteClick
|
||||||
|
title=(if canFavoriteMoreBadges "badges.favorite_max_not_reached" "badges.favorite_max_reached")
|
||||||
|
disabled=(not canFavoriteMoreBadges)
|
||||||
|
}}
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div class="badge-contents" >
|
||||||
<div class="badge-icon {{badge.badgeTypeClassName}}">
|
<div class="badge-icon {{badge.badgeTypeClassName}}">
|
||||||
<a href={{url}}>{{icon-or-image badge}}</a>
|
<a href={{url}}>{{icon-or-image badge}}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
{{#d-section pageClass="user-badges" class="user-content user-badges-list"}}
|
{{#d-section pageClass="user-badges" class="user-content user-badges-list"}}
|
||||||
|
<p class="favorite-count">
|
||||||
|
{{i18n "badges.favorite_count" count=this.favoriteBadges.length max=model.meta.max_favorites}}
|
||||||
|
</p>
|
||||||
{{#each sortedBadges as |ub|}}
|
{{#each sortedBadges as |ub|}}
|
||||||
{{badge-card
|
{{badge-card
|
||||||
badge=ub.badge
|
badge=ub.badge
|
||||||
count=ub.count
|
count=ub.count
|
||||||
|
canFavorite=ub.can_favorite
|
||||||
|
isFavorite=ub.is_favorite
|
||||||
username=username
|
username=username
|
||||||
|
canFavoriteMoreBadges=canFavoriteMoreBadges
|
||||||
|
onFavoriteClick=(action "favorite" ub)
|
||||||
filterUser="true"}}
|
filterUser="true"}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/d-section}}
|
{{/d-section}}
|
||||||
|
|
|
@ -576,6 +576,9 @@ export function applyDefaultHandlers(pretender) {
|
||||||
response(200, fixturesByUrl["/user_badges"])
|
response(200, fixturesByUrl["/user_badges"])
|
||||||
);
|
);
|
||||||
pretender.delete("/user_badges/:badge_id", success);
|
pretender.delete("/user_badges/:badge_id", success);
|
||||||
|
pretender.put("/user_badges/:id/toggle_favorite", () =>
|
||||||
|
response(200, { user_badge: { is_favorite: true } })
|
||||||
|
);
|
||||||
|
|
||||||
pretender.post("/posts", function (request) {
|
pretender.post("/posts", function (request) {
|
||||||
const data = parsePostData(request.requestBody);
|
const data = parsePostData(request.requestBody);
|
||||||
|
|
|
@ -57,4 +57,12 @@ module("Unit | Model | user-badge", function () {
|
||||||
const userBadge = UserBadge.create({ id: 1 });
|
const userBadge = UserBadge.create({ id: 1 });
|
||||||
await userBadge.revoke();
|
await userBadge.revoke();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("favorite", async function (assert) {
|
||||||
|
const userBadge = UserBadge.create({ id: 1 });
|
||||||
|
assert.notOk(userBadge.is_favorite);
|
||||||
|
|
||||||
|
await userBadge.favorite();
|
||||||
|
assert.ok(userBadge.is_favorite);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -143,6 +143,12 @@
|
||||||
font-size: $font-up-2;
|
font-size: $font-up-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.favorite-btn {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.badge-contents {
|
.badge-contents {
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 128px;
|
min-height: 128px;
|
||||||
|
|
|
@ -60,6 +60,10 @@
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.user-badges-list .favorite-count {
|
||||||
|
flex: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.btn.right {
|
.btn.right {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class UserBadgesController < ApplicationController
|
class UserBadgesController < ApplicationController
|
||||||
|
MAX_FAVORITES = 2
|
||||||
|
MAX_BADGES = 96 # This was limited in PR#2360 to make it divisible by 8
|
||||||
|
|
||||||
before_action :ensure_badges_enabled
|
before_action :ensure_badges_enabled
|
||||||
|
|
||||||
def index
|
def index
|
||||||
params.permit [:granted_before, :offset, :username]
|
params.permit [:granted_before, :offset, :username]
|
||||||
|
|
||||||
badge = fetch_badge_from_params
|
badge = fetch_badge_from_params
|
||||||
user_badges = badge.user_badges.order('granted_at DESC, id DESC').limit(96)
|
user_badges = badge.user_badges.order('granted_at DESC, id DESC').limit(MAX_BADGES)
|
||||||
user_badges = user_badges.includes(:user, :granted_by, badge: :badge_type, post: :topic, user: :primary_group)
|
user_badges = user_badges.includes(:user, :granted_by, badge: :badge_type, post: :topic, user: :primary_group)
|
||||||
|
|
||||||
grant_count = nil
|
grant_count = nil
|
||||||
|
@ -37,15 +40,19 @@ class UserBadgesController < ApplicationController
|
||||||
user_badges = user.user_badges
|
user_badges = user.user_badges
|
||||||
|
|
||||||
if params[:grouped]
|
if params[:grouped]
|
||||||
user_badges = user_badges.group(:badge_id)
|
user_badges = user_badges.group(:badge_id).select_for_grouping
|
||||||
.select(UserBadge.attribute_names.map { |x| "MAX(#{x}) AS #{x}" }, 'COUNT(*) AS "count"')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
user_badges = user_badges.includes(badge: [:badge_grouping, :badge_type, :image_upload])
|
user_badges = user_badges.includes(badge: [:badge_grouping, :badge_type, :image_upload])
|
||||||
.includes(post: :topic)
|
.includes(post: :topic)
|
||||||
.includes(:granted_by)
|
.includes(:granted_by)
|
||||||
|
|
||||||
render_serialized(user_badges, DetailedUserBadgeSerializer, root: :user_badges)
|
render_serialized(
|
||||||
|
user_badges,
|
||||||
|
DetailedUserBadgeSerializer,
|
||||||
|
root: :user_badges,
|
||||||
|
meta: { max_favorites: MAX_FAVORITES },
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -91,6 +98,24 @@ class UserBadgesController < ApplicationController
|
||||||
render json: success_json
|
render json: success_json
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def toggle_favorite
|
||||||
|
params.require(:user_badge_id)
|
||||||
|
user_badge = UserBadge.find(params[:user_badge_id])
|
||||||
|
user_badges = user_badge.user.user_badges
|
||||||
|
|
||||||
|
unless can_favorite_badge?(user_badge)
|
||||||
|
return render json: failed_json, status: 403
|
||||||
|
end
|
||||||
|
|
||||||
|
if !user_badge.is_favorite && user_badges.where(is_favorite: true).count >= MAX_FAVORITES
|
||||||
|
return render json: failed_json, status: 400
|
||||||
|
end
|
||||||
|
|
||||||
|
user_badge.toggle!(:is_favorite)
|
||||||
|
UserBadge.update_featured_ranks!(user_badge.user_id)
|
||||||
|
render_serialized(user_badge, DetailedUserBadgeSerializer, root: :user_badge)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Get the badge from either the badge name or id specified in the params.
|
# Get the badge from either the badge name or id specified in the params.
|
||||||
|
@ -114,6 +139,10 @@ class UserBadgesController < ApplicationController
|
||||||
master_api_call || guardian.can_grant_badges?(user)
|
master_api_call || guardian.can_grant_badges?(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_favorite_badge?(user_badge)
|
||||||
|
current_user == user_badge.user && !(1..4).include?(user_badge.badge_id)
|
||||||
|
end
|
||||||
|
|
||||||
def ensure_badges_enabled
|
def ensure_badges_enabled
|
||||||
raise Discourse::NotFound unless SiteSetting.enable_badges?
|
raise Discourse::NotFound unless SiteSetting.enable_badges?
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,12 +9,24 @@ class UserBadge < ActiveRecord::Base
|
||||||
|
|
||||||
scope :grouped_with_count, -> {
|
scope :grouped_with_count, -> {
|
||||||
group(:badge_id, :user_id)
|
group(:badge_id, :user_id)
|
||||||
.select(UserBadge.attribute_names.map { |x| "MAX(user_badges.#{x}) AS #{x}" },
|
.select_for_grouping
|
||||||
'COUNT(*) AS "count"')
|
|
||||||
.order('MAX(featured_rank) ASC')
|
.order('MAX(featured_rank) ASC')
|
||||||
.includes(:user, :granted_by, { badge: :badge_type }, post: :topic)
|
.includes(:user, :granted_by, { badge: :badge_type }, post: :topic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scope :select_for_grouping, -> {
|
||||||
|
select(
|
||||||
|
UserBadge.attribute_names.map do |name|
|
||||||
|
if name == 'is_favorite'
|
||||||
|
"BOOL_OR(user_badges.#{name}) AS is_favorite"
|
||||||
|
else
|
||||||
|
"MAX(user_badges.#{name}) AS #{name}"
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
'COUNT(*) AS "count"'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
scope :for_enabled_badges, -> { where('user_badges.badge_id IN (SELECT id FROM badges WHERE enabled)') }
|
scope :for_enabled_badges, -> { where('user_badges.badge_id IN (SELECT id FROM badges WHERE enabled)') }
|
||||||
|
|
||||||
validates :badge_id,
|
validates :badge_id,
|
||||||
|
@ -62,6 +74,7 @@ class UserBadge < ActiveRecord::Base
|
||||||
PARTITION BY user_badges.user_id -- Do a separate rank for each user
|
PARTITION BY user_badges.user_id -- Do a separate rank for each user
|
||||||
ORDER BY BOOL_OR(badges.enabled) DESC, -- Disabled badges last
|
ORDER BY BOOL_OR(badges.enabled) DESC, -- Disabled badges last
|
||||||
MAX(featured_tl_badge.user_id) NULLS LAST, -- Best tl badge first
|
MAX(featured_tl_badge.user_id) NULLS LAST, -- Best tl badge first
|
||||||
|
BOOL_OR(user_badges.is_favorite) DESC NULLS LAST, -- Favorite badges next
|
||||||
CASE WHEN user_badges.badge_id IN (1,2,3,4) THEN 1 ELSE 0 END ASC, -- Non-featured tl badges last
|
CASE WHEN user_badges.badge_id IN (1,2,3,4) THEN 1 ELSE 0 END ASC, -- Non-featured tl badges last
|
||||||
MAX(badges.badge_type_id) ASC,
|
MAX(badges.badge_type_id) ASC,
|
||||||
MAX(badges.grant_count) ASC,
|
MAX(badges.grant_count) ASC,
|
||||||
|
@ -102,6 +115,7 @@ end
|
||||||
# seq :integer default(0), not null
|
# seq :integer default(0), not null
|
||||||
# featured_rank :integer
|
# featured_rank :integer
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
|
# is_favorite :boolean
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
@ -109,4 +123,5 @@ end
|
||||||
# index_user_badges_on_badge_id_and_user_id_and_post_id (badge_id,user_id,post_id) UNIQUE WHERE (post_id IS NOT NULL)
|
# index_user_badges_on_badge_id_and_user_id_and_post_id (badge_id,user_id,post_id) UNIQUE WHERE (post_id IS NOT NULL)
|
||||||
# index_user_badges_on_badge_id_and_user_id_and_seq (badge_id,user_id,seq) UNIQUE WHERE (post_id IS NULL)
|
# index_user_badges_on_badge_id_and_user_id_and_seq (badge_id,user_id,seq) UNIQUE WHERE (post_id IS NULL)
|
||||||
# index_user_badges_on_user_id (user_id)
|
# index_user_badges_on_user_id (user_id)
|
||||||
|
# index_user_badges_on_is_favorite (is_favorite)
|
||||||
#
|
#
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
class DetailedUserBadgeSerializer < BasicUserBadgeSerializer
|
class DetailedUserBadgeSerializer < BasicUserBadgeSerializer
|
||||||
has_one :granted_by, serializer: UserBadgeSerializer::UserSerializer
|
has_one :granted_by, serializer: UserBadgeSerializer::UserSerializer
|
||||||
|
|
||||||
attributes :post_number, :topic_id, :topic_title
|
attributes :post_number, :topic_id, :topic_title, :is_favorite, :can_favorite
|
||||||
|
|
||||||
def include_post_number?
|
def include_post_number?
|
||||||
object.post
|
object.post
|
||||||
|
@ -24,4 +24,8 @@ class DetailedUserBadgeSerializer < BasicUserBadgeSerializer
|
||||||
object.post.topic.title if object.post && object.post.topic
|
object.post.topic.title if object.post && object.post.topic
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_favorite
|
||||||
|
(scope.current_user.present? && object.user_id == scope.current_user.id) &&
|
||||||
|
!(1..4).include?(object.badge_id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3617,6 +3617,9 @@ en:
|
||||||
name: Other
|
name: Other
|
||||||
posting:
|
posting:
|
||||||
name: Posting
|
name: Posting
|
||||||
|
favorite_max_reached: "You can’t favorite more badges."
|
||||||
|
favorite_max_not_reached: "Mark this badge as favorite"
|
||||||
|
favorite_count: "%{count}/%{max} badges marked as favorite"
|
||||||
|
|
||||||
tagging:
|
tagging:
|
||||||
all_tags: "All Tags"
|
all_tags: "All Tags"
|
||||||
|
|
|
@ -671,7 +671,9 @@ Discourse::Application.routes.draw do
|
||||||
|
|
||||||
resources :badges, only: [:index]
|
resources :badges, only: [:index]
|
||||||
get "/badges/:id(/:slug)" => "badges#show", constraints: { format: /(json|html|rss)/ }
|
get "/badges/:id(/:slug)" => "badges#show", constraints: { format: /(json|html|rss)/ }
|
||||||
resources :user_badges, only: [:index, :create, :destroy]
|
resources :user_badges, only: [:index, :create, :destroy] do
|
||||||
|
put "toggle_favorite" => "user_badges#toggle_favorite", constraints: { format: :json }
|
||||||
|
end
|
||||||
|
|
||||||
get '/c', to: redirect(relative_url_root + 'categories')
|
get '/c', to: redirect(relative_url_root + 'categories')
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddIsFavoriteToUserBadge < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
add_column :user_badges, :is_favorite, :boolean
|
||||||
|
end
|
||||||
|
end
|
|
@ -113,6 +113,7 @@ module SvgSprite
|
||||||
"far-moon",
|
"far-moon",
|
||||||
"far-smile",
|
"far-smile",
|
||||||
"far-square",
|
"far-square",
|
||||||
|
"far-star",
|
||||||
"far-sun",
|
"far-sun",
|
||||||
"far-thumbs-down",
|
"far-thumbs-down",
|
||||||
"far-thumbs-up",
|
"far-thumbs-up",
|
||||||
|
|
|
@ -267,4 +267,41 @@ describe UserBadgesController do
|
||||||
expect(events).to include(:user_badge_removed)
|
expect(events).to include(:user_badge_removed)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "favorite" do
|
||||||
|
let!(:user_badge) { UserBadge.create(badge: badge, user: user, granted_by: Discourse.system_user, granted_at: Time.now) }
|
||||||
|
|
||||||
|
it "checks that the user is authorized to favorite the badge" do
|
||||||
|
sign_in(Fabricate(:admin))
|
||||||
|
put "/user_badges/#{user_badge.id}/toggle_favorite.json"
|
||||||
|
expect(response.status).to eq(403)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "checks that the user has less than two favorited badges" do
|
||||||
|
sign_in(user)
|
||||||
|
UserBadge.create(badge: Fabricate(:badge), user: user, granted_by: Discourse.system_user, granted_at: Time.now, is_favorite: true)
|
||||||
|
UserBadge.create(badge: Fabricate(:badge), user: user, granted_by: Discourse.system_user, granted_at: Time.now, is_favorite: true)
|
||||||
|
put "/user_badges/#{user_badge.id}/toggle_favorite.json"
|
||||||
|
expect(response.status).to eq(400)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "favorites a badge" do
|
||||||
|
sign_in(user)
|
||||||
|
put "/user_badges/#{user_badge.id}/toggle_favorite.json"
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
|
user_badge = UserBadge.find_by(user: user, badge: badge)
|
||||||
|
expect(user_badge.is_favorite).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "unfavorites a badge" do
|
||||||
|
sign_in(user)
|
||||||
|
user_badge.toggle!(:is_favorite)
|
||||||
|
put "/user_badges/#{user_badge.id}/toggle_favorite.json"
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
|
user_badge = UserBadge.find_by(user: user, badge: badge)
|
||||||
|
expect(user_badge.is_favorite).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user