5 Commits

Author SHA1 Message Date
Râu Cao
595bb03c5a Do not exchange XMPP contacts when turned off by inviter
Some checks are pending
continuous-integration/drone/push Build is running
2023-04-04 12:45:13 +02:00
Râu Cao
62cd0eb7d1 Re-rename "ejabberd" service to "xmpp"
Shouldn't matter which implementation is integrated if someone adds
another one
2023-04-04 12:29:39 +02:00
Râu Cao
f19baaf22a Add new user settings pages for Chat and Wallet 2023-04-04 12:28:53 +02:00
Râu Cao
23821f9e65 Add preferences to user model 2023-04-04 12:27:49 +02:00
Râu Cao
a33410eeb4 Allow handing custom field names to toggle fieldset component 2023-04-04 12:03:00 +02:00
12 changed files with 175 additions and 23 deletions

View File

@@ -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 %>

View File

@@ -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

View File

@@ -12,6 +12,13 @@ class SettingsController < ApplicationController
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
@@ -29,10 +36,17 @@ class SettingsController < ApplicationController
def set_settings_section
@settings_section = params[:section]
allowed_sections = [:profile, :account, :notifications]
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

View File

@@ -1,6 +1,8 @@
class User < ApplicationRecord
include EmailValidatable
serialize :preferences, Hash, default: {}
# Relations
has_many :invitations, dependent: :destroy
has_one :invitation, inverse_of: :invitee, foreign_key: 'invited_user_id'
@@ -55,13 +57,16 @@ 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.pref_enabled?("xmpp:exchange_contacts_with_invitees")
exchange_xmpp_contact_with_inviter
end
end
end
@@ -133,9 +138,14 @@ class User < ApplicationRecord
ldap.delete_attribute(dn,:service)
end
def pref_enabled?(key)
value = preferences.dig(*key.split(":"))
[true, "true", "enabled", 1].include?(value)
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

View File

@@ -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>

View File

@@ -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

View File

@@ -0,0 +1,27 @@
<%= 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.fields_for :lightning do |l| %>
<%= l.select :notify_sats_received, options_for_select([
["off", "off"],
["Chat (Jabber)", "xmpp"],
["E-Mail", "email"]
], selected: @user.preferences.dig('lightning', 'notify_sats_received')) %>
<% end %>
<% 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 %>

View 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.pref_enabled?("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 %>

View File

@@ -6,7 +6,15 @@
name: "Account", path: setting_path(:account), icon: "key",
active: current_page?(setting_path(:account))
) %>
<% if Setting.ejabberd_enabled %>
<%= render SidenavLinkComponent.new(
name: "Notifications", path: setting_path(:notifications), icon: "bell",
active: current_page?(setting_path(:notifications))
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 %>

View File

@@ -0,0 +1,5 @@
class AddPreferencesToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :preferences, :text
end
end

View File

@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_03_19_101128) do
ActiveRecord::Schema[7.0].define(version: 2023_04_03_135149) do
create_table "donations", force: :cascade do |t|
t.integer "user_id"
t.integer "amount_sats"
@@ -57,8 +57,10 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_19_101128) do
t.text "ln_login_ciphertext"
t.text "ln_password_ciphertext"
t.string "ln_account"
t.string "nostr_pubkey"
t.datetime "remember_created_at"
t.string "remember_token"
t.text "preferences"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end

View File

@@ -109,7 +109,7 @@ RSpec.describe User, type: :model do
before do
Invitation.create! user: user, invited_user_id: guest.id, used_at: DateTime.now
allow_any_instance_of(User).to receive(:services_enabled).and_return(%w[ ejabberd ])
allow_any_instance_of(User).to receive(:services_enabled).and_return(%w[ xmpp ])
end
it "enqueues a job to exchange XMPP contacts between inviter and invitee" do
@@ -131,14 +131,16 @@ RSpec.describe User, type: :model do
let(:user) { create :user, cn: "willherschel", ou: "kosmos.org" }
it "enables default services" do
expect(user).to receive(:enable_service).with(%w[ discourse ejabberd gitea mediawiki ])
expect(user).to receive(:enable_service).with(%w[ discourse gitea mediawiki xmpp ])
user.send(:devise_after_confirmation)
end
context "for invited user with ejabberd enabled" do
context "for invited user with xmpp enabled" do
let(:guest) { create :user, id: 2, cn: "isaacnewton", ou: "kosmos.org", email: "newt@example.com" }
before do
# TODO remove when defaults are implemented
user.update! preferences: {"xmpp" => { "exchange_contacts_with_invitees" => true }}
Invitation.create! user: user, invited_user_id: guest.id, used_at: DateTime.now
allow_any_instance_of(User).to receive(:enable_service).and_return(true)
end
@@ -147,6 +149,63 @@ RSpec.describe User, type: :model do
expect(guest).to receive(:exchange_xmpp_contact_with_inviter)
guest.send(:devise_after_confirmation)
end
context "automatic contact exchange disabled" do
before do
user.update! preferences: {"xmpp" => { "exchange_contacts_with_invitees" => false }}
end
it "does not exchange XMPP contacts with the inviter" do
expect(guest).to_not receive(:exchange_xmpp_contact_with_inviter)
guest.send(:devise_after_confirmation)
end
end
end
end
describe "#pref_enabled?" do
describe "preference not set" do
# TODO return default value
it "returns false" do
expect(user.pref_enabled?("lightning:notify_sats_received")).to be(false)
end
end
describe "preference is set" do
it "returns true for boolean true" do
user.preferences.merge!({"lightning" => {"notify_sats_received" => true}})
expect(user.pref_enabled?("lightning:notify_sats_received")).to be(true)
end
it "returns true for string 'true'" do
user.preferences.merge!({"lightning" => {"notify_sats_received" => "true"}})
expect(user.pref_enabled?("lightning:notify_sats_received")).to be(true)
end
it "returns true for string 'enabled'" do
user.preferences.merge!({"lightning" => {"notify_sats_received" => "enabled"}})
expect(user.pref_enabled?("lightning:notify_sats_received")).to be(true)
end
it "returns true for integer 1" do
user.preferences.merge!({"lightning" => {"notify_sats_received" => 1}})
expect(user.pref_enabled?("lightning:notify_sats_received")).to be(true)
end
it "returns false for boolean false" do
user.preferences.merge!({"lightning" => {"notify_sats_received" => false}})
expect(user.pref_enabled?("lightning:notify_sats_received")).to be(false)
end
it "returns false for string 'false'" do
user.preferences.merge!({"lightning" => {"notify_sats_received" => "false"}})
expect(user.pref_enabled?("lightning:notify_sats_received")).to be(false)
end
it "returns false for integer 0" do
user.preferences.merge!({"lightning" => {"notify_sats_received" => 0}})
expect(user.pref_enabled?("lightning:notify_sats_received")).to be(false)
end
end
end
end