trust factor updates and more (#263)

Co-authored-by: ImgBotApp <ImgBotHelp@gmail.com>
This commit is contained in:
Echo 2025-05-29 17:40:26 -04:00 committed by GitHub
parent 433aeac3ac
commit f7f2be553c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 71 additions and 44 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -34,7 +34,7 @@
width: 8px;
height: 8px;
border-radius: 50%;
background-color: var(--primary-color);
background-color: #4caf50;
display: inline-block;
margin-right: 0.2em;
animation: pulse 2s infinite;
@ -100,4 +100,4 @@
.currently-hacking-header {
color: white;
}
}
}

View file

@ -21,6 +21,14 @@ module ApplicationHelper
country_code.tr("A-Z", "\u{1F1E6}-\u{1F1FF}")
end
# infer country from timezone
def timezone_to_country(timezone)
return null unless timezone.present?
tz = ActiveSupport::TimeZone[timezone]
return null unless tz && tz.tzinfo.respond_to?(:country_code)
tz.tzinfo.country_code || null
end
def timezone_difference_in_seconds(timezone1, timezone2)
return 0 if timezone1 == timezone2

View file

@ -1,57 +1,62 @@
import { Controller } from "@hotwired/stimulus"
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
static targets = ["select"]
static targets = ["select"];
connect() {
this.selectTarget.addEventListener('change', this.handleChange.bind(this))
this.selectTarget.addEventListener("change", this.handleChange.bind(this));
}
async handleChange(event) {
const userId = event.target.dataset.userId
const trustLevel = event.target.value
const userId = event.target.dataset.userId;
const trustLevel = event.target.value;
if (!userId) {
console.error('No user ID found in dataset')
event.target.value = event.target.dataset.currentTrustLevel
alert('Error: No user ID found. Please try again.')
return
console.error("No user ID found in dataset");
event.target.value = event.target.dataset.currentTrustLevel;
alert("Error: No user ID found. Please try again.");
return;
}
try {
console.log('Updating trust level for user:', userId, 'to:', trustLevel)
const url = new URL(`/users/${userId}/update_trust_level`, window.location.origin)
console.log("Updating trust level for user:", userId, "to:", trustLevel);
const url = new URL(
`/users/${userId}/update_trust_level`,
window.location.origin
);
const response = await fetch(url, {
method: 'PATCH',
method: "PATCH",
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
"Content-Type": "application/json",
"X-CSRF-Token": document.querySelector('meta[name="csrf-token"]')
.content,
},
body: JSON.stringify({ trust_level: trustLevel })
})
body: JSON.stringify({ trust_level: trustLevel }),
});
if (!response.ok) {
throw new Error(`Failed to update trust level: ${response.status} ${response.statusText}`)
throw new Error(
`Failed to update trust level: ${response.status} ${response.statusText}`
);
}
// Update the current trust level in the dataset
event.target.dataset.currentTrustLevel = trustLevel
event.target.dataset.currentTrustLevel = trustLevel;
// Update the leaderboard entry's omitted class
const leaderboardEntry = event.target.closest('.leaderboard-entry')
const leaderboardEntry = event.target.closest(".leaderboard-entry");
if (leaderboardEntry) {
if (trustLevel === 'untrusted') {
leaderboardEntry.classList.add('omitted')
if (trustLevel === "red") {
leaderboardEntry.classList.add("omitted");
} else {
leaderboardEntry.classList.remove('omitted')
leaderboardEntry.classList.remove("omitted");
}
}
} catch (error) {
console.error('Error updating trust level:', error)
console.error("Error updating trust level:", error);
// Revert the select to its previous value
event.target.value = event.target.dataset.currentTrustLevel
alert('Failed to update trust level. Please try again.')
event.target.value = event.target.dataset.currentTrustLevel;
alert("Failed to update trust level. Please try again.");
}
}
}
}

View file

@ -16,10 +16,17 @@ class User < ApplicationRecord
end
enum :trust_level, {
default: 0,
untrusted: 1,
trusted: 2
yellow: 0,
red: 1,
green: 2
}
# yellow is unscored, red being convicted while green being trusted
# labels make it easier for display :okay-1:
def set_trust(level)
update!(trust_level: level)
end
# ex: .set_trust(:green) or set_trust(1) setting it to red
has_many :heartbeats
has_many :email_addresses, dependent: :destroy

View file

@ -22,9 +22,6 @@
Hackatime is totally free. Anyone can see the <a href="https://github.com/hackclub/hackatime" target="_blank">code</a>. It's like <a href="https://wakatime.com" target="_blank">WakaTime</a> but free and open source.
</p>
<h3>🎯 How to Start</h3>
<ol>
<li><strong>Make an account</strong> at <a href="<%= root_url %>">hackatime.hackclub.com</a></li>
@ -33,10 +30,11 @@
<li><strong>Start coding</strong>: It tracks your time by itself!</li>
</ol>
<p style="background: #f0f8ff; padding: 1rem; border-radius: 8px; border-left: 4px solid var(--primary);">
<p id="tip-box" class="tip-box" style="background: #f0f8ff; padding: 1rem; border-radius: 8px; border-left: 4px solid var(--primary);">
<strong>💡 Tip:</strong> The <a href="<%= my_wakatime_setup_path %>">setup page</a> does everything for you. No hard stuff to figure out!
</p>
<h3>🔌 What Code Editors Work</h3>
<p>
Hackatime works with <strong>any editor</strong> that has <a href="https://wakatime.com" target="_blank">WakaTime</a>. Just add the WakaTime plugin to your editor:
@ -55,7 +53,7 @@
<div>
<strong>Lots More:</strong>
<ul style="margin-top: 0.5rem;">
<li><%= link_to "Atom", doc_path("editors/atom") %>, <%= link_to "Brackets", doc_path("editors/brackets") %></li>
<li><%= link_to "Brackets", doc_path("editors/brackets") %></li>
<li>All JetBrains apps</li>
<li><%= link_to "Command Line", doc_path("editors/terminal") %></li>
<li><a href="#supported-editors" onclick="document.getElementById('all-editors').parentElement.open = true; document.getElementById('all-editors').scrollIntoView({behavior: 'smooth'});">70+ others</a></li>
@ -226,6 +224,16 @@
<% end %>
</div>
</details>
<style>
@media (prefers-color-scheme: dark) {
.tip-box, #all-editors {
background: #1a2233 !important;
border-left-color: #4fd1c5 !important;
color: #e6e6e6 !important;
}
.tip-box a { color: #4fd1c5 !important; }
}
</style>
<hr style="margin: 2rem 0;">

View file

@ -46,7 +46,8 @@
<% if @entries&.any? %>
<div class="leaderboard-entries">
<% @entries.each_with_index do |entry, index| %>
<div class="leaderboard-entry <%= 'current-user' if entry.user_id == current_user&.id %> <%= 'omitted' if entry.user.untrusted? && current_user&.admin? %>">
<div class="leaderboard-entry <%= 'current-user' if entry.user_id == current_user&.id %> <%= 'omitted' if entry.user.red? && current_user&.admin? %>">
<!-- in the future, convicted users will be hidden from public leaderboards -->
<span class="rank">
<% case index %>
<% when 0 then %>
@ -86,9 +87,9 @@
data-trust-level-target="select"
data-user-id="<%= entry.user.id %>"
data-current-trust-level="<%= entry.user.trust_level %>">
<option value="default" <%= 'selected' if entry.user.default? %>>Default</option>
<option value="untrusted" <%= 'selected' if entry.user.untrusted? %>>Untrusted</option>
<option value="trusted" <%= 'selected' if entry.user.trusted? %>>Trusted</option>
<option value="yellow" <%= 'selected' if entry.user.yellow? %>>Yellow</option>
<option value="red" <%= 'selected' if entry.user.red? %>>Red</option>
<option value="green" <%= 'selected' if entry.user.green? %>>Green</option>
</select>
<% end %>
</div>

View file

@ -1,3 +1 @@
<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg">
<circle cx="256" cy="256" r="256" fill="red"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512"><circle cx="256" cy="256" r="256" fill="red"/></svg>

Before

Width:  |  Height:  |  Size: 122 B

After

Width:  |  Height:  |  Size: 117 B