Skip to conversation
Back to public feed

Test CodeReview

Contributing to Devdevian

Thanks for helping improve Devdevian. This is an early development project; focused changes are easier to review and validate.

Before starting

For a bug, describe the page, exact steps, expected result, actual result, browser, and viewport. Remove personal data from screenshots. For a significant feature or architectural change, discuss the proposed behavior in an issue before implementing it.

Follow the code of conduct.

Do not publish security vulnerabilities in public issues. Follow SECURITY.md.

Local setup

Follow the development guide and architecture overview. Use an isolated DB_PATH or npm run demo when experimenting with account deletion, moderation, or migrations. Never commit environment files, databases, uploads, session cookies, OAuth tokens, or production exports.

Implementation guidelines

  • Keep the blue/violet palette and support both themes.
  • Check permissions on the server; hiding a button does not protect a route.
  • Preserve the distinction between public topics and private communities.
  • Keep lists bounded and paginated. Respect private access in search, notifications, profiles, and uploads.
  • Validate input, use bound SQL parameters, and use transactions for related writes.
  • Preserve existing data during migrations. Document storage or configuration changes.
  • Add comments for non-obvious constraints, not for every line.
  • Keep interfaces usable with keyboard navigation, small touch screens, tablets, and reduced motion.

Validation

Run npm test and npm run build. For affected UI flows, also run npm run test:browser and inspect screenshots. Add a regression check when the change affects permissions, identity, persistence, or another behavior that is not reliably verified visually.

Pull requests

Explain the problem, the resulting behavior, and how you verified it. Attach relevant light/dark and mobile screenshots for visual changes. Identify migrations, configuration changes, and known limitations. Keep unrelated refactors separate.

By contributing, you agree that your contribution is licensed under GPL-3.0-only. Do not submit code or assets you do not have permission to distribute.

Published snippet
import { notifyMentions } from "./mentions.mjs";
import { notificationAllowed } from "./preferences.mjs";
import { scopeSQL, pageRows } from "./spaces.mjs";
// In-app activity only. No email, push service, or external delivery.
export function transaction(db, work) {
 
export function notify(
  db,
  { recipient, actor, post = null, event = null, reply = null, type, key },
) {
  if (
    !recipient ||
    recipient === actor ||
    !notificationAllowed(db, recipient, type)
  )
    return;
  db.prepare(
    `INSERT OR IGNORE INTO notifications(recipient_id,actor_id,post_id,event_id,reply_id,type,event_key) VALUES(?,?,?,?,?,?,?)`,
  ).run(recipient, actor, post, event, reply, type, key);
}
// Fan out in one statement so a popular topic does not issue one remote query per follower.
export function notifySubscribers(
  db,
  { kind, item, actor, post, reply = null, type, key },
) {
  db.prepare(
    `INSERT OR IGNORE INTO notifications(recipient_id,actor_id,post_id,reply_id,type,event_key)
    SELECT s.user_id,?,?,?,?,? FROM subscriptions s
    JOIN users u ON u.id=s.user_id JOIN posts p ON p.id=?
    LEFT JOIN user_settings settings ON settings.user_id=s.user_id
    WHERE s.kind=? AND s.item=? AND s.user_id<>? AND u.deleted_at IS NULL AND p.removed_at IS NULL
    AND (p.community_id IS NULL OR EXISTS(SELECT 1 FROM memberships m WHERE m.user_id=s.user_id AND m.kind='group' AND m.item=p.community_id))
    AND COALESCE(json_extract(settings.data,'$.notifications_enabled'),1)=1
    AND COALESCE(json_extract(settings.data,'$.notify_subscriptions'),1)=1
    AND (?<>'reply' OR COALESCE(json_extract(settings.data,'$.notify_replies'),1)=1)`,
  ).run(actor, post, reply, type, key, post, kind, String(item), actor, type);
}
export function notifyReply(db, post, replyId, actor, parentId) {
  notifySubscribers(db, {
    kind: "post",
    item: post.id,
    actor,
    post: post.id,
    reply: replyId,
    type: "reply",
    key: `reply:${replyId}`,
  });
  const recipients = new Set([post.user_id]);
  if (parentId)
    recipients.add(
      db
        .prepare("SELECT user_id FROM replies WHERE id=? AND post_id=?")
        .get(parentId, post.id)?.user_id,
    );
  for (const recipient of recipients)
    notify(db, {
      recipient,
      actor,
      post: post.id,
      reply: replyId,
      type: "reply",
      key: `reply:${replyId}`,
    });
}
function text(value, max, min = 0) {
  return (
    typeof value === "string" &&
    value.trim().length >= min &&
    value.length <= max
  );
}
JavaScriptNode.jsPostgreSQL

The conversation

0 replies

Bring a fresh perspective.

Your reply could be the start of a useful conversation.

Add your perspective.

Sign in to reply with your Devdevian account. You’ll return to this conversation.