Skip to content
This repository was archived by the owner on Jul 16, 2025. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,38 +1,43 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2024-06-25 17:50:29 UTC using RuboCop version 1.64.1.
# on 2024-07-03 21:53:32 UTC using RuboCop version 1.64.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.

# Offense count: 3
# Offense count: 4
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
Metrics/AbcSize:
Max: 24
Max: 45

# Offense count: 8
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
# AllowedMethods: refine
Metrics/BlockLength:
Max: 66

# Offense count: 1
# Configuration parameters: CountComments, CountAsOne.
Metrics/ClassLength:
Max: 134

# Offense count: 1
# Configuration parameters: AllowedMethods, AllowedPatterns.
Metrics/CyclomaticComplexity:
Max: 9

# Offense count: 6
# Offense count: 8
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
Metrics/MethodLength:
Max: 16
Max: 30

# Offense count: 1
# Configuration parameters: AllowedMethods, AllowedPatterns.
Metrics/PerceivedComplexity:
Max: 9

# Offense count: 28
# Offense count: 27
# Configuration parameters: AllowedConstants.
Style/Documentation:
Enabled: false
Expand Down
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ group :test do
# Adds support for Capybara system testing and selenium driver
gem 'selenium-webdriver', '>= 4.17.0.rc1'
# Easy installation and use of web drivers to run system tests with browsers
gem 'webdrivers'
gem 'webdrivers', '~> 5.0'

# Adds support to make API calls
gem 'webmock'
end
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ DEPENDENCIES
turbo-rails
tzinfo-data
web-console (>= 4.2.1)
webdrivers
webdrivers (~> 5.0)
webmock

RUBY VERSION
Expand Down
3 changes: 3 additions & 0 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# frozen_string_literal: true

module ApplicationHelper
def wallet_connected?
current_user.wallet.present?
end
end
11 changes: 11 additions & 0 deletions app/javascript/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,14 @@
import "@hotwired/turbo-rails"
import "./controllers"
import "../assets/stylesheets/application.tailwind.css";


document.addEventListener('DOMContentLoaded', () => {
const alertElement = document.querySelector('.alert');
if (alertElement) {
const alertMessage = alertElement.getAttribute('data-alert-message');
if (alertMessage) {
alert(alertMessage);
}
}
});
89 changes: 66 additions & 23 deletions app/javascript/controllers/wallet_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,30 @@ import { mainnet, arbitrum } from "viem/chains";
import { getAccount, reconnect } from "@wagmi/core";

export default class extends Controller {
static targets = ["openModal"];
static targets = ["openModal", 'createCampaignButton', 'tooltipText'];
static targets = ["openModal", "createCampaignButton", "tooltipText"];
static values = {
projectId: String,
userId: Number,
walletIdValue: Number,
csrfToken: String,
walletConnected: Boolean,
};

connect() {
this.isDeleting = false;
this.disconnectRequested = false;
this.walletConnectedValue = this.walletConnectedValue || false;

this.updateButtonState();

const projectId = this.projectIdValue;
const chains = [mainnet, arbitrum];
const metadata = {
name: 'Web3Modal',
description: 'Web3Modal Example',
url: 'http://localhost:3000',
icons: ['https://avatars.githubusercontent.com/u/37784886'],
name: "Web3Modal",
description: "Web3Modal Example",
url: "http://localhost:3000",
icons: ["https://avatars.githubusercontent.com/u/37784886"],
};

this.config = defaultWagmiConfig({
Expand All @@ -36,6 +41,14 @@ export default class extends Controller {
});
reconnect(this.config);

this.account = getAccount(this.config);
if (this.account && this.account.isConnected) {
this.walletConnectedValue = true;
} else {
this.walletConnectedValue = false;
}
this.updateButtonState();

this.modal = createWeb3Modal({
wagmiConfig: this.config,
projectId: this.projectIdValue,
Expand All @@ -45,20 +58,29 @@ export default class extends Controller {
this.modal.subscribeEvents((event) => {
this.account = getAccount(this.config);

if (event.data.event === 'CONNECT_SUCCESS') {
if (event.data.event === "CONNECT_SUCCESS") {
this.addWalletToDatabase(this.account);
this.openModalTarget.textContent = 'Disconnect Wallet';
} else if (event.data.event === 'MODAL_CLOSE' && this.disconnectRequested && !event.data.properties.connected) {
this.openModalTarget.textContent = "Disconnect Wallet";
this.walletConnectedValue = true;
this.updateButtonState();
} else if (
event.data.event === "MODAL_CLOSE" &&
this.disconnectRequested &&
!event.data.properties.connected
) {
this.removeWalletFromDatabase(this.account);
this.openModalTarget.textContent = 'Connect Wallet';
this.openModalTarget.textContent = "Connect Wallet";
this.walletConnectedValue = false;
this.updateButtonState();
} else {
console.log('Modal closed without disconnection');
console.log("Modal closed without disconnection");
}
});
}

openModal() {
const isDisconnect = this.openModalTarget.textContent === 'Disconnect Wallet';
const isDisconnect =
this.openModalTarget.textContent === "Disconnect Wallet";
this.disconnectRequested = isDisconnect;
this.modal.open();
}
Expand All @@ -72,10 +94,10 @@ export default class extends Controller {

try {
const response = await fetch(`/users/${this.userIdValue}/wallet`, {
method: 'POST',
method: "POST",
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': this.csrfTokenValue,
"Content-Type": "application/json",
"X-CSRF-Token": this.csrfTokenValue,
},
body: JSON.stringify(walletData),
});
Expand All @@ -85,25 +107,25 @@ export default class extends Controller {
}

const responseData = await response.json();
console.log('Successfully created wallet in database:', responseData);
console.log("Successfully created wallet in database:", responseData);
} catch (error) {
console.error('Error creating wallet in database:', error);
console.error("Error creating wallet in database:", error);
}
}

async removeWalletFromDatabase() {
if (this.isDeleting) {
console.log('Already deleting wallet, aborting.');
console.log("Already deleting wallet, aborting.");
return;
}
this.isDeleting = true;

try {
const response = await fetch(`/users/${this.userIdValue}/wallet`, {
method: 'DELETE',
method: "DELETE",
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': this.csrfTokenValue,
"Content-Type": "application/json",
"X-CSRF-Token": this.csrfTokenValue,
},
});

Expand All @@ -112,13 +134,34 @@ export default class extends Controller {
}

const responseData = await response.json();
console.log('Successfully removed from database:', responseData);
this.openModalTarget.textContent = 'Connect Wallet';
console.log("Successfully removed from database:", responseData);
this.openModalTarget.textContent = "Connect Wallet";
} catch (error) {
console.error('Error removing wallet from database:', error);
console.error("Error removing wallet from database:", error);
} finally {
this.isDeleting = false;
this.disconnectRequested = false;
}
}

updateButtonState() {
this.createCampaignButtonTargets.forEach((button, index) => {
const tooltip = this.tooltipTextTargets[index];
if (this.walletConnectedValue) {
button.disabled = false;
button.classList.remove("bg-gray-500", "cursor-not-allowed");
button.classList.add("bg-blue-500", "hover:bg-blue-700");

tooltip.classList.add("hidden");
tooltip.classList.remove("block");
} else {
button.disabled = true;
button.classList.remove("bg-blue-500", "hover:bg-blue-700");
button.classList.add("bg-gray-500", "cursor-not-allowed");

tooltip.classList.remove("hidden");
tooltip.classList.add("block");
}
});
}
}
57 changes: 37 additions & 20 deletions app/views/home/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
<div>
<div
data-controller="wallet"
data-wallet-project-id-value="<%= Rails.application.credentials.dig(:wallet_connect, :project_id) %>"
data-wallet-user-id-value="<%= current_user.id %>"
data-wallet-csrf-token-value="<%= form_authenticity_token %>"
>
<% wallet_connected = current_user.wallet.present? %>
<div class="flex items-center space-x-4">
<%= image_tag 'logo.jpg', alt: "Logo", class: 'my-6' %>
<%= image_tag @avatar, alt: "Avatar", class: 'w-24 h-24 rounded-full' %>
<%= button_to "logout", destroy_user_session_path, method: :delete %>
</div>
<p>
<%= "Hello, #{current_user.nickname}, welcome to Line - The Developers Marketplace " %>
<%= "Hello, #{current_user.nickname}, welcome to Line - The Developers Marketplace" %>
</p>
<% wallet_connected = current_user.wallet.present? %>
<div
data-controller="wallet"
data-wallet-project-id-value="<%= Rails.application.credentials.dig(:wallet_connect, :project_id) %>"
data-wallet-user-id-value="<%= current_user.id %>"
data-wallet-csrf-token-value="<%= form_authenticity_token %>"
>
<button
data-wallet-target="openModal"
data-action="click->wallet#openModal"
class="bg-blue-500 text-white font-bold py-2 px-4 rounded hover:bg-blue-700">
<%= wallet_connected ? 'Disconnect Wallet' : 'Connect Wallet' %>
</button>
</div>

<p>
<% if @organizations.present? %>
<ul>
Expand Down Expand Up @@ -57,10 +54,30 @@
</div>
<td>
<% campaign = @campaigns_by_repo_identifier[repo[:repo].full_name] %>
<% if campaign %>
<%= link_to 'Show Campaign', user_campaign_path(current_user, campaign, repo_identifier: repo[:repo].full_name), class: "btn btn-success" %>
<% if wallet_connected %>
<% if campaign %>
<%= link_to 'Show Campaign', user_campaign_path(current_user, campaign, repo_identifier: repo[:repo].full_name), class: "btn btn-success" %>
<% else %>
<%= link_to 'Create Campaign', new_user_campaign_path(current_user, repo_name: repo[:repo].full_name), class: "btn btn-primary" %>
<% end %>
<% else %>
<%= link_to 'Create Campaign', new_user_campaign_path(current_user, repo_name: repo[:repo].full_name), class: "btn btn-primary" %>
<div class="tooltip relative group">
<button
data-wallet-target="createCampaignButton"
type="button"
class="bg-gray-500 text-white font-bold py-2 px-4 rounded cursor-not-allowed"
disabled
>
Create Campaign
</button>
<span
id="tooltip-top"
data-wallet-target="tooltipText"
class="tooltip-text absolute z-10 px-3 py-2 text-sm font-medium text-white bg-gray-900 rounded-lg shadow-sm opacity-0 group-hover:opacity-100 transition-opacity"
>
Please connect a wallet to create campaigns
</span>
</div>
<% end %>
</td>
<td class="px-6 py-4 whitespace-nowrap">
Expand All @@ -75,12 +92,12 @@
</td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<p>No repos found.</p>
<% end %>
</div>
</table>
<% else %>
<p>No repos found.</p>
<% end %>
</div>

<script>
function copyToClipboard(text) {
if (navigator.clipboard && window.isSecureContext) {
Expand Down
Loading