mirror of
https://github.com/System-End/hackatime.git
synced 2026-04-20 00:35:22 +00:00
fingers crossed this helps a bit
This commit is contained in:
parent
26f3d4e814
commit
4c64cffb1e
9 changed files with 149 additions and 12 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -49,3 +49,4 @@ public/vite-dev
|
|||
public/vite-ssr
|
||||
|
||||
.vite
|
||||
public/vite
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
web: bin/rails server -b 0.0.0.0
|
||||
css: bin/rails tailwindcss:watch
|
||||
vite: bin/vite dev
|
||||
ssr: bin/vite ssr
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class InertiaController < ApplicationController
|
|||
admin_links: inertia_admin_links,
|
||||
viewer_links: inertia_viewer_links,
|
||||
superadmin_links: inertia_superadmin_links,
|
||||
activities_html: inertia_activities_html
|
||||
activities: inertia_activities_props
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -120,9 +120,22 @@ class InertiaController < ApplicationController
|
|||
{ label: label, href: href, active: active, badge: badge }
|
||||
end
|
||||
|
||||
def inertia_activities_html
|
||||
def inertia_activities_props
|
||||
return nil unless defined?(@activities) && @activities.present?
|
||||
helpers.render_activities(@activities)
|
||||
|
||||
@activities.filter_map do |activity|
|
||||
owner = activity.owner
|
||||
next unless owner
|
||||
|
||||
message = inertia_activity_message(activity)
|
||||
next if message.blank?
|
||||
|
||||
{
|
||||
id: activity.id,
|
||||
owner: inertia_user_mention_props(owner),
|
||||
message: message
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def inertia_footer_props
|
||||
|
|
@ -164,7 +177,8 @@ class InertiaController < ApplicationController
|
|||
avatar_url: user.avatar_url,
|
||||
title: title,
|
||||
country_code: user.country_code,
|
||||
country_name: country_name
|
||||
country_name: country_name,
|
||||
impersonate_path: impersonate_path_for(user)
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -182,6 +196,49 @@ class InertiaController < ApplicationController
|
|||
}
|
||||
end
|
||||
|
||||
def inertia_activity_message(activity)
|
||||
params = activity.parameters&.with_indifferent_access || {}
|
||||
project = params[:project].presence
|
||||
|
||||
case activity.key
|
||||
when "user.first_signup"
|
||||
"just signed in for the first time"
|
||||
when "user.first_heartbeat"
|
||||
base = "just started tracking their coding time"
|
||||
project ? "#{base} on #{project}!" : "#{base}!"
|
||||
when "user.started_working"
|
||||
base = "just started working"
|
||||
project ? "#{base} on #{project}" : base
|
||||
when "user.coding_session"
|
||||
base = "just finished coding"
|
||||
duration = helpers.short_time_simple(params[:duration_seconds].to_i)
|
||||
if project
|
||||
"#{base} on #{project} for #{duration}"
|
||||
else
|
||||
"#{base} for #{duration}"
|
||||
end
|
||||
when "physical_mail.mail_sent"
|
||||
mission_type = params[:humanized_mission_type].presence || "a mission"
|
||||
"was just sent a letter for '#{mission_type}'"
|
||||
when "physical_mail.first_streak_achieved"
|
||||
"just hit their first 7 day coding streak!"
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def impersonate_path_for(user)
|
||||
return nil unless current_user
|
||||
return nil unless current_user.admin_level.in?(%w[admin superadmin])
|
||||
return nil if user == current_user
|
||||
return nil if user.admin_level == "superadmin"
|
||||
|
||||
return impersonate_user_path(user) if user.admin_level != "admin"
|
||||
return impersonate_user_path(user) if current_user.admin_level == "superadmin"
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def currently_hacking_props
|
||||
data = Cache::CurrentlyHackingJob.perform_now
|
||||
users = (data[:users] || []).map do |u|
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
title?: string | null;
|
||||
country_code?: string | null;
|
||||
country_name?: string | null;
|
||||
impersonate_path?: string | null;
|
||||
};
|
||||
|
||||
let { user }: { user: NavUserMention } = $props();
|
||||
|
|
@ -35,14 +36,27 @@
|
|||
<span class="inline-flex items-center gap-1">
|
||||
{user.display_name}
|
||||
</span>
|
||||
{#if user.country_code}
|
||||
{@const flagUrl = countryFlagUrl(user.country_code)}
|
||||
{#if flagUrl}
|
||||
<span title={user.country_name || undefined} class="flex items-center">
|
||||
<img
|
||||
src={countryFlagUrl(user.country_code)}
|
||||
src={flagUrl}
|
||||
alt={`${user.country_code} flag`}
|
||||
class="inline-block w-5 h-5 align-middle"
|
||||
loading="lazy"
|
||||
/>
|
||||
</span>
|
||||
{/if}
|
||||
{#if user.impersonate_path}
|
||||
<span class="admin-tool">
|
||||
<a
|
||||
href={user.impersonate_path}
|
||||
class="text-primary font-bold hover:text-red-300 transition-colors duration-200"
|
||||
data-turbo-frame="_top"
|
||||
data-turbo-prefetch="false"
|
||||
>
|
||||
🥸
|
||||
</a>
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { createInertiaApp, type ResolvedComponent } from '@inertiajs/svelte'
|
||||
import { mount } from 'svelte'
|
||||
import { hydrate, mount } from 'svelte'
|
||||
import AppLayout from '../layouts/AppLayout.svelte'
|
||||
|
||||
createInertiaApp({
|
||||
|
|
@ -22,7 +22,9 @@ createInertiaApp({
|
|||
|
||||
setup({ el, App, props }) {
|
||||
if (el) {
|
||||
mount(App, { target: el, props })
|
||||
const hasServerMarkup = el.hasChildNodes()
|
||||
const render = hasServerMarkup ? hydrate : mount
|
||||
render(App, { target: el, props })
|
||||
} else {
|
||||
console.error(
|
||||
'Missing root element.\n\n' +
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
title?: string | null;
|
||||
country_code?: string | null;
|
||||
country_name?: string | null;
|
||||
impersonate_path?: string | null;
|
||||
};
|
||||
|
||||
type NavStreak = {
|
||||
|
|
@ -32,6 +33,12 @@
|
|||
show_super_class?: boolean;
|
||||
};
|
||||
|
||||
type NavActivity = {
|
||||
id: number;
|
||||
owner?: NavUserMentionType | null;
|
||||
message: string;
|
||||
};
|
||||
|
||||
type LayoutNav = {
|
||||
flash: { message: string; class_name: string }[];
|
||||
user_present: boolean;
|
||||
|
|
@ -44,7 +51,7 @@
|
|||
admin_links: NavLink[];
|
||||
viewer_links: NavLink[];
|
||||
superadmin_links: NavLink[];
|
||||
activities_html?: string | null;
|
||||
activities?: NavActivity[] | null;
|
||||
};
|
||||
|
||||
type Footer = {
|
||||
|
|
@ -340,8 +347,17 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
{#if layout.nav.activities_html}
|
||||
<div class="pt-2">{@html layout.nav.activities_html}</div>
|
||||
{#if layout.nav.activities && layout.nav.activities.length > 0}
|
||||
<div class="pt-2 space-y-2">
|
||||
{#each layout.nav.activities as activity (activity.id)}
|
||||
<div class="flex flex-wrap items-center gap-1">
|
||||
{#if activity.owner}
|
||||
<NavUserMention user={activity.owner} />
|
||||
{/if}
|
||||
<span>{activity.message}</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</nav>
|
||||
</div>
|
||||
|
|
|
|||
41
app/javascript/ssr/ssr.ts
Normal file
41
app/javascript/ssr/ssr.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { createInertiaApp, type ResolvedComponent } from '@inertiajs/svelte'
|
||||
import createServer from '@inertiajs/svelte/server'
|
||||
import { render } from 'svelte/server'
|
||||
import AppLayout from '../layouts/AppLayout.svelte'
|
||||
|
||||
createServer((page) =>
|
||||
createInertiaApp({
|
||||
page,
|
||||
|
||||
resolve: (name) => {
|
||||
const pages = import.meta.glob<ResolvedComponent>('../pages/**/*.svelte', {
|
||||
eager: true,
|
||||
})
|
||||
const pageComponent = pages[`../pages/${name}.svelte`]
|
||||
if (!pageComponent) {
|
||||
console.error(`Missing Inertia page component: '${name}.svelte'`)
|
||||
}
|
||||
|
||||
return {
|
||||
default: pageComponent.default,
|
||||
layout: pageComponent.layout || AppLayout,
|
||||
} as ResolvedComponent
|
||||
},
|
||||
|
||||
setup({ App, props }) {
|
||||
return render(App, { props })
|
||||
},
|
||||
|
||||
defaults: {
|
||||
form: {
|
||||
forceIndicesArrayFormatInFormData: false,
|
||||
},
|
||||
future: {
|
||||
useScriptElementForInitialPage: true,
|
||||
useDataInertiaHeadAttribute: true,
|
||||
useDialogForErrorModal: true,
|
||||
preserveEqualProps: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
|
@ -6,4 +6,6 @@ InertiaRails.configure do |config|
|
|||
config.always_include_errors_hash = true
|
||||
config.use_script_element_for_initial_page = true
|
||||
config.use_data_inertia_head_attribute = true
|
||||
config.ssr_enabled = ViteRuby.config.ssr_build_enabled
|
||||
config.ssr_url = ENV.fetch("INERTIA_SSR_URL", "http://127.0.0.1:13714")
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,5 +12,8 @@
|
|||
"autoBuild": true,
|
||||
"publicOutputDir": "vite-test",
|
||||
"port": 3037
|
||||
},
|
||||
"production": {
|
||||
"ssrBuildEnabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue