mirror of
https://github.com/discourse/discourse-rewind.git
synced 2025-07-06 13:52:10 +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
|
||||
|
||||
- add `http://127.0.0.1:4200/rewinds?` in `SiteSetting.allowed_iframes`
|
||||
- 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" />
|
||||
```
|
||||
Navigate to `/my/activity/rewind`
|
||||
|
@ -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
|
||||
requires_plugin PLUGIN_NAME
|
||||
|
||||
skip_before_action :preload_json, :check_xhr, :redirect_to_login_if_required, only: %i[show]
|
||||
|
||||
def show
|
||||
# expires_in 1.minute, public: false
|
||||
response.headers["X-Robots-Tag"] = "noindex"
|
||||
|
||||
DiscourseRewind::Rewind::Fetch.call(service_params) do
|
||||
on_success do |reports:|
|
||||
@reports = reports
|
||||
render "show", layout: false
|
||||
render json: MultiJson.dump(reports), status: 200
|
||||
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
|
||||
|
||||
module DiscourseRewind
|
||||
class Rewind::Action::Base < Service::ActionBase
|
||||
class Rewind::Action::BaseReport < Service::ActionBase
|
||||
option :user
|
||||
option :date
|
||||
|
||||
@ -9,7 +9,7 @@ module DiscourseRewind
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def enabled?
|
||||
def self.enabled?
|
||||
true
|
||||
end
|
||||
end
|
@ -3,7 +3,7 @@
|
||||
# Forum Best Friend Forever ranking
|
||||
# Score is informative only, do not show in UI
|
||||
module DiscourseRewind
|
||||
class Rewind::Action::Fbff < Action::Base
|
||||
class Rewind::Action::Fbff < Rewind::Action::BaseReport
|
||||
MAX_SUMMARY_RESULTS = 50
|
||||
LIKE_SCORE = 1
|
||||
REPLY_SCORE = 10
|
||||
|
@ -3,7 +3,7 @@
|
||||
# For a GitHub like calendar
|
||||
# https://docs.github.com/assets/cb-35216/mw-1440/images/help/profile/contributions-graph.webp
|
||||
module DiscourseRewind
|
||||
class Rewind::Action::PostingCalendar < Action::Base
|
||||
class Rewind::Action::PostingCalendar < Rewind::Action::BaseReport
|
||||
def call
|
||||
calendar =
|
||||
Post
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
# For a most user / received reactions cards
|
||||
module DiscourseRewind
|
||||
class Rewind::Action::Reactions < Action::Base
|
||||
class Rewind::Action::Reactions < Rewind::Action::BaseReport
|
||||
def call
|
||||
post_used_reactions = {}
|
||||
post_received_reactions = {}
|
||||
|
@ -3,9 +3,9 @@
|
||||
# For showcasing the reading time of a user
|
||||
# Should we show book covers or just the names?
|
||||
module DiscourseRewind
|
||||
class Rewind::Action::ReadingTime < Action::Base
|
||||
class Rewind::Action::ReadingTime < Rewind::Action::BaseReport
|
||||
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: {
|
||||
@ -38,7 +38,7 @@ module DiscourseRewind
|
||||
"And Then There Were None" => 16_200,
|
||||
"The Alchemist" => 10_800,
|
||||
"The Hitchhiker's Guide to the Galaxy" => 12_600,
|
||||
}
|
||||
}.symbolize_keys
|
||||
end
|
||||
|
||||
def best_book_fit(reading_time)
|
||||
|
@ -5,11 +5,7 @@ module DiscourseRewind
|
||||
#
|
||||
# @example
|
||||
# ::DiscourseRewind::Rewind::Fetch.call(
|
||||
# guardian: guardian,
|
||||
# params: {
|
||||
# username: "falco",
|
||||
# year: 2024,
|
||||
# }
|
||||
# guardian: guardian
|
||||
# )
|
||||
#
|
||||
class Rewind::Fetch
|
||||
@ -22,18 +18,9 @@ module DiscourseRewind
|
||||
# @option params [Integer] :username of the rewind
|
||||
# @return [Service::Base::Context]
|
||||
|
||||
# Do we need a custom order?
|
||||
available_reports = DiscourseRewind::Rewind::Action::Base.descendants
|
||||
|
||||
CACHE_DURATION = 5.minutes
|
||||
|
||||
params do
|
||||
attribute :year, :integer
|
||||
attribute :username, :string
|
||||
|
||||
validates :year, presence: true
|
||||
validates :username, presence: true
|
||||
end
|
||||
YEAR = 2024
|
||||
|
||||
model :user
|
||||
model :date
|
||||
@ -41,23 +28,24 @@ module DiscourseRewind
|
||||
|
||||
private
|
||||
|
||||
def fetch_user(params:)
|
||||
User.find_by_username(params.username)
|
||||
def fetch_user(guardian:)
|
||||
User.find_by_username(guardian.user.username)
|
||||
end
|
||||
|
||||
def fetch_date(params:)
|
||||
Date.new(params.year).all_year
|
||||
Date.new(YEAR).all_year
|
||||
end
|
||||
|
||||
def fetch_reports(params:, date:, user:, guardian:)
|
||||
key = "rewind:#{params.username}:#{params.year}"
|
||||
def fetch_reports(date:, user:, guardian:)
|
||||
key = "rewind:#{guardian.user.username}:#{YEAR}"
|
||||
reports = Discourse.redis.get(key)
|
||||
|
||||
if Rails.env.development? || !reports
|
||||
reports =
|
||||
available_reports
|
||||
::DiscourseRewind::Rewind::Action::BaseReport
|
||||
.descendants
|
||||
.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))
|
||||
else
|
||||
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
|
||||
|
||||
DiscourseRewind::Engine.routes.draw do
|
||||
get "/rewinds" => "rewinds#show"
|
||||
get "/rewinds/assets/:version/:name" => "rewinds_assets#show"
|
||||
end
|
||||
DiscourseRewind::Engine.routes.draw { get "/rewinds" => "rewinds#show" }
|
||||
|
||||
Discourse::Application.routes.draw { mount ::DiscourseRewind::Engine, at: "/" }
|
||||
|
@ -9,5 +9,11 @@ module ::DiscourseRewind
|
||||
config.to_prepare do
|
||||
Rails.autoloaders.main.eager_load_dir(scheduled_job_dir) if Dir.exist?(scheduled_job_dir)
|
||||
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
|
||||
|
@ -10,6 +10,12 @@
|
||||
|
||||
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
|
||||
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