@@ -9,14 +9,22 @@ class Cards::PinsController < ApplicationController
|
||||
@pin = @card.pin_by Current.user
|
||||
|
||||
broadcast_add_pin_to_tray
|
||||
render_pin_button_replacement
|
||||
|
||||
respond_to do |format|
|
||||
format.turbo_stream { render_pin_button_replacement }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@pin = @card.unpin_by Current.user
|
||||
|
||||
broadcast_remove_pin_from_tray
|
||||
render_pin_button_replacement
|
||||
|
||||
respond_to do |format|
|
||||
format.turbo_stream { render_pin_button_replacement }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
class My::PinsController < ApplicationController
|
||||
def index
|
||||
@pins = Current.user.pins.includes(:card).ordered.limit(20)
|
||||
@pins = user_pins
|
||||
fresh_when etag: [ @pins, @pins.collect(&:card) ]
|
||||
end
|
||||
|
||||
private
|
||||
def user_pins
|
||||
Current.user.pins.includes(:card).ordered.limit(pins_limit)
|
||||
end
|
||||
|
||||
def pins_limit
|
||||
request.format.json? ? 100 : 20
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
json.array! @pins do |pin|
|
||||
json.partial! "cards/card", card: pin.card
|
||||
end
|
||||
+73
@@ -805,6 +805,79 @@ __Response:__
|
||||
|
||||
Returns `204 No Content` on success.
|
||||
|
||||
## Pins
|
||||
|
||||
Pins let users keep quick access to important cards.
|
||||
|
||||
### `POST /:account_slug/cards/:card_number/pin`
|
||||
|
||||
Pins a card for the current user.
|
||||
|
||||
__Response:__
|
||||
|
||||
Returns `204 No Content` on success.
|
||||
|
||||
### `DELETE /:account_slug/cards/:card_number/pin`
|
||||
|
||||
Unpins a card for the current user.
|
||||
|
||||
__Response:__
|
||||
|
||||
Returns `204 No Content` on success.
|
||||
|
||||
### `GET /my/pins`
|
||||
|
||||
Returns the current user's pinned cards. This endpoint is not paginated and returns up to 100 cards.
|
||||
|
||||
__Response:__
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "03f5vaeq985jlvwv3arl4srq2",
|
||||
"number": 1,
|
||||
"title": "First!",
|
||||
"status": "published",
|
||||
"description": "Hello, World!",
|
||||
"description_html": "<div class=\"action-text-content\"><p>Hello, World!</p></div>",
|
||||
"image_url": null,
|
||||
"tags": ["programming"],
|
||||
"golden": false,
|
||||
"last_active_at": "2025-12-05T19:38:48.553Z",
|
||||
"created_at": "2025-12-05T19:38:48.540Z",
|
||||
"url": "http://fizzy.localhost:3006/897362094/cards/4",
|
||||
"board": {
|
||||
"id": "03f5v9zkft4hj9qq0lsn9ohcm",
|
||||
"name": "Fizzy",
|
||||
"all_access": true,
|
||||
"created_at": "2025-12-05T19:36:35.534Z",
|
||||
"url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm",
|
||||
"creator": {
|
||||
"id": "03f5v9zjw7pz8717a4no1h8a7",
|
||||
"name": "David Heinemeier Hansson",
|
||||
"role": "owner",
|
||||
"active": true,
|
||||
"email_address": "david@example.com",
|
||||
"created_at": "2025-12-05T19:36:35.401Z",
|
||||
"url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7",
|
||||
"avatar_url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7/avatar"
|
||||
}
|
||||
},
|
||||
"creator": {
|
||||
"id": "03f5v9zjw7pz8717a4no1h8a7",
|
||||
"name": "David Heinemeier Hansson",
|
||||
"role": "owner",
|
||||
"active": true,
|
||||
"email_address": "david@example.com",
|
||||
"created_at": "2025-12-05T19:36:35.401Z",
|
||||
"url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7",
|
||||
"avatar_url": "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7/avatar"
|
||||
},
|
||||
"comments_url": "http://fizzy.localhost:3006/897362094/cards/4/comments"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Comments
|
||||
|
||||
Comments are attached to cards and support rich text.
|
||||
|
||||
@@ -17,6 +17,17 @@ class Cards::PinsControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "create as JSON" do
|
||||
card = cards(:layout)
|
||||
|
||||
assert_not card.pinned_by?(users(:kevin))
|
||||
|
||||
post card_pin_path(card), as: :json
|
||||
|
||||
assert_response :no_content
|
||||
assert card.reload.pinned_by?(users(:kevin))
|
||||
end
|
||||
|
||||
test "destroy" do
|
||||
assert_changes -> { cards(:shipping).pinned_by?(users(:kevin)) }, from: true, to: false do
|
||||
perform_enqueued_jobs do
|
||||
@@ -28,4 +39,15 @@ class Cards::PinsControllerTest < ActionDispatch::IntegrationTest
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "destroy as JSON" do
|
||||
card = cards(:shipping)
|
||||
|
||||
assert card.pinned_by?(users(:kevin))
|
||||
|
||||
delete card_pin_path(card), as: :json
|
||||
|
||||
assert_response :no_content
|
||||
assert_not card.reload.pinned_by?(users(:kevin))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,4 +11,14 @@ class My::PinsControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_response :success
|
||||
assert_select "div", text: /#{users(:kevin).pins.first.card.title}/
|
||||
end
|
||||
|
||||
test "index as JSON" do
|
||||
expected_ids = users(:kevin).pins.ordered.pluck(:card_id)
|
||||
|
||||
get my_pins_path(format: :json)
|
||||
|
||||
assert_response :success
|
||||
assert_equal expected_ids.count, @response.parsed_body.count
|
||||
assert_equal expected_ids, @response.parsed_body.map { |card| card["id"] }
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user