Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add story pinned option #975

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions rails/app/assets/stylesheets/cms/_cards.scss
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@

&:hover {
box-shadow: 0 0 10px $lighter-gray;

.edit-icon-unpinned {
visibility: visible;
}
}

&--header {
display: flex;
justify-content: space-between;
griseduardo marked this conversation as resolved.
Show resolved Hide resolved
}

&--narrow {
Expand Down Expand Up @@ -117,3 +126,58 @@
word-wrap: break-word;
}
}

.edit-icon-pinned, .edit-icon-unpinned, .view-icon-pinned {
height: fit-content;
margin-inline-start: auto;
}

.edit-icon-unpinned {
visibility: hidden;
}

.edit-icon-pinned, .edit-icon-unpinned {
&:hover {
box-shadow: 0 0 10px $lighter-gray;
}
}

.icon-button, .view-icon-pinned {
max-width: 25px;
max-height: 25px;
padding: 0;
}

.icon-button {
border: none;
background-color: inherit;

&:hover {
cursor: pointer;

.icon-unpinned {
fill: $dark-gray;
}

.icon-pinned {
fill: $red;
}
}
}

.icon-unpinned, .icon-pinned {
max-height: 18px;
width: 100%;
}

.icon-unpinned {
fill: $light-gray;
}

.icon-pinned {
fill: $black;
}
griseduardo marked this conversation as resolved.
Show resolved Hide resolved

.story-header {
display: flex;
}
2 changes: 1 addition & 1 deletion rails/app/assets/stylesheets/cms/_icons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@
&::before {
content: "\1F4CD";
}
}
}
7 changes: 7 additions & 0 deletions rails/app/assets/stylesheets/components/card.scss
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@ $el-shadow: 0 1px 4px $lighter-gray;
.stories {
overflow-x: hidden;
flex: 5 0 0;

.pinned {
float: right;
width: 18px;
height: 18px;
};

.container {
padding-bottom: 3rem;

Expand Down
25 changes: 25 additions & 0 deletions rails/app/controllers/dashboard/stories_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ def new

def create
authorize Story

if ActiveModel::Type::Boolean.new.cast(story_params[:story_pinned])
community_stories.update_all(story_pinned: false)
end

@story = community_stories.new(story_params.except(:media))

if @story.save
Expand All @@ -44,6 +49,10 @@ def edit
def update
@story = authorize community_stories.find(params[:id])

if [email protected]_pinned && ActiveModel::Type::Boolean.new.cast(story_params[:story_pinned])
community_stories.update_all(story_pinned: false)
end

if @story.update(story_params.except(:media))
story_params[:media]&.each do |media|
m = @story.media.create(media: media)
Expand All @@ -55,6 +64,21 @@ def update
end
end

def pin
@story = authorize community_stories.find(params[:id])
community_stories.update_all(story_pinned: false)
@story.update(story_pinned: true)

redirect_to stories_path
end

def unpin
@story = authorize community_stories.find(params[:id])
@story.update(story_pinned: false)

redirect_to stories_path
end

def destroy
@story = authorize community_stories.find(params[:id])

Expand Down Expand Up @@ -88,6 +112,7 @@ def story_params
:topic,
:interview_location_id,
:interviewer_id,
:story_pinned,
media: [],
speaker_ids: [],
place_ids: []
Expand Down
6 changes: 6 additions & 0 deletions rails/app/javascript/components/Sort.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ class Sort extends Component {
return {value: value, label: this.props.t(value)};
}

storyPinnedSort = (sortedStories) => (
sortedStories.sort(story => story.story_pinned ? -1 : 1)
)

handleSort = (option) => {
this.setState({
sortSelectValue: option.value
Expand Down Expand Up @@ -93,6 +97,8 @@ class Sort extends Component {
}
}

sortedStories = this.storyPinnedSort(sortedStories)

this.props.handleStoriesChanged(sortedStories);
}

Expand Down
35 changes: 25 additions & 10 deletions rails/app/javascript/components/Story.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React from "react";
import PropTypes from "prop-types";
import StoryMedia from "./StoryMedia";
import { useTranslation } from 'react-i18next';
import StoryMedia from "./StoryMedia";

const Story = props => {
const { t } = useTranslation();
const { story, storyClass } = props;
const { onStoryClick, story, storyClass } = props;

const renderSpeakers = speakers => {
return (
Expand Down Expand Up @@ -34,14 +34,23 @@ const Story = props => {
}

return (
<React.Fragment>
<>
<li
className={storyClass}
onClick={() => props.onStoryClick(story)}
onKeyDown={() => props.onStoryClick(story)}
onClick={() => onStoryClick(story)}
onKeyDown={() => onStoryClick(story)}
key={story.id}
role="presentation"
>
{
story.story_pinned && (
<div className="pinned">
<svg viewBox="0 -960 960 960" xmlns="http://www.w3.org/2000/svg">
<path d="m634-448 86 77v60H510v241l-30 30-30-30v-241H240v-60l80-77v-333h-50v-60h414v60h-50v333Z"/>
</svg>
</div>
)
}
<div className="speakers">
{renderSpeakers(story.speakers)}
</div>
Expand All @@ -61,14 +70,20 @@ const Story = props => {
))
}
{
story.language &&
<p>
<b>{t("language")}:</b> {story.language}
</p>
story.language && (
<p>
<b>
{t("language")}
:
</b>
{' '}
{story.language}
</p>
)
}
</div>
</li>
</React.Fragment>
</>
);
}

Expand Down
1 change: 1 addition & 0 deletions rails/app/models/story.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ def public_points
# desc :text
# language :string
# permission_level :integer
# story_pinned :boolean default(FALSE)
# title :string
# topic :string
# created_at :datetime not null
Expand Down
7 changes: 5 additions & 2 deletions rails/app/pages/stories_page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ def relation
stories = stories.joins(:speaker_stories).where(speaker_stories: {speaker_id: @meta[:speaker]}) if @meta[:speaker].present?
stories = stories.where(permission_level: @meta[:visibility]) if @meta[:visibility].present?

stories.order(@meta[:sort_by] => @meta[:sort_dir])
stories.order(
story_pinned: :desc,
@meta[:sort_by] => @meta[:sort_dir]
)
end
end
end
8 changes: 8 additions & 0 deletions rails/app/policies/story_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ def update?
edit?
end

def pin?
edit?
end

def unpin?
edit?
end

def destroy?
user.admin? || user.editor?
end
Expand Down
5 changes: 5 additions & 0 deletions rails/app/views/dashboard/stories/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
</div>
<% end %>

<%= f.label :pin_story %>
<div class="checklist">
<%= f.check_box :story_pinned, {}, "true", "false" %>
</div>

<%= f.label :speaker_ids, class: "required" %>
<div class="checklist">
<%= f.collection_check_boxes :speaker_ids, current_community.speakers, :id, :name, required: true %>
Expand Down
36 changes: 32 additions & 4 deletions rails/app/views/dashboard/stories/_stories.html.erb
Original file line number Diff line number Diff line change
@@ -1,10 +1,38 @@
<% @stories.each do |story| %>
<%= link_to story, class: "card" do %>
<div>
<h3>
<span class="card__heading--small"><%= story.topic %></span>
<%= story.title %>
</h3>
<div class="story-header">
<div>
<h3 style="width:max-content">
<span class="card__heading--small"><%= story.topic %></span>
<%= story.title %>
</h3>
</div>
<% if current_user.member? && story.story_pinned %>
<div class="view-icon-pinned">
<svg class="icon-pinned"><use href="#icon-thumbtack"></svg>
</div>
<% end %>
<% if current_user.admin? || current_user.editor? %>
<% if story.story_pinned %>
<div class="edit-icon-pinned">
<%= form_with model: @story, url: unpin_story_path(story), method: "patch", multipart: true, class: "form", data: {remote: false}, locale: true do |f| %>
<%= f.button nil, class: "icon-button" do %>
<svg class="icon-pinned"><use href="#icon-thumbtack"></svg>
<% end %>
<% end %>
</div>
<% else %>
<div class="edit-icon-unpinned">
<%= form_with model: @story, url: pin_story_path(story), method: "patch", multipart: true, class: "form", data: {remote: false}, locale: true do |f| %>
<%= f.button nil, class: "icon-button" do %>
<svg class="icon-unpinned"><use href="#icon-thumbtack"></svg>
<% end %>
<% end %>
</div>
<% end %>
griseduardo marked this conversation as resolved.
Show resolved Hide resolved
<% end %>
</div>

<% if story.language.present? %>
<span class="badge badge-red"><%= story.language %></span>
Expand Down
2 changes: 1 addition & 1 deletion rails/app/views/home/_home.json.jbuilder
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
json.stories stories do |story|
json.extract! story, :title, :desc, :id, :created_at
json.extract! story, :title, :desc, :id, :created_at, :story_pinned
json.points story.places.map(&:point_geojson)
json.places story.places
json.language story.language
Expand Down
6 changes: 5 additions & 1 deletion rails/app/views/shared/_icons.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@
<path d="M10.5 8a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0z"/>
<path d="M0 8s3-5.5 8-5.5S16 8 16 8s-3 5.5-8 5.5S0 8 0 8zm8 3.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z"/>
</symbol>
</svg>

<symbol id="icon-thumbtack" viewBox="0 -960 960 960">
<path d="m634-448 86 77v60H510v241l-30 30-30-30v-241H240v-60l80-77v-333h-50v-60h414v60h-50v333Z"/>
</symbol>
</svg>
2 changes: 2 additions & 0 deletions rails/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
delete :name_audio, action: :delete_name_audio
end
resources :stories do
patch :pin, on: :member
patch :unpin, on: :member
delete '/media/:id/delete', action: :delete_media, as: :delete_media
end
resource :theme, only: [:update, :edit, :show]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddStoryPinnedToStories < ActiveRecord::Migration[6.1]
def change
add_column :stories, :story_pinned, :boolean, default: false
end
end
3 changes: 2 additions & 1 deletion rails/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2023_08_08_135800) do
ActiveRecord::Schema.define(version: 2023_10_09_012520) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -154,6 +154,7 @@
t.integer "interviewer_id"
t.integer "community_id"
t.string "topic"
t.boolean "story_pinned", default: false
end

create_table "themes", force: :cascade do |t|
Expand Down
Loading