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 3 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
27 changes: 27 additions & 0 deletions rails/app/assets/stylesheets/cms/_cards.scss
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,30 @@
word-wrap: break-word;
}
}

.edit-icon-pinned, .edit-icon-not-pinned, .view-icon-pinned {
display: none;
float: right;
height: fit-content;
margin-inline-start: auto;
}

.edit-icon-pinned, .view-icon-pinned {
color: $black;
griseduardo marked this conversation as resolved.
Show resolved Hide resolved
}

.edit-icon-not-pinned {
color: $light-gray;
}

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

.card:hover .edit-icon-pinned,
.card:hover .edit-icon-not-pinned,
.card:hover .view-icon-pinned {
display: flex;
}
11 changes: 10 additions & 1 deletion rails/app/assets/stylesheets/cms/_icons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,13 @@
&::before {
content: "\1F4CD";
}
}
}

.icon-pinned, .icon-not-pinned {
background-color: inherit;
border: none;
}

.icon-not-pinned {
color: $light-gray;
}
griseduardo marked this conversation as resolved.
Show resolved Hide resolved
15 changes: 15 additions & 0 deletions rails/app/assets/stylesheets/cms/layout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,18 @@ svg[hidden] {
svg:not(:root) {
overflow: hidden;
}

.story-main-heading {
width: max-content;
}

.story-title {
display: flex;
}

.story-pinned {
display: flex;
height: 100%;
float: right;
margin-left: 15px;
}
3 changes: 3 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,9 @@ $el-shadow: 0 1px 4px $lighter-gray;
.stories {
overflow-x: hidden;
flex: 5 0 0;
.pinned {
float: right;
};
.container {
padding-bottom: 3rem;

Expand Down
24 changes: 23 additions & 1 deletion rails/app/controllers/dashboard/stories_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,29 @@ def edit

def update
@story = authorize community_stories.find(params[:id])
@stories = community_stories.all

if @story.update(story_params.except(:media))
if params[:story_pinned].present?
if params[:story_pinned]
@pinned_story = @stories.find_by(story_pinned: true)
@pinned_story&.update(story_pinned: !params[:story_pinned])
end

@story.update(story_pinned: params[:story_pinned])

return redirect_to action: "index"
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For ease of maintaining and reducing complexity of this, I think we should split out this block to allow pinning/unpinning from non-form views as its own custom rails action. I might also suggest we create two, one for pinning and one for unpinning and trigger them using remote: true calls so we don't refresh the page on them. Another benefit of this will ensure the logic stays relatively simple:

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

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

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea separate in different actions.
Only one problem, related to use remote true, is that it doesn't change the icon color and reordenize the stories, that can pass the wrong idea that the pin or unpin action didn't worked.
I maintained the render behavior. What do you think about it?


if story_params[:story_pinned].present?
if story_params[:story_pinned]
@pinned_story = @stories.find_by(story_pinned: true)
@pinned_story&.update(story_pinned: !story_params[:story_pinned])
end

@story.update(story_pinned: story_params[:story_pinned])

render :edit
griseduardo marked this conversation as resolved.
Show resolved Hide resolved
elsif @story.update(story_params.except(:media))
story_params[:media].each do |media|
m = @story.media.create(media: media)
end
Expand Down Expand Up @@ -88,6 +109,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
33 changes: 23 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,21 @@ 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">
<i className="icon-pushpin" />
</div>
)
}
<div className="speakers">
{renderSpeakers(story.speakers)}
</div>
Expand All @@ -61,14 +68,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 @@ -73,6 +73,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
8 changes: 5 additions & 3 deletions rails/app/pages/stories_page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ def initialize(scoped_stories, meta = {})

@meta[:limit] ||= 10
@meta[:offset] ||= 0
@meta[:sort_by] ||= "created_at"
@meta[:sort_by_pinned] ||= "story_pinned"
@meta[:sort_by_created] ||= "created_at"
griseduardo marked this conversation as resolved.
Show resolved Hide resolved
@meta[:sort_dir] ||= "desc"
end

Expand All @@ -16,6 +17,7 @@ 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 = stories.order(@meta[:sort_by_pinned] => @meta[:sort_dir])
griseduardo marked this conversation as resolved.
Show resolved Hide resolved
stories.order(@meta[:sort_by_created] => @meta[:sort_dir])
end
end
end
38 changes: 34 additions & 4 deletions rails/app/views/dashboard/stories/_stories.html.erb
Original file line number Diff line number Diff line change
@@ -1,10 +1,40 @@
<% @stories.each do |story| %>
<%= link_to story, class: "card" do %>
<div>
<h3>
<span class="card__heading--small"><%= story.topic %></span>
<%= story.title %>
</h3>
<div style="display:flex">
<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">
<i class="icon-pushpin"></i>
</div>
<% end %>
<% if current_user.admin? || current_user.editor? %>
<% if story.story_pinned %>
<div class="edit-icon-pinned">
<%= form_with model: @story, url: story_path(story.id), method: "patch", multipart: true, class: "form", data: {remote: false}, locale: true do |f| %>
<%= f.hidden_field :story_pinned, value: false %>
<%= f.button nil, class: "icon-pinned" do %>
<i class="icon-pushpin"></i>
<% end %>
<% end %>
</div>
<% else %>
<div class="edit-icon-not-pinned">
<%= form_with model: @story, url: story_path(story.id), method: "patch", multipart: true, class: "form", data: {remote: false}, locale: true do |f| %>
<%= f.hidden_field :story_pinned, value: true %>
<%= f.button nil, class: "icon-not-pinned" do %>
<i class="icon-pushpin"></i>
<% end %>
<% end %>
</div>
<% end %>
<% end %>
</div>

<% if story.language.present? %>
<span class="badge badge-red"><%= story.language %></span>
Expand Down
25 changes: 23 additions & 2 deletions rails/app/views/dashboard/stories/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,32 @@
<% end %>

<% content_for(:main_heading) do %>
<h2>
<h2 class="story-main-heading">
<% if @story.topic.present? %>
<div class="small"><%= @story.topic %></div>
<% end %>
<%= @story.title %>
<div class="story-title">
<%= @story.title %>
<%= form_with model: @story, multipart: true, class: "form", data: {remote: false}, locale: true do |f| %>
<% if current_user.admin? || current_user.editor? %>
<% if @story.story_pinned %>
<%= f.hidden_field :story_pinned, value: false %>
<div class="story-pinned">
<%= f.button nil, class: "icon-pinned" do %>
<i class="icon-pushpin"></i>
<% end %>
</div>
<% else %>
<%= f.hidden_field :story_pinned, value: true %>
<div class="story-pinned">
<%= f.button nil, class: "icon-not-pinned" do %>
<i class="icon-pushpin"></i>
<% end %>
</div>
<% end %>
<% end %>
<% end %>
</div>
<div class="small">
<% if @story.language.present? %>
<span class="badge"><%= @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
2 changes: 2 additions & 0 deletions rails/app/views/layouts/dashboard.html.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<!DOCTYPE html>
<html lang="<%= I18n.locale %>">
<head>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.no-icons.min.css" rel="stylesheet">
griseduardo marked this conversation as resolved.
Show resolved Hide resolved
<link href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css" rel="stylesheet">
<meta charset="utf-8">
<meta name="ROBOTS" content="NOODP">
<meta name="viewport" content="width=device-width, initial-scale=1">
Expand Down
2 changes: 2 additions & 0 deletions rails/app/views/layouts/react.html.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.no-icons.min.css" rel="stylesheet">
<link href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css" rel="stylesheet">
<title>Terrastories</title>
<%= favicon_link_tag asset_path('favicon.ico'), :rel => 'icon', :type => 'image/png' %>
<%= csrf_meta_tags %>
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