Merge pull request #1026 from Retrospring/feature/pinned-answers
This commit is contained in:
commit
3122d31f55
|
@ -82,11 +82,22 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__pinned {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.card-body {
|
.card-body {
|
||||||
padding-bottom: .6rem;
|
padding-bottom: .6rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pinned-answers {
|
||||||
|
|
||||||
|
.answerbox__pinned {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
body:not(.cap-web-share) {
|
body:not(.cap-web-share) {
|
||||||
[name="ab-share"] {
|
[name="ab-share"] {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class AnswerController < ApplicationController
|
class AnswerController < ApplicationController
|
||||||
|
before_action :authenticate_user!, only: %i[pin unpin]
|
||||||
|
|
||||||
|
include TurboStreamable
|
||||||
|
|
||||||
|
turbo_stream_actions :pin, :unpin
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@answer = Answer.includes(comments: %i[user smiles], question: [:user], smiles: [:user]).find(params[:id])
|
@answer = Answer.includes(comments: %i[user smiles], question: [:user], smiles: [:user]).find(params[:id])
|
||||||
@display_all = true
|
@display_all = true
|
||||||
|
@ -16,4 +22,34 @@ class AnswerController < ApplicationController
|
||||||
notif.update_all(new: false) unless notif.empty?
|
notif.update_all(new: false) unless notif.empty?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pin
|
||||||
|
answer = Answer.includes(:user).find(params[:id])
|
||||||
|
UseCase::Answer::Pin.call(user: current_user, answer:)
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html { redirect_to(user_path(username: current_user.screen_name)) }
|
||||||
|
format.turbo_stream do
|
||||||
|
render turbo_stream: [
|
||||||
|
turbo_stream.update("ab-pin-#{answer.id}", partial: "actions/pin", locals: { answer: }),
|
||||||
|
render_toast(t(".success"))
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unpin
|
||||||
|
answer = Answer.includes(:user).find(params[:id])
|
||||||
|
UseCase::Answer::Unpin.call(user: current_user, answer:)
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html { redirect_to(user_path(username: current_user.screen_name)) }
|
||||||
|
format.turbo_stream do
|
||||||
|
render turbo_stream: [
|
||||||
|
turbo_stream.update("ab-pin-#{answer.id}", partial: "actions/pin", locals: { answer: }),
|
||||||
|
render_toast(t(".success"))
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,6 +7,7 @@ class UserController < ApplicationController
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@answers = @user.cursored_answers(last_id: params[:last_id])
|
@answers = @user.cursored_answers(last_id: params[:last_id])
|
||||||
|
@pinned_answers = @user.answers.pinned.order(pinned_at: :desc).limit(10)
|
||||||
@answers_last_id = @answers.map(&:id).min
|
@answers_last_id = @answers.map(&:id).min
|
||||||
@more_data_available = !@user.cursored_answers(last_id: @answers_last_id, size: 1).count.zero?
|
@more_data_available = !@user.cursored_answers(last_id: @answers_last_id, size: 1).count.zero?
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ class Answer < ApplicationRecord
|
||||||
validates :question_id, uniqueness: { scope: :user_id }
|
validates :question_id, uniqueness: { scope: :user_id }
|
||||||
# rubocop:enable Rails/UniqueValidationWithoutIndex
|
# rubocop:enable Rails/UniqueValidationWithoutIndex
|
||||||
|
|
||||||
|
scope :pinned, -> { where.not(pinned_at: nil) }
|
||||||
|
|
||||||
SHORT_ANSWER_MAX_LENGTH = 640
|
SHORT_ANSWER_MAX_LENGTH = 640
|
||||||
|
|
||||||
# rubocop:disable Rails/SkipsModelValidations
|
# rubocop:disable Rails/SkipsModelValidations
|
||||||
|
@ -56,4 +58,6 @@ class Answer < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def long? = content.length > SHORT_ANSWER_MAX_LENGTH
|
def long? = content.length > SHORT_ANSWER_MAX_LENGTH
|
||||||
|
|
||||||
|
def pinned? = pinned_at.present?
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AnswerPolicy
|
||||||
|
attr_reader :user, :answer
|
||||||
|
|
||||||
|
def initialize(user, answer)
|
||||||
|
@user = user
|
||||||
|
@answer = answer
|
||||||
|
end
|
||||||
|
|
||||||
|
def pin? = answer.user == user
|
||||||
|
|
||||||
|
def unpin? = answer.user == user
|
||||||
|
end
|
|
@ -16,6 +16,8 @@
|
||||||
%a.dropdown-item{ href: "#", data: { a_id: answer.id, action: "ab-report" } }
|
%a.dropdown-item{ href: "#", data: { a_id: answer.id, action: "ab-report" } }
|
||||||
%i.fa.fa-fw.fa-exclamation-triangle
|
%i.fa.fa-fw.fa-exclamation-triangle
|
||||||
= t("voc.report")
|
= t("voc.report")
|
||||||
|
- else
|
||||||
|
= render "actions/pin", answer:
|
||||||
- if current_user.admin?
|
- if current_user.admin?
|
||||||
%a.dropdown-item{ href: rails_admin_path_for_resource(answer), target: "_blank" }
|
%a.dropdown-item{ href: rails_admin_path_for_resource(answer), target: "_blank" }
|
||||||
%i.fa.fa-fw.fa-gears
|
%i.fa.fa-fw.fa-gears
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
- if answer.pinned?
|
||||||
|
= button_to unpin_answer_path(username: current_user.screen_name, id: answer.id),
|
||||||
|
class: "dropdown-item",
|
||||||
|
method: :delete,
|
||||||
|
form: { id: "ab-pin-#{answer.id}", data: { turbo_stream: true } } do
|
||||||
|
%i.fa.fa-fw.fa-thumbtack
|
||||||
|
= t(".unpin")
|
||||||
|
- else
|
||||||
|
= button_to pin_answer_path(username: current_user.screen_name, id: answer.id),
|
||||||
|
class: "dropdown-item",
|
||||||
|
method: :post,
|
||||||
|
form: { id: "ab-pin-#{answer.id}", data: { turbo_stream: true } } do
|
||||||
|
%i.fa.fa-fw.fa-thumbtack
|
||||||
|
= t(".pin")
|
|
@ -0,0 +1,2 @@
|
||||||
|
= turbo_stream.update("ab-pin-#{answer.id}") do
|
||||||
|
= render "actions/pin", answer:
|
|
@ -27,6 +27,11 @@
|
||||||
.col-md-6.text-start.text-muted
|
.col-md-6.text-start.text-muted
|
||||||
%i.fa.fa-clock-o
|
%i.fa.fa-clock-o
|
||||||
= link_to(raw(t("time.distance_ago", time: time_tooltip(a))), answer_path(a.user.screen_name, a.id), class: "answerbox__permalink")
|
= link_to(raw(t("time.distance_ago", time: time_tooltip(a))), answer_path(a.user.screen_name, a.id), class: "answerbox__permalink")
|
||||||
|
- if a.pinned_at.present?
|
||||||
|
%span.answerbox__pinned
|
||||||
|
·
|
||||||
|
%i.fa.fa-thumbtack
|
||||||
|
= t(".pinned")
|
||||||
.col-md-6.d-md-flex.answerbox__actions
|
.col-md-6.d-md-flex.answerbox__actions
|
||||||
= render "answerbox/actions", a: a, display_all: display_all
|
= render "answerbox/actions", a: a, display_all: display_all
|
||||||
.card-footer{ id: "ab-comments-section-#{a.id}", class: display_all.nil? ? "d-none" : nil }
|
.card-footer{ id: "ab-comments-section-#{a.id}", class: display_all.nil? ? "d-none" : nil }
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
- unless @user.banned?
|
- unless @user.banned?
|
||||||
|
#pinned-answers
|
||||||
|
- @pinned_answers.each do |a|
|
||||||
|
= render "answerbox", a:
|
||||||
|
|
||||||
#answers
|
#answers
|
||||||
- @answers.each do |a|
|
- @answers.each do |a|
|
||||||
= render 'answerbox', a: a
|
= render "answerbox", a:
|
||||||
|
|
||||||
- if @more_data_available
|
- if @more_data_available
|
||||||
.d-flex.justify-content-center.justify-content-sm-start#paginator
|
.d-flex.justify-content-center.justify-content-sm-start#paginator
|
||||||
|
|
|
@ -26,6 +26,8 @@ en:
|
||||||
destroy:
|
destroy:
|
||||||
nopriv: "You cannot delete other people's answers."
|
nopriv: "You cannot delete other people's answers."
|
||||||
success: "Successfully deleted answer."
|
success: "Successfully deleted answer."
|
||||||
|
pin:
|
||||||
|
success: "Successfully pinned answer."
|
||||||
comment:
|
comment:
|
||||||
create:
|
create:
|
||||||
invalid: "Your comment is too long."
|
invalid: "Your comment is too long."
|
||||||
|
@ -210,3 +212,8 @@ en:
|
||||||
timeline:
|
timeline:
|
||||||
public:
|
public:
|
||||||
title: "Public Timeline"
|
title: "Public Timeline"
|
||||||
|
answer:
|
||||||
|
pin:
|
||||||
|
success: "This answer will now appear at the top of your profile."
|
||||||
|
unpin:
|
||||||
|
success: "This answer will no longer be pinned to the top of your profile."
|
||||||
|
|
|
@ -75,6 +75,9 @@ en:
|
||||||
return: "Return to Inbox"
|
return: "Return to Inbox"
|
||||||
comment:
|
comment:
|
||||||
view_smiles: "View comment smiles"
|
view_smiles: "View comment smiles"
|
||||||
|
pin:
|
||||||
|
pin: "Pin to Profile"
|
||||||
|
unpin: "Unpin from Profile"
|
||||||
share:
|
share:
|
||||||
twitter: "Share on Twitter"
|
twitter: "Share on Twitter"
|
||||||
tumblr: "Share on Tumblr"
|
tumblr: "Share on Tumblr"
|
||||||
|
@ -123,6 +126,7 @@ en:
|
||||||
read: "Read the entire answer"
|
read: "Read the entire answer"
|
||||||
answered: "%{hide} %{user}" # resolves into "Answered by %{user}"
|
answered: "%{hide} %{user}" # resolves into "Answered by %{user}"
|
||||||
hide: "Answered by"
|
hide: "Answered by"
|
||||||
|
pinned: "Pinned"
|
||||||
questionbox:
|
questionbox:
|
||||||
title: "Ask something!"
|
title: "Ask something!"
|
||||||
placeholder: "Type your question here…"
|
placeholder: "Type your question here…"
|
||||||
|
|
|
@ -146,6 +146,8 @@ Rails.application.routes.draw do
|
||||||
get "/user/:username", to: "user#show"
|
get "/user/:username", to: "user#show"
|
||||||
get "/@:username", to: "user#show", as: :user
|
get "/@:username", to: "user#show", as: :user
|
||||||
get "/@:username/a/:id", to: "answer#show", as: :answer
|
get "/@:username/a/:id", to: "answer#show", as: :answer
|
||||||
|
post "/@:username/a/:id/pin", to: "answer#pin", as: :pin_answer
|
||||||
|
delete "/@:username/a/:id/pin", to: "answer#unpin", as: :unpin_answer
|
||||||
get "/@:username/q/:id", to: "question#show", as: :question
|
get "/@:username/q/:id", to: "question#show", as: :question
|
||||||
get "/@:username/followers", to: "user#followers", as: :show_user_followers
|
get "/@:username/followers", to: "user#followers", as: :show_user_followers
|
||||||
get "/@:username/followings", to: "user#followings", as: :show_user_followings
|
get "/@:username/followings", to: "user#followings", as: :show_user_followings
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddPinnedAtToAnswers < ActiveRecord::Migration[6.1]
|
||||||
|
def change
|
||||||
|
add_column :answers, :pinned_at, :timestamp
|
||||||
|
add_index :answers, %i[user_id pinned_at]
|
||||||
|
end
|
||||||
|
end
|
|
@ -48,8 +48,10 @@ ActiveRecord::Schema.define(version: 2023_02_12_181044) do
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.integer "smile_count", default: 0, null: false
|
t.integer "smile_count", default: 0, null: false
|
||||||
|
t.datetime "pinned_at"
|
||||||
t.index ["question_id"], name: "index_answers_on_question_id"
|
t.index ["question_id"], name: "index_answers_on_question_id"
|
||||||
t.index ["user_id", "created_at"], name: "index_answers_on_user_id_and_created_at"
|
t.index ["user_id", "created_at"], name: "index_answers_on_user_id_and_created_at"
|
||||||
|
t.index ["user_id", "pinned_at"], name: "index_answers_on_user_id_and_pinned_at"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "appendables", force: :cascade do |t|
|
create_table "appendables", force: :cascade do |t|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module UseCase
|
||||||
|
module Answer
|
||||||
|
class Pin < UseCase::Base
|
||||||
|
option :user, type: Types.Instance(::User)
|
||||||
|
option :answer, type: Types.Instance(::Answer)
|
||||||
|
|
||||||
|
def call
|
||||||
|
authorize!(:pin, user, answer)
|
||||||
|
check_unpinned!
|
||||||
|
|
||||||
|
answer.pinned_at = Time.now.utc
|
||||||
|
answer.save!
|
||||||
|
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
resource: answer,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_unpinned!
|
||||||
|
raise ::Errors::BadRequest if answer.pinned_at.present?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,29 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module UseCase
|
||||||
|
module Answer
|
||||||
|
class Unpin < UseCase::Base
|
||||||
|
option :user, type: Types.Instance(::User)
|
||||||
|
option :answer, type: Types.Instance(::Answer)
|
||||||
|
|
||||||
|
def call
|
||||||
|
authorize!(:unpin, user, answer)
|
||||||
|
check_pinned!
|
||||||
|
|
||||||
|
answer.pinned_at = nil
|
||||||
|
answer.save!
|
||||||
|
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
resource: nil,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_pinned!
|
||||||
|
raise ::Errors::BadRequest if answer.pinned_at.nil?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,5 +9,11 @@ module UseCase
|
||||||
def self.call(...) = new(...).call
|
def self.call(...) = new(...).call
|
||||||
|
|
||||||
def call = raise NotImplementedError
|
def call = raise NotImplementedError
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def authorize!(verb, user, record, error_class: Errors::NotAuthorized)
|
||||||
|
raise error_class unless Pundit.policy!(user, record).public_send("#{verb}?")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
require "rails_helper"
|
require "rails_helper"
|
||||||
|
|
||||||
describe AnswerController do
|
describe AnswerController, type: :controller do
|
||||||
|
include ActiveSupport::Testing::TimeHelpers
|
||||||
|
|
||||||
let(:user) do
|
let(:user) do
|
||||||
FactoryBot.create :user,
|
FactoryBot.create :user,
|
||||||
otp_module: :disabled,
|
otp_module: :disabled,
|
||||||
|
@ -39,4 +41,60 @@ describe AnswerController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#pin" do
|
||||||
|
subject { post :pin, params: { username: user.screen_name, id: answer.id }, format: :turbo_stream }
|
||||||
|
|
||||||
|
context "user signed in" do
|
||||||
|
before(:each) { sign_in user }
|
||||||
|
|
||||||
|
it "pins the answer" do
|
||||||
|
travel_to(Time.at(1603290950).utc) do
|
||||||
|
expect { subject }.to change { answer.reload.pinned_at }.from(nil).to(Time.at(1603290950).utc)
|
||||||
|
expect(response.body).to include("turbo-stream action=\"update\" target=\"ab-pin-#{answer.id}\"")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "other user signed in" do
|
||||||
|
let(:other_user) { FactoryBot.create(:user) }
|
||||||
|
|
||||||
|
before(:each) { sign_in other_user }
|
||||||
|
|
||||||
|
it "does not pin the answer" do
|
||||||
|
travel_to(Time.at(1603290950).utc) do
|
||||||
|
expect { subject }.not_to(change { answer.reload.pinned_at })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#unpin" do
|
||||||
|
subject { delete :unpin, params: { username: user.screen_name, id: answer.id }, format: :turbo_stream }
|
||||||
|
|
||||||
|
context "user signed in" do
|
||||||
|
before(:each) do
|
||||||
|
sign_in user
|
||||||
|
answer.update!(pinned_at: Time.at(1603290950).utc)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "unpins the answer" do
|
||||||
|
expect { subject }.to change { answer.reload.pinned_at }.from(Time.at(1603290950).utc).to(nil)
|
||||||
|
expect(response.body).to include("turbo-stream action=\"update\" target=\"ab-pin-#{answer.id}\"")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "other user signed in" do
|
||||||
|
let(:other_user) { FactoryBot.create(:user) }
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
sign_in other_user
|
||||||
|
answer.update!(pinned_at: Time.at(1603290950).utc)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not unpin the answer" do
|
||||||
|
expect { subject }.not_to(change { answer.reload.pinned_at })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
describe UseCase::Answer::Pin do
|
||||||
|
include ActiveSupport::Testing::TimeHelpers
|
||||||
|
|
||||||
|
subject { UseCase::Answer::Pin.call(user:, answer:) }
|
||||||
|
|
||||||
|
context "answer exists" do
|
||||||
|
let(:answer) { FactoryBot.create(:answer, user: FactoryBot.create(:user)) }
|
||||||
|
|
||||||
|
context "as answer owner" do
|
||||||
|
let(:user) { answer.user }
|
||||||
|
|
||||||
|
it "pins the answer" do
|
||||||
|
travel_to(Time.at(1603290950).utc) do
|
||||||
|
expect { subject }.to change { answer.pinned_at }.from(nil).to(Time.at(1603290950).utc)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "answer is already pinned" do
|
||||||
|
before do
|
||||||
|
answer.update!(pinned_at: Time.at(1603290950).utc)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error" do
|
||||||
|
expect { subject }.to raise_error(Errors::BadRequest)
|
||||||
|
expect(answer.reload.pinned_at).to eq(Time.at(1603290950).utc)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "as other user" do
|
||||||
|
let(:user) { FactoryBot.create(:user) }
|
||||||
|
|
||||||
|
it "does not pin the answer" do
|
||||||
|
expect { subject }.to raise_error(Errors::NotAuthorized)
|
||||||
|
expect(answer.reload.pinned_at).to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,40 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
describe UseCase::Answer::Unpin do
|
||||||
|
include ActiveSupport::Testing::TimeHelpers
|
||||||
|
|
||||||
|
subject { UseCase::Answer::Unpin.call(user:, answer:) }
|
||||||
|
|
||||||
|
context "answer exists" do
|
||||||
|
let(:pinned_at) { Time.at(1603290950).utc }
|
||||||
|
let(:answer) { FactoryBot.create(:answer, user: FactoryBot.create(:user), pinned_at:) }
|
||||||
|
|
||||||
|
context "as answer owner" do
|
||||||
|
let(:user) { answer.user }
|
||||||
|
|
||||||
|
it "unpins the answer" do
|
||||||
|
expect { subject }.to change { answer.pinned_at }.from(pinned_at).to(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "answer is already unpinned" do
|
||||||
|
let(:pinned_at) { nil }
|
||||||
|
|
||||||
|
it "raises an error" do
|
||||||
|
expect { subject }.to raise_error(Errors::BadRequest)
|
||||||
|
expect(answer.reload.pinned_at).to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "as other user" do
|
||||||
|
let(:user) { FactoryBot.create(:user) }
|
||||||
|
|
||||||
|
it "does not unpin the answer" do
|
||||||
|
expect { subject }.to raise_error(Errors::NotAuthorized)
|
||||||
|
expect(answer.reload.pinned_at).to eq(pinned_at)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -32,7 +32,8 @@ describe UseCase::DataExport::Answers, :data_export do
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
created_at: "2022-12-10T13:37:42.000Z",
|
created_at: "2022-12-10T13:37:42.000Z",
|
||||||
updated_at: "2022-12-10T13:37:42.000Z",
|
updated_at: "2022-12-10T13:37:42.000Z",
|
||||||
smile_count: 0
|
smile_count: 0,
|
||||||
|
pinned_at: nil,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue