mirror of
https://github.com/discourse/discourse-rewind.git
synced 2025-07-06 22:02:12 +00:00
DEV: move to an ember component
This commit is contained in:
parent
df21bc6e73
commit
66ec74bf2b
@ -4,9 +4,4 @@ Display stats on your last year of Discourse usage.
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
- add `http://127.0.0.1:4200/rewinds?` in `SiteSetting.allowed_iframes`
|
Navigate to `/my/activity/rewind`
|
||||||
- example embed of a rewind in a post:
|
|
||||||
|
|
||||||
```
|
|
||||||
<iframe src="http://127.0.0.1:4200/rewinds?year=2024&username=j.jaffeux" width="320" height="500" frameborder="0" style="border:0;" allowfullscreen="" aria-hidden="false" tabindex="0" />
|
|
||||||
```
|
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module ::DiscourseRewind
|
|
||||||
class RewindsAssetsController < ::ApplicationController
|
|
||||||
requires_plugin PLUGIN_NAME
|
|
||||||
|
|
||||||
skip_before_action :preload_json, :check_xhr, only: %i[show]
|
|
||||||
skip_before_action :verify_authenticity_token, only: %i[show]
|
|
||||||
|
|
||||||
def show
|
|
||||||
no_cookies
|
|
||||||
|
|
||||||
name = params[:name]
|
|
||||||
path, content_type =
|
|
||||||
if name == "rewind"
|
|
||||||
%w[rewind.css text/css]
|
|
||||||
else
|
|
||||||
raise Discourse::NotFound
|
|
||||||
end
|
|
||||||
|
|
||||||
content = File.read(DiscourseRewind.public_asset_path("css/#{path}"))
|
|
||||||
|
|
||||||
# note, path contains a ":version" which automatically busts the cache
|
|
||||||
# based on file content, so this is safe
|
|
||||||
response.headers["Last-Modified"] = 10.years.ago.httpdate
|
|
||||||
response.headers["Content-Length"] = content.bytesize.to_s
|
|
||||||
immutable_for 1.year
|
|
||||||
|
|
||||||
render plain: content, disposition: :nil, content_type: content_type
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -4,16 +4,11 @@ module ::DiscourseRewind
|
|||||||
class RewindsController < ::ApplicationController
|
class RewindsController < ::ApplicationController
|
||||||
requires_plugin PLUGIN_NAME
|
requires_plugin PLUGIN_NAME
|
||||||
|
|
||||||
skip_before_action :preload_json, :check_xhr, :redirect_to_login_if_required, only: %i[show]
|
|
||||||
|
|
||||||
def show
|
def show
|
||||||
# expires_in 1.minute, public: false
|
|
||||||
response.headers["X-Robots-Tag"] = "noindex"
|
|
||||||
|
|
||||||
DiscourseRewind::Rewind::Fetch.call(service_params) do
|
DiscourseRewind::Rewind::Fetch.call(service_params) do
|
||||||
on_success do |reports:|
|
on_success do |reports:|
|
||||||
@reports = reports
|
@reports = reports
|
||||||
render "show", layout: false
|
render json: MultiJson.dump(reports), status: 200
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module DiscourseRewind
|
|
||||||
module RewindsHelper
|
|
||||||
# keeping it here for caching
|
|
||||||
def self.rewind_asset_url(asset_name)
|
|
||||||
if !%w[rewind.css].include?(asset_name)
|
|
||||||
raise StandardError, "unknown asset type #{asset_name}"
|
|
||||||
end
|
|
||||||
|
|
||||||
@urls ||= {}
|
|
||||||
url = @urls[asset_name]
|
|
||||||
return url if url
|
|
||||||
|
|
||||||
content = File.read(DiscourseRewind.public_asset_path("css/#{asset_name}"))
|
|
||||||
sha1 = Digest::SHA1.hexdigest(content)
|
|
||||||
url = "/rewinds/assets/#{sha1}/#{asset_name}"
|
|
||||||
@urls[asset_name] = GlobalPath.cdn_path(url)
|
|
||||||
end
|
|
||||||
|
|
||||||
def rewind_asset_url(asset_name)
|
|
||||||
DiscourseRewind::RewindsHelper.rewind_asset_url(asset_name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,7 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module DiscourseRewind
|
module DiscourseRewind
|
||||||
class Rewind::Action::Base < Service::ActionBase
|
class Rewind::Action::BaseReport < Service::ActionBase
|
||||||
option :user
|
option :user
|
||||||
option :date
|
option :date
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ module DiscourseRewind
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
def enabled?
|
def self.enabled?
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -3,7 +3,7 @@
|
|||||||
# Forum Best Friend Forever ranking
|
# Forum Best Friend Forever ranking
|
||||||
# Score is informative only, do not show in UI
|
# Score is informative only, do not show in UI
|
||||||
module DiscourseRewind
|
module DiscourseRewind
|
||||||
class Rewind::Action::Fbff < Action::Base
|
class Rewind::Action::Fbff < Rewind::Action::BaseReport
|
||||||
MAX_SUMMARY_RESULTS = 50
|
MAX_SUMMARY_RESULTS = 50
|
||||||
LIKE_SCORE = 1
|
LIKE_SCORE = 1
|
||||||
REPLY_SCORE = 10
|
REPLY_SCORE = 10
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
# For a GitHub like calendar
|
# For a GitHub like calendar
|
||||||
# https://docs.github.com/assets/cb-35216/mw-1440/images/help/profile/contributions-graph.webp
|
# https://docs.github.com/assets/cb-35216/mw-1440/images/help/profile/contributions-graph.webp
|
||||||
module DiscourseRewind
|
module DiscourseRewind
|
||||||
class Rewind::Action::PostingCalendar < Action::Base
|
class Rewind::Action::PostingCalendar < Rewind::Action::BaseReport
|
||||||
def call
|
def call
|
||||||
calendar =
|
calendar =
|
||||||
Post
|
Post
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# For a most user / received reactions cards
|
# For a most user / received reactions cards
|
||||||
module DiscourseRewind
|
module DiscourseRewind
|
||||||
class Rewind::Action::Reactions < Action::Base
|
class Rewind::Action::Reactions < Rewind::Action::BaseReport
|
||||||
def call
|
def call
|
||||||
post_used_reactions = {}
|
post_used_reactions = {}
|
||||||
post_received_reactions = {}
|
post_received_reactions = {}
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
# For showcasing the reading time of a user
|
# For showcasing the reading time of a user
|
||||||
# Should we show book covers or just the names?
|
# Should we show book covers or just the names?
|
||||||
module DiscourseRewind
|
module DiscourseRewind
|
||||||
class Rewind::Action::ReadingTime < Action::Base
|
class Rewind::Action::ReadingTime < Rewind::Action::BaseReport
|
||||||
def call
|
def call
|
||||||
reading_time = UserVisit.where(user: user).where(visited_at: date).sum(:time_read)
|
reading_time = UserVisit.where(user_id: user.id).where(visited_at: date).sum(:time_read)
|
||||||
|
|
||||||
{
|
{
|
||||||
data: {
|
data: {
|
||||||
@ -38,7 +38,7 @@ module DiscourseRewind
|
|||||||
"And Then There Were None" => 16_200,
|
"And Then There Were None" => 16_200,
|
||||||
"The Alchemist" => 10_800,
|
"The Alchemist" => 10_800,
|
||||||
"The Hitchhiker's Guide to the Galaxy" => 12_600,
|
"The Hitchhiker's Guide to the Galaxy" => 12_600,
|
||||||
}
|
}.symbolize_keys
|
||||||
end
|
end
|
||||||
|
|
||||||
def best_book_fit(reading_time)
|
def best_book_fit(reading_time)
|
||||||
|
@ -5,11 +5,7 @@ module DiscourseRewind
|
|||||||
#
|
#
|
||||||
# @example
|
# @example
|
||||||
# ::DiscourseRewind::Rewind::Fetch.call(
|
# ::DiscourseRewind::Rewind::Fetch.call(
|
||||||
# guardian: guardian,
|
# guardian: guardian
|
||||||
# params: {
|
|
||||||
# username: "falco",
|
|
||||||
# year: 2024,
|
|
||||||
# }
|
|
||||||
# )
|
# )
|
||||||
#
|
#
|
||||||
class Rewind::Fetch
|
class Rewind::Fetch
|
||||||
@ -22,18 +18,9 @@ module DiscourseRewind
|
|||||||
# @option params [Integer] :username of the rewind
|
# @option params [Integer] :username of the rewind
|
||||||
# @return [Service::Base::Context]
|
# @return [Service::Base::Context]
|
||||||
|
|
||||||
# Do we need a custom order?
|
|
||||||
available_reports = DiscourseRewind::Rewind::Action::Base.descendants
|
|
||||||
|
|
||||||
CACHE_DURATION = 5.minutes
|
CACHE_DURATION = 5.minutes
|
||||||
|
|
||||||
params do
|
YEAR = 2024
|
||||||
attribute :year, :integer
|
|
||||||
attribute :username, :string
|
|
||||||
|
|
||||||
validates :year, presence: true
|
|
||||||
validates :username, presence: true
|
|
||||||
end
|
|
||||||
|
|
||||||
model :user
|
model :user
|
||||||
model :date
|
model :date
|
||||||
@ -41,23 +28,24 @@ module DiscourseRewind
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def fetch_user(params:)
|
def fetch_user(guardian:)
|
||||||
User.find_by_username(params.username)
|
User.find_by_username(guardian.user.username)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_date(params:)
|
def fetch_date(params:)
|
||||||
Date.new(params.year).all_year
|
Date.new(YEAR).all_year
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_reports(params:, date:, user:, guardian:)
|
def fetch_reports(date:, user:, guardian:)
|
||||||
key = "rewind:#{params.username}:#{params.year}"
|
key = "rewind:#{guardian.user.username}:#{YEAR}"
|
||||||
reports = Discourse.redis.get(key)
|
reports = Discourse.redis.get(key)
|
||||||
|
|
||||||
if Rails.env.development? || !reports
|
if Rails.env.development? || !reports
|
||||||
reports =
|
reports =
|
||||||
available_reports
|
::DiscourseRewind::Rewind::Action::BaseReport
|
||||||
|
.descendants
|
||||||
.filter { _1.enabled? }
|
.filter { _1.enabled? }
|
||||||
.map { |report| report.call(params:, date:, user:, guardian:) }
|
.map { |report| report.call(date:, user:, guardian:) }
|
||||||
Discourse.redis.setex(key, CACHE_DURATION, MultiJson.dump(reports))
|
Discourse.redis.setex(key, CACHE_DURATION, MultiJson.dump(reports))
|
||||||
else
|
else
|
||||||
reports = MultiJson.load(reports, symbolize_keys: true)
|
reports = MultiJson.load(reports, symbolize_keys: true)
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title><%= I18n.t("discourse_rewind.title", title: "RETRO TITLE", site_name: SiteSetting.title) %></title>
|
|
||||||
<meta property="og:title" content="<%= I18n.t("discourse_rewind.title", title: "RETRO TITLE", site_name: SiteSetting.title) %>">
|
|
||||||
<meta property="og:description" content="DESC">
|
|
||||||
<meta property="og:type" content="website">
|
|
||||||
<meta property="og:url" content="<%= request.original_url %>">
|
|
||||||
<meta name="twitter:card" content="summary">
|
|
||||||
<meta name="twitter:title" content="<%= I18n.t("discourse_rewind.title", title: "RETRO TITLE", site_name: SiteSetting.title) %>">
|
|
||||||
<meta name="twitter:description" content="DESC">
|
|
||||||
<meta name="viewport" content="width=device-width">
|
|
||||||
<link rel="stylesheet" href="<%= rewind_asset_url("rewind.css") %>">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="rewind__wrapper">
|
|
||||||
<div class="rewind">
|
|
||||||
<%- @reports.each do |report| %>
|
|
||||||
<div class="rewind__report">
|
|
||||||
<div class="rewind__report__header">
|
|
||||||
<%= report[:identifier] %>
|
|
||||||
</div>
|
|
||||||
<div class="rewind__report__body">
|
|
||||||
<%= report[:data][:count] %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<%- end %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
27
assets/javascripts/discourse/components/rewind.gjs
Normal file
27
assets/javascripts/discourse/components/rewind.gjs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
|
||||||
|
export default class Rewind extends Component {
|
||||||
|
@tracked rewind = [];
|
||||||
|
|
||||||
|
@action
|
||||||
|
async loadRewind() {
|
||||||
|
try {
|
||||||
|
this.rewind = await ajax("/rewinds");
|
||||||
|
} catch (e) {
|
||||||
|
popupAjaxError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="rewind" {{didInsert this.loadRewind}}>
|
||||||
|
{{#each this.rewind as |report|}}
|
||||||
|
<p>{{report.identifier}}</p>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import DNavigationItem from "discourse/components/d-navigation-item";
|
||||||
|
import icon from "discourse-common/helpers/d-icon";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
|
||||||
|
export default class RewindTab extends Component {
|
||||||
|
<template>
|
||||||
|
<DNavigationItem
|
||||||
|
@route="userActivity.rewind"
|
||||||
|
@ariaCurrentContext="subNav"
|
||||||
|
class="user-nav__activity-rewind"
|
||||||
|
>
|
||||||
|
{{icon "repeat"}}
|
||||||
|
<span>{{i18n "rewind.title"}}</span>
|
||||||
|
</DNavigationItem>
|
||||||
|
</template>
|
||||||
|
}
|
11
assets/javascripts/discourse/rewind-route-map.js
Normal file
11
assets/javascripts/discourse/rewind-route-map.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export default function () {
|
||||||
|
this.route(
|
||||||
|
"user",
|
||||||
|
{ path: "/u/:username", resetNamespace: true },
|
||||||
|
function () {
|
||||||
|
this.route("userActivity", function () {
|
||||||
|
this.route("rewind");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
|
||||||
|
export default class UserActivityRewind extends DiscourseRoute {
|
||||||
|
templateName = "user/rewind";
|
||||||
|
}
|
1
assets/javascripts/discourse/templates/user/rewind.hbs
Normal file
1
assets/javascripts/discourse/templates/user/rewind.hbs
Normal file
@ -0,0 +1 @@
|
|||||||
|
<Rewind />
|
1
assets/stylesheets/common/index.scss
Normal file
1
assets/stylesheets/common/index.scss
Normal file
@ -0,0 +1 @@
|
|||||||
|
@import "rewind";
|
6
assets/stylesheets/common/rewind.scss
Normal file
6
assets/stylesheets/common/rewind.scss
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.rewind {
|
||||||
|
width: 280px;
|
||||||
|
height: 400px;
|
||||||
|
background-color: var(--secondary-high);
|
||||||
|
border-radius: var(--d-border-radius);
|
||||||
|
}
|
0
assets/stylesheets/desktop/index.scss
Normal file
0
assets/stylesheets/desktop/index.scss
Normal file
0
assets/stylesheets/mobile/index.scss
Normal file
0
assets/stylesheets/mobile/index.scss
Normal file
@ -1,8 +1,5 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
DiscourseRewind::Engine.routes.draw do
|
DiscourseRewind::Engine.routes.draw { get "/rewinds" => "rewinds#show" }
|
||||||
get "/rewinds" => "rewinds#show"
|
|
||||||
get "/rewinds/assets/:version/:name" => "rewinds_assets#show"
|
|
||||||
end
|
|
||||||
|
|
||||||
Discourse::Application.routes.draw { mount ::DiscourseRewind::Engine, at: "/" }
|
Discourse::Application.routes.draw { mount ::DiscourseRewind::Engine, at: "/" }
|
||||||
|
@ -9,5 +9,11 @@ module ::DiscourseRewind
|
|||||||
config.to_prepare do
|
config.to_prepare do
|
||||||
Rails.autoloaders.main.eager_load_dir(scheduled_job_dir) if Dir.exist?(scheduled_job_dir)
|
Rails.autoloaders.main.eager_load_dir(scheduled_job_dir) if Dir.exist?(scheduled_job_dir)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Rails.application.reloader.to_prepare do
|
||||||
|
Dir[
|
||||||
|
"#{Rails.root}/plugins/discourse-rewind/app/services/discourse_rewind/rewind/action/*.rb"
|
||||||
|
].each { |file| require_dependency file }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -10,6 +10,12 @@
|
|||||||
|
|
||||||
enabled_site_setting :discourse_rewind_enabled
|
enabled_site_setting :discourse_rewind_enabled
|
||||||
|
|
||||||
|
register_svg_icon "repeat"
|
||||||
|
|
||||||
|
register_asset "stylesheets/common/index.scss"
|
||||||
|
register_asset "stylesheets/desktop/index.scss", :desktop
|
||||||
|
register_asset "stylesheets/mobile/index.scss", :mobile
|
||||||
|
|
||||||
module ::DiscourseRewind
|
module ::DiscourseRewind
|
||||||
PLUGIN_NAME = "discourse-rewind"
|
PLUGIN_NAME = "discourse-rewind"
|
||||||
|
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
/* RESET CSS */
|
|
||||||
*, *::before, *::after { box-sizing: border-box; }
|
|
||||||
* { margin: 0; padding: 0; }
|
|
||||||
ul[role='list'], ol[role='list'] { list-style: none; }
|
|
||||||
html:focus-within { scroll-behavior: smooth; }
|
|
||||||
a:not([class]) { text-decoration-skip-ink: auto; }
|
|
||||||
input, button, textarea, select { font: inherit; }
|
|
||||||
body, html { height: 100%; scroll-behavior: smooth; }
|
|
||||||
/* END RESET CSS */
|
|
||||||
|
|
||||||
.rewind {
|
|
||||||
display: flex;
|
|
||||||
height: 100vh;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user