The app becomes a Rails 8 application; the Vue frontend moves to app/frontend and is served through vite_rails. - Without an account documents stay in the browser (IndexedDB). Sign-in is by magic link or the 6-digit code from the same mail; afterwards local documents can be uploaded into the account. - Server documents (/d/<id>) with versioned saves and a single-editor lock per browser tab; others see them read-only and follow by polling. - Sharing via link (works without an account) and via e-mail invitation, roles viewer/editor; the owner manages links and people. - Store gains a read-only mode that blocks all content changes. - Solid Queue, Cache and Cable share the one PostgreSQL database. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
29 lines
1.0 KiB
Ruby
29 lines
1.0 KiB
Ruby
class User < ApplicationRecord
|
|
COLORS = %w[#e67e22 #2980b9 #27ae60 #8e44ad #c0392b #16a085 #d35400 #2c3e50].freeze
|
|
|
|
has_many :sessions, dependent: :destroy
|
|
has_many :owned_documents, class_name: "Document", foreign_key: :owner_id, dependent: :destroy, inverse_of: :owner
|
|
has_many :document_memberships, dependent: :destroy
|
|
has_many :shared_documents, through: :document_memberships, source: :document
|
|
|
|
normalizes :email, with: ->(e) { e.strip.downcase }
|
|
validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }
|
|
validates :display_name, length: { maximum: 80 }
|
|
|
|
before_create { self.color ||= COLORS.sample }
|
|
|
|
# Name shown to others (share dialog, lock holder, later: cursors).
|
|
def name
|
|
display_name.presence || email.split("@").first
|
|
end
|
|
|
|
# Documents this user owns or has been given access to.
|
|
def accessible_documents
|
|
Document.where(owner_id: id).or(Document.where(id: document_memberships.select(:document_id)))
|
|
end
|
|
|
|
def as_json(*)
|
|
{ id:, email:, name:, display_name:, color: }
|
|
end
|
|
end
|