Merge branch 'master' into feature/rs-oauth
This commit is contained in:
@@ -5,10 +5,4 @@
|
||||
&:visited { @apply text-indigo-600; }
|
||||
&:active { @apply text-red-600; }
|
||||
}
|
||||
|
||||
.devise-links {
|
||||
a {
|
||||
@apply ks-text-link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<%= tag.public_send(@tag, class: "mb-6 last:mb-0") do %>
|
||||
<% if @positioning == :vertical %>
|
||||
<label class="block">
|
||||
<p class="font-bold <%= @descripton.present? ? "mb-1" : "mb-2" %>">
|
||||
<%= @title %>
|
||||
@@ -10,4 +11,19 @@
|
||||
<% end %>
|
||||
<%= content %>
|
||||
</label>
|
||||
<% elsif @positioning == :horizontal %>
|
||||
<label class="block flex items-center justify-between">
|
||||
<div class="flex flex-col">
|
||||
<label class="font-bold mb-1"><%= @title %></label>
|
||||
<% if @descripton.present? %>
|
||||
<p class="text-gray-500"><%= @descripton %></p>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="relative ml-4 inline-flex flex-shrink-0">
|
||||
<%= content %>
|
||||
</div>
|
||||
</label>
|
||||
<% else %>
|
||||
<p>Invalid <code>positioning<code> argument for <code>FieldsetComponent</code>.</p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
module FormElements
|
||||
class FieldsetComponent < ViewComponent::Base
|
||||
def initialize(tag: "li", title:, description: nil)
|
||||
@tag = tag
|
||||
@title = title
|
||||
@descripton = description
|
||||
def initialize(tag: "li", positioning: :vertical, title:, description: nil)
|
||||
@tag = tag
|
||||
@positioning = positioning
|
||||
@title = title
|
||||
@descripton = description
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<%= tag.public_send @tag, class: "flex items-center justify-between mb-6 last:mb-0",
|
||||
data: @form.present? ? {
|
||||
data: @form_enabled ? {
|
||||
controller: "settings--toggle",
|
||||
:'settings--toggle-switch-enabled-value' => @enabled.to_s
|
||||
} : nil do %>
|
||||
@@ -11,16 +11,23 @@
|
||||
<%= render FormElements::ToggleComponent.new(
|
||||
enabled: @enabled,
|
||||
input_enabled: @input_enabled,
|
||||
class_names: @form.present? ? "hidden" : nil,
|
||||
class_names: @form_enabled ? "hidden" : nil,
|
||||
data: {
|
||||
:'settings--toggle-target' => "button",
|
||||
action: "settings--toggle#toggleSwitch"
|
||||
}) %>
|
||||
<% if @form.present? %>
|
||||
<%= @form.check_box @attribute, {
|
||||
checked: @enabled,
|
||||
data: { :'settings--toggle-target' => "checkbox" }
|
||||
}, "true", "false" %>
|
||||
<% if @form_enabled %>
|
||||
<% if @attribute.present? %>
|
||||
<%= @form.check_box @attribute, {
|
||||
checked: @enabled,
|
||||
data: { :'settings--toggle-target' => "checkbox" }
|
||||
}, "true", "false" %>
|
||||
<% else %>
|
||||
<input name="<%= @field_name %>" type="hidden" value="false" autocomplete="off">
|
||||
<%= check_box_tag @field_name, "true", @enabled, {
|
||||
data: { :'settings--toggle-target' => "checkbox" }
|
||||
} %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
module FormElements
|
||||
class FieldsetToggleComponent < ViewComponent::Base
|
||||
def initialize(form: nil, attribute: nil, tag: "li", enabled: false,
|
||||
input_enabled: true, title:, description:)
|
||||
def initialize(tag: "li", form: nil, attribute: nil, field_name: nil,
|
||||
enabled: false, input_enabled: true, title:, description:)
|
||||
@tag = tag
|
||||
@form = form
|
||||
@attribute = attribute
|
||||
@tag = tag
|
||||
@field_name = field_name
|
||||
@form_enabled = @form.present? || @field_name.present?
|
||||
@enabled = enabled
|
||||
@input_enabled = input_enabled
|
||||
@title = title
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<%= button_tag type: "button", name: "toggle", data: @data,
|
||||
role: "switch", aria: { checked: @enabled.to_s },
|
||||
disabled: !@input_enabled,
|
||||
tabindex: @tabindex, disabled: !@input_enabled,
|
||||
class: "#{ @enabled ? 'bg-blue-600' : 'bg-gray-200' }
|
||||
#{ @class_names.present? ? @class_names : '' }
|
||||
relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
|
||||
module FormElements
|
||||
class ToggleComponent < ViewComponent::Base
|
||||
def initialize(enabled:, input_enabled: true, data: nil, class_names: nil)
|
||||
def initialize(enabled:, input_enabled: true, data: nil, class_names: nil, tabindex: nil)
|
||||
@enabled = !!enabled
|
||||
@input_enabled = input_enabled
|
||||
@data = data
|
||||
@class_names = class_names
|
||||
@tabindex = tabindex
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,6 +3,18 @@ class ApplicationController < ActionController::Base
|
||||
render :text => exception, :status => 500
|
||||
end
|
||||
|
||||
before_action :sentry_set_user
|
||||
|
||||
def sentry_set_user
|
||||
return unless Setting.sentry_enabled
|
||||
|
||||
if user_signed_in?
|
||||
Sentry.set_user(id: current_user.id, username: current_user.cn)
|
||||
else
|
||||
Sentry.set_user({})
|
||||
end
|
||||
end
|
||||
|
||||
def require_user_signed_in
|
||||
unless user_signed_in?
|
||||
redirect_to welcome_path and return
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
class Settings::AccountController < SettingsController
|
||||
|
||||
def index
|
||||
end
|
||||
|
||||
def reset_password
|
||||
current_user.send_reset_password_instructions
|
||||
sign_out current_user
|
||||
msg = "We have sent you an email with a link to reset your password."
|
||||
redirect_to check_your_email_path, notice: msg
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,11 +0,0 @@
|
||||
class Settings::ProfileController < SettingsController
|
||||
|
||||
def index
|
||||
@user = current_user
|
||||
end
|
||||
|
||||
def update
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,13 +1,52 @@
|
||||
class SettingsController < ApplicationController
|
||||
before_action :require_user_signed_in
|
||||
before_action :set_current_section
|
||||
before_action :authenticate_user!
|
||||
before_action :set_main_nav_section
|
||||
before_action :set_settings_section, only: ['show', 'update']
|
||||
|
||||
def index
|
||||
redirect_to setting_path(:profile)
|
||||
end
|
||||
|
||||
def show
|
||||
@user = current_user
|
||||
end
|
||||
|
||||
def update
|
||||
@user = current_user
|
||||
@user.preferences.merge! user_params[:preferences]
|
||||
@user.save!
|
||||
|
||||
redirect_to setting_path(@settings_section), flash: {
|
||||
success: 'Settings saved.'
|
||||
}
|
||||
end
|
||||
|
||||
def reset_password
|
||||
current_user.send_reset_password_instructions
|
||||
sign_out current_user
|
||||
msg = "We have sent you an email with a link to reset your password."
|
||||
redirect_to check_your_email_path, notice: msg
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_current_section
|
||||
def set_main_nav_section
|
||||
@current_section = :settings
|
||||
end
|
||||
|
||||
def set_settings_section
|
||||
@settings_section = params[:section]
|
||||
allowed_sections = [:profile, :account, :lightning, :xmpp]
|
||||
|
||||
unless allowed_sections.include?(@settings_section.to_sym)
|
||||
redirect_to setting_path(:profile)
|
||||
end
|
||||
end
|
||||
|
||||
def user_params
|
||||
params.require(:user).permit(preferences: [
|
||||
:lightning_notify_sats_received,
|
||||
:xmpp_exchange_contacts_with_invitees
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
57
app/controllers/webfinger_controller.rb
Normal file
57
app/controllers/webfinger_controller.rb
Normal file
@@ -0,0 +1,57 @@
|
||||
class WebfingerController < ApplicationController
|
||||
before_action :allow_cross_origin_requests, only: [:show]
|
||||
|
||||
layout false
|
||||
|
||||
def show
|
||||
resource = params[:resource]
|
||||
|
||||
if resource && resource.match(/acct:\w+/)
|
||||
useraddress = resource.split(":").last
|
||||
username, org = useraddress.split("@")
|
||||
username.downcase!
|
||||
unless User.where(cn: username, ou: org).any?
|
||||
head 404 and return
|
||||
end
|
||||
|
||||
render json: webfinger(useraddress).to_json,
|
||||
content_type: "application/jrd+json"
|
||||
else
|
||||
head 422 and return
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def webfinger(useraddress)
|
||||
links = [];
|
||||
|
||||
links << remotestorage_link(useraddress) if Setting.remotestorage_enabled
|
||||
|
||||
{ "links" => links }
|
||||
end
|
||||
|
||||
def remotestorage_link(useraddress)
|
||||
# TODO use when OAuth routes are available
|
||||
# auth_url = new_rs_oauth_url(useraddress)
|
||||
auth_url = "https://example.com/rs/oauth"
|
||||
storage_url = "#{Setting.rs_storage_url}/#{useraddress}"
|
||||
|
||||
{
|
||||
"rel" => "http://tools.ietf.org/id/draft-dejong-remotestorage",
|
||||
"href" => storage_url,
|
||||
"properties" => {
|
||||
"http://remotestorage.io/spec/version" => "draft-dejong-remotestorage-13",
|
||||
"http://tools.ietf.org/html/rfc6749#section-4.2" => auth_url,
|
||||
"http://tools.ietf.org/html/rfc6750#section-2.3" => nil, # access token via a HTTP query parameter
|
||||
"http://tools.ietf.org/html/rfc7233": "GET", # content range requests
|
||||
"http://remotestorage.io/spec/web-authoring": nil
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def allow_cross_origin_requests
|
||||
headers['Access-Control-Allow-Origin'] = '*'
|
||||
headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, OPTIONS'
|
||||
end
|
||||
end
|
||||
@@ -12,22 +12,28 @@ class WebhooksController < ApplicationController
|
||||
end
|
||||
|
||||
user = User.find_by!(ln_account: payload[:user_login])
|
||||
|
||||
# TODO make configurable
|
||||
notify_xmpp(user.address, payload[:amount], payload[:memo])
|
||||
notify = user.preferences[:lightning_notify_sats_received]
|
||||
case notify
|
||||
when "xmpp"
|
||||
notify_xmpp(user.address, payload[:amount], payload[:memo])
|
||||
when "email"
|
||||
NotificationMailer.with(user: user, amount_sats: payload[:amount])
|
||||
.lightning_sats_received.deliver_later
|
||||
end
|
||||
|
||||
head :ok
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# TODO refactor into mailer-like generic class/service
|
||||
def notify_xmpp(address, amt_sats, memo)
|
||||
payload = {
|
||||
type: "normal",
|
||||
from: "kosmos.org", # TODO domain config
|
||||
to: address,
|
||||
subject: "Sats received!",
|
||||
body: "#{amt_sats} sats received in your Lightning wallet:\n> #{memo}"
|
||||
body: "#{helpers.number_with_delimiter amt_sats} sats received in your Lightning wallet:\n> #{memo}"
|
||||
}
|
||||
XmppSendMessageJob.perform_later(payload)
|
||||
end
|
||||
|
||||
@@ -4,6 +4,10 @@ export default class extends Controller {
|
||||
static targets = ["buttons", "countdown"]
|
||||
|
||||
connect() {
|
||||
// Devise timeoutable ends up adding a second flash message without content
|
||||
// TODO investigate bug
|
||||
if (this.element.textContent.trim() == "true") return;
|
||||
|
||||
const timeoutSeconds = parseInt(this.data.get("timeout"));
|
||||
|
||||
setTimeout(() => {
|
||||
|
||||
8
app/mailers/notification_mailer.rb
Normal file
8
app/mailers/notification_mailer.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
class NotificationMailer < ApplicationMailer
|
||||
def lightning_sats_received
|
||||
@user = params[:user]
|
||||
@amount_sats = params[:amount_sats]
|
||||
@subject = "Sats received"
|
||||
mail to: @user.email, subject: @subject
|
||||
end
|
||||
end
|
||||
@@ -2,6 +2,13 @@
|
||||
class Setting < RailsSettings::Base
|
||||
cache_prefix { "v1" }
|
||||
|
||||
#
|
||||
# Internal services
|
||||
#
|
||||
|
||||
field :redis_url, type: :string, readonly: true,
|
||||
default: ENV["REDIS_URL"] || "redis://localhost:6379/0"
|
||||
|
||||
#
|
||||
# Registrations
|
||||
#
|
||||
@@ -10,6 +17,13 @@ class Setting < RailsSettings::Base
|
||||
account accounts donations mail webmaster support
|
||||
]
|
||||
|
||||
#
|
||||
# Sentry
|
||||
#
|
||||
|
||||
field :sentry_enabled, type: :boolean, readonly: true,
|
||||
default: (ENV["SENTRY_DSN"].present?.to_s || false)
|
||||
|
||||
#
|
||||
# Discourse
|
||||
#
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
class User < ApplicationRecord
|
||||
include EmailValidatable
|
||||
|
||||
serialize :preferences, UserPreferences
|
||||
|
||||
# Relations
|
||||
has_many :invitations, dependent: :destroy
|
||||
has_one :invitation, inverse_of: :invitee, foreign_key: 'invited_user_id'
|
||||
@@ -24,6 +26,7 @@ class User < ApplicationRecord
|
||||
validates_format_of :cn, without: /\A-/,
|
||||
if: Proc.new{ |u| u.cn.present? },
|
||||
message: "is invalid. Usernames need to start with a letter."
|
||||
# FIXME This needs a server restart to apply values
|
||||
validates_format_of :cn, without: /\A(#{Setting.reserved_usernames.join('|')})\z/i,
|
||||
message: "has already been taken"
|
||||
|
||||
@@ -40,7 +43,9 @@ class User < ApplicationRecord
|
||||
devise :ldap_authenticatable,
|
||||
:confirmable,
|
||||
:recoverable,
|
||||
:validatable
|
||||
:validatable,
|
||||
:timeoutable,
|
||||
:rememberable
|
||||
|
||||
def ldap_before_save
|
||||
self.email = Devise::LDAP::Adapter.get_ldap_param(self.cn, "mail").first
|
||||
@@ -55,16 +60,23 @@ class User < ApplicationRecord
|
||||
end
|
||||
|
||||
def devise_after_confirmation
|
||||
enable_service %w[ discourse ejabberd gitea mediawiki ]
|
||||
enable_service %w[ discourse gitea mediawiki xmpp ]
|
||||
|
||||
#TODO enable in development when we have easy setup of ejabberd etc.
|
||||
return if Rails.env.development?
|
||||
|
||||
if inviter.present?
|
||||
exchange_xmpp_contact_with_inviter if Setting.ejabberd_enabled?
|
||||
if Setting.ejabberd_enabled? &&
|
||||
inviter.preferences[:xmpp_exchange_contacts_with_invitees]
|
||||
exchange_xmpp_contact_with_inviter
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def send_devise_notification(notification, *args)
|
||||
devise_mailer.send(notification, self, *args).deliver_later
|
||||
end
|
||||
|
||||
def reset_password(new_password, new_password_confirmation)
|
||||
self.password = new_password
|
||||
self.password_confirmation = new_password_confirmation
|
||||
@@ -130,8 +142,8 @@ class User < ApplicationRecord
|
||||
end
|
||||
|
||||
def exchange_xmpp_contact_with_inviter
|
||||
return unless inviter.services_enabled.include?("ejabberd") &&
|
||||
services_enabled.include?("ejabberd")
|
||||
return unless inviter.services_enabled.include?("xmpp") &&
|
||||
services_enabled.include?("xmpp")
|
||||
XmppExchangeContactsJob.perform_later(inviter, self.cn, self.ou)
|
||||
end
|
||||
|
||||
|
||||
29
app/models/user_preferences.rb
Normal file
29
app/models/user_preferences.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
DEFAULT_PREFS = YAML.load_file("#{Rails.root}/config/default_preferences.yml")
|
||||
|
||||
class UserPreferences
|
||||
def self.dump(value)
|
||||
process(value).to_yaml
|
||||
end
|
||||
|
||||
def self.load(string)
|
||||
stored_prefs = YAML.load(string || "{}")
|
||||
DEFAULT_PREFS.merge(stored_prefs).with_indifferent_access
|
||||
end
|
||||
|
||||
def self.is_integer?(value)
|
||||
value.to_i.to_s == value
|
||||
end
|
||||
|
||||
def self.process(hash)
|
||||
hash.each do |key, value|
|
||||
if value == "true"
|
||||
hash[key] = true
|
||||
elsif value == "false"
|
||||
hash[key] = false
|
||||
elsif value.is_a?(String) && is_integer?(value)
|
||||
hash[key] = value.to_i
|
||||
end
|
||||
end
|
||||
hash.stringify_keys!.to_h
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
class EjabberdApiClient
|
||||
def initialize
|
||||
@base_url = ENV["EJABBERD_API_URL"]
|
||||
@base_url = Setting.ejabberd_api_url
|
||||
end
|
||||
|
||||
def post(endpoint, payload)
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
<td>XMPP (ejabberd)</td>
|
||||
<td>
|
||||
<%= render FormElements::ToggleComponent.new(
|
||||
enabled: @services_enabled.include?("ejabberd"),
|
||||
enabled: @services_enabled.include?("xmpp"),
|
||||
input_enabled: false
|
||||
) %>
|
||||
</td>
|
||||
|
||||
@@ -7,19 +7,43 @@
|
||||
<%= f.label :cn, 'User', class: 'block mb-2 font-bold' %>
|
||||
<p class="flex gap-2 items-center">
|
||||
<%= f.text_field :cn, autofocus: true, autocomplete: "username",
|
||||
required: true, class: "relative grow"%>
|
||||
required: true, class: "relative grow", tabindex: "1" %>
|
||||
<span class="relative shrink-0 text-gray-500">@ kosmos.org</span>
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
<p class="mb-8">
|
||||
<%= f.label :password, class: 'block mb-2 font-bold' %>
|
||||
<%= f.password_field :password, autocomplete: "current-password",
|
||||
required: true, class: "w-full"%>
|
||||
required: true, class: "w-full", tabindex: "2" %>
|
||||
</p>
|
||||
<p class="mt-8">
|
||||
<%= f.submit "Log in", class: 'btn-md btn-blue w-full' %>
|
||||
|
||||
<%= tag.div class: "flex items-center mb-8 gap-x-3", data: {
|
||||
controller: "settings--toggle",
|
||||
:'settings--toggle-switch-enabled-value' => "false"
|
||||
} do %>
|
||||
<div class="relative inline-flex flex-shrink-0">
|
||||
<%= render FormElements::ToggleComponent.new(
|
||||
enabled: false, input_enabled: true, class_names: "hidden",
|
||||
tabindex: "3", data: {
|
||||
:'settings--toggle-target' => "button",
|
||||
action: "settings--toggle#toggleSwitch"
|
||||
}) %>
|
||||
<%= f.check_box :remember_me, {
|
||||
checked: false,
|
||||
data: { :'settings--toggle-target' => "checkbox" }
|
||||
}, "true", "false" %>
|
||||
</div>
|
||||
<%= f.label :remember_me,
|
||||
class: "text-gray-500 flex flex-col",
|
||||
data: { action: "click->settings--toggle#toggleSwitch" } %>
|
||||
<p class="grow text-sm text-right">
|
||||
<%= link_to "Forgot your password?", new_password_path(resource_name),
|
||||
class: "text-gray-500 underline" %><br />
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<p>
|
||||
<%= f.submit "Log in", class: 'btn-md btn-blue w-full', tabindex: "4" %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<%= render "devise/shared/links" %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
<div class="devise-links mt-8 text-sm">
|
||||
<%- if controller_name != 'sessions' %>
|
||||
<p class="mb-1.5">
|
||||
<%= link_to "Log in", new_session_path(resource_name) %><br />
|
||||
<p class="mb-2">
|
||||
<%= link_to "Log in", new_session_path(resource_name),
|
||||
class: "text-gray-500 underline" %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
|
||||
<p class="mb-1.5">
|
||||
<%= link_to "Forgot your password?", new_password_path(resource_name) %><br />
|
||||
<p class="mb-2">
|
||||
<%= link_to "Forgot your password?", new_password_path(resource_name),
|
||||
class: "text-gray-500 underline" %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
|
||||
<p class="mb-1.5">
|
||||
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %><br />
|
||||
<%- if devise_mapping.confirmable? && !controller_name.match(/^(confirmations|sessions)$/) %>
|
||||
<p class="mb-2">
|
||||
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name),
|
||||
class: "text-gray-500 underline" %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
|
||||
<p class="mb-1.5">
|
||||
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br />
|
||||
<p class="mb-2">
|
||||
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name),
|
||||
class: "text-gray-500 underline" %>
|
||||
</p>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-bell"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path><path d="M13.73 21a2 2 0 0 1-3.46 0"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-bell <%= custom_class %>"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path><path d="M13.73 21a2 2 0 0 1-3.46 0"></path></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 321 B After Width: | Height: | Size: 342 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-message-circle"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-message-circle <%= custom_class %>"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 428 B After Width: | Height: | Size: 449 B |
@@ -0,0 +1,3 @@
|
||||
You just received <%= number_with_delimiter @amount_sats %> sats in your Lightning account (<%= @user.address %>). Check your wallet app, or open the account page for details:
|
||||
|
||||
<%= wallet_transactions_url %>
|
||||
19
app/views/settings/_account.html.erb
Normal file
19
app/views/settings/_account.html.erb
Normal file
@@ -0,0 +1,19 @@
|
||||
<section>
|
||||
<h3>E-Mail</h3>
|
||||
<p class="mb-2">
|
||||
<%= label :email, 'Address', class: 'font-bold' %>
|
||||
</p>
|
||||
<p class="flex gap-1 mb-2 sm:w-3/5">
|
||||
<input type="text" id="email" class="grow"
|
||||
value=<%= current_user.email %> disabled="disabled" />
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<h3>Password</h3>
|
||||
<p class="mb-8">Use the following button to request an email with a password reset link:</p>
|
||||
<%= form_with(url: reset_password_settings_path, method: :post) do %>
|
||||
<p>
|
||||
<%= submit_tag("Send me a password reset link", class: 'btn-md btn-gray w-full sm:w-auto') %>
|
||||
</p>
|
||||
<% end %>
|
||||
</section>
|
||||
25
app/views/settings/_lightning.html.erb
Normal file
25
app/views/settings/_lightning.html.erb
Normal file
@@ -0,0 +1,25 @@
|
||||
<%= form_for @user, url: setting_path(:lightning), html: { :method => :put } do |f| %>
|
||||
<section>
|
||||
<h3>Notifications</h3>
|
||||
<ul role="list">
|
||||
<%= render FormElements::FieldsetComponent.new(
|
||||
positioning: :horizontal,
|
||||
title: "Sats received",
|
||||
description: "Notify me when sats are sent to my Lightning Address"
|
||||
) do %>
|
||||
<% f.fields_for :preferences do |p| %>
|
||||
<%= p.select :lightning_notify_sats_received, options_for_select([
|
||||
["off", "disabled"],
|
||||
["Chat (Jabber)", "xmpp"],
|
||||
["E-Mail", "email"]
|
||||
], selected: @user.preferences[:lightning_notify_sats_received]) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<p class="pt-6 border-t border-gray-200 text-right">
|
||||
<%= f.submit 'Save', class: "btn-md btn-blue w-full md:w-auto" %>
|
||||
</p>
|
||||
</section>
|
||||
<% end %>
|
||||
16
app/views/settings/_notifications.html.erb
Normal file
16
app/views/settings/_notifications.html.erb
Normal file
@@ -0,0 +1,16 @@
|
||||
<section>
|
||||
<h3>Lightning Wallet</h3>
|
||||
|
||||
<ul role="list">
|
||||
<%= render FormElements::FieldsetComponent.new(
|
||||
positioning: :horizontal,
|
||||
title: "Sats received",
|
||||
description: "Notify when sats are sent to my Lightning Address"
|
||||
) do %>
|
||||
<%= select_tag :sats_received, options_for_select([
|
||||
["off", "off"],
|
||||
["Chat (Jabber)", "xmpp"]
|
||||
]) %>
|
||||
<% end %>
|
||||
</ul>
|
||||
</section>
|
||||
30
app/views/settings/_profile.html.erb
Normal file
30
app/views/settings/_profile.html.erb
Normal file
@@ -0,0 +1,30 @@
|
||||
<section>
|
||||
<h3>Profile</h3>
|
||||
<p class="mb-2">
|
||||
<%= label :user_address, 'User address', class: 'font-bold' %>
|
||||
</p>
|
||||
<p data-controller="clipboard" class="flex gap-1 mb-2 sm:w-3/5">
|
||||
<input type="text" id="user_address" class="grow"
|
||||
value=<%= @user.address %> disabled="disabled"
|
||||
data-clipboard-target="source" />
|
||||
<button id="copy-user-address" class="btn-md btn-icon btn-blue shrink-0"
|
||||
data-clipboard-target="trigger" data-action="clipboard#copy"
|
||||
title="Copy to clipboard">
|
||||
<span class="content-initial">
|
||||
<%= render partial: "icons/copy", locals: { custom_class: "text-white h-4 w-4 inline" } %>
|
||||
</span>
|
||||
<span class="content-active hidden">
|
||||
<%= render partial: "icons/check", locals: { custom_class: "text-white h-4 w-4 inline" } %>
|
||||
</span>
|
||||
</button>
|
||||
</p>
|
||||
<p class="text-sm text-gray-500">
|
||||
Your user address for Chat and Lightning Network.
|
||||
</p>
|
||||
|
||||
<%# <%= form_for(@user, as: "profile", url: settings_profile_path) do |f| %>
|
||||
<%# <p class="mt-8">
|
||||
<%# <%= f.submit "Save changes", class: 'btn-md btn-blue w-full sm:w-auto' %>
|
||||
<%# </p>
|
||||
<%# <% end %>
|
||||
</section>
|
||||
18
app/views/settings/_xmpp.html.erb
Normal file
18
app/views/settings/_xmpp.html.erb
Normal file
@@ -0,0 +1,18 @@
|
||||
<%= form_for @user, url: setting_path(:xmpp), html: { :method => :put } do |f| %>
|
||||
<section>
|
||||
<h3>Contacts</h3>
|
||||
<ul role="list">
|
||||
<%= render FormElements::FieldsetToggleComponent.new(
|
||||
field_name: "user[preferences][xmpp_exchange_contacts_with_invitees]",
|
||||
enabled: @user.preferences[:xmpp_exchange_contacts_with_invitees],
|
||||
title: "Exchange contacts when invited user signs up",
|
||||
description: "Add each others contacts, so you can chat with them immediately"
|
||||
) %>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<p class="pt-6 border-t border-gray-200 text-right">
|
||||
<%= f.submit 'Save', class: "btn-md btn-blue w-full md:w-auto" %>
|
||||
</p>
|
||||
</section>
|
||||
<% end %>
|
||||
@@ -1,23 +0,0 @@
|
||||
<%= render HeaderComponent.new(title: "Settings") %>
|
||||
|
||||
<%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/sidenav_settings') do %>
|
||||
<section>
|
||||
<h3>E-Mail</h3>
|
||||
<p class="mb-2">
|
||||
<%= label :email, 'Address', class: 'font-bold' %>
|
||||
</p>
|
||||
<p class="flex gap-1 mb-2 sm:w-3/5">
|
||||
<input type="text" id="email" class="grow"
|
||||
value=<%= current_user.email %> disabled="disabled" />
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<h3>Password</h3>
|
||||
<p class="mb-8">Use the following button to request an email with a password reset link:</p>
|
||||
<%= form_with(url: settings_reset_password_path, method: :post) do %>
|
||||
<p>
|
||||
<%= submit_tag("Send me a password reset link", class: 'btn-md btn-gray w-full sm:w-auto') %>
|
||||
</p>
|
||||
<% end %>
|
||||
</section>
|
||||
<% end %>
|
||||
@@ -1,34 +0,0 @@
|
||||
<%= render HeaderComponent.new(title: "Settings") %>
|
||||
|
||||
<%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/sidenav_settings') do %>
|
||||
<section>
|
||||
<h3>Profile</h3>
|
||||
<p class="mb-2">
|
||||
<%= label :user_address, 'User address', class: 'font-bold' %>
|
||||
</p>
|
||||
<p data-controller="clipboard" class="flex gap-1 mb-2 sm:w-3/5">
|
||||
<input type="text" id="user_address" class="grow"
|
||||
value=<%= @user.address %> disabled="disabled"
|
||||
data-clipboard-target="source" />
|
||||
<button id="copy-user-address" class="btn-md btn-icon btn-blue shrink-0"
|
||||
data-clipboard-target="trigger" data-action="clipboard#copy"
|
||||
title="Copy to clipboard">
|
||||
<span class="content-initial">
|
||||
<%= render partial: "icons/copy", locals: { custom_class: "text-white h-4 w-4 inline" } %>
|
||||
</span>
|
||||
<span class="content-active hidden">
|
||||
<%= render partial: "icons/check", locals: { custom_class: "text-white h-4 w-4 inline" } %>
|
||||
</span>
|
||||
</button>
|
||||
</p>
|
||||
<p class="text-sm text-gray-500">
|
||||
Your user address for Chat and Lightning Network.
|
||||
</p>
|
||||
|
||||
<%# <%= form_for(@user, as: "profile", url: settings_profile_path) do |f| %>
|
||||
<%# <p class="mt-8">
|
||||
<%# <%= f.submit "Save changes", class: 'btn-md btn-blue w-full sm:w-auto' %>
|
||||
<%# </p>
|
||||
<%# <% end %>
|
||||
</section>
|
||||
<% end %>
|
||||
5
app/views/settings/show.html.erb
Normal file
5
app/views/settings/show.html.erb
Normal file
@@ -0,0 +1,5 @@
|
||||
<%= render HeaderComponent.new(title: "Settings") %>
|
||||
|
||||
<%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/sidenav_settings') do %>
|
||||
<%= render partial: @settings_section %>
|
||||
<% end %>
|
||||
@@ -6,5 +6,5 @@
|
||||
class: main_nav_class(@current_section, :invitations) %>
|
||||
<%= link_to "Wallet", wallet_path,
|
||||
class: main_nav_class(@current_section, :wallet) %>
|
||||
<%= link_to "Settings", settings_profile_path,
|
||||
<%= link_to "Settings", settings_path,
|
||||
class: main_nav_class(@current_section, :settings) %>
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
<%= render SidenavLinkComponent.new(
|
||||
name: "Profile", path: settings_profile_path, icon: "user",
|
||||
active: current_page?(settings_profile_path)
|
||||
name: "Profile", path: setting_path(:profile), icon: "user",
|
||||
active: current_page?(setting_path(:profile))
|
||||
) %>
|
||||
<%= render SidenavLinkComponent.new(
|
||||
name: "Account", path: settings_account_path, icon: "key",
|
||||
active: current_page?(settings_account_path)
|
||||
name: "Account", path: setting_path(:account), icon: "key",
|
||||
active: current_page?(setting_path(:account))
|
||||
) %>
|
||||
<% if Setting.ejabberd_enabled %>
|
||||
<%= render SidenavLinkComponent.new(
|
||||
name: "Security", path: "#", icon: "shield", disabled: true
|
||||
name: "Chat", path: setting_path(:xmpp), icon: "message-circle",
|
||||
active: current_page?(setting_path(:xmpp))
|
||||
) %>
|
||||
<% end %>
|
||||
<% if Setting.lndhub_enabled %>
|
||||
<%= render SidenavLinkComponent.new(
|
||||
name: "Wallet", path: setting_path(:lightning), icon: "zap",
|
||||
active: current_page?(setting_path(:lightning))
|
||||
) %>
|
||||
<% end %>
|
||||
|
||||
Reference in New Issue
Block a user