@monygroupcorp/micro-web3 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,238 @@
1
+ import { Component } from '@monygroupcorp/microact';
2
+
3
+ export class TransactionOptions extends Component {
4
+ constructor(props) {
5
+ super(props);
6
+ this.eventBus = props.eventBus;
7
+
8
+ this.state = {
9
+ nftMintingEnabled: props.nftMintingEnabled || false,
10
+ message: props.message || '',
11
+ isValid: props.isValid !== undefined ? props.isValid : true,
12
+ nftBalance: props.nftBalance || 0,
13
+ swapDirection: props.swapDirection || 'buy',
14
+ lastValidationState: true,
15
+ isPhase2: props.isPhase2 === null ? null : props.isPhase2
16
+ };
17
+
18
+ this.handleMessageInput = this.handleMessageInput.bind(this);
19
+ this.handleNFTToggle = this.handleNFTToggle.bind(this);
20
+ this.handleStoreUpdate = this.handleStoreUpdate.bind(this);
21
+
22
+ this.updateTimer = null;
23
+ }
24
+
25
+ onMount() {
26
+ this.contractDataUnsubscribe = this.eventBus.on('contractData:updated', (data) => {
27
+ this.updateProps({ isPhase2: data.liquidityPool && data.liquidityPool !== '0x0000000000000000000000000000000000000000' });
28
+ });
29
+
30
+ this.addEventListeners();
31
+ this.updateElements();
32
+ }
33
+
34
+ onUnmount() {
35
+ if (this.contractDataUnsubscribe) this.contractDataUnsubscribe();
36
+ this.removeEventListeners();
37
+ }
38
+
39
+ updateProps(newProps) {
40
+ const newState = { ...this.state, ...newProps };
41
+ const shouldUpdate = this.state.isPhase2 !== newState.isPhase2 ||
42
+ this.state.swapDirection !== newState.swapDirection ||
43
+ this.state.nftBalance !== newState.nftBalance;
44
+
45
+ this.setState(newState);
46
+
47
+ if (shouldUpdate) {
48
+ this.validateTransaction();
49
+ this.update(); // Re-render if phase or other critical props change
50
+ } else {
51
+ this.updateElements(); // Just update dynamic parts
52
+ }
53
+ }
54
+
55
+ addEventListeners() {
56
+ const messageInput = this.element.querySelector('#messageInput');
57
+ const nftToggle = this.element.querySelector('#nftToggle');
58
+
59
+ if (messageInput) {
60
+ this._messageInput = messageInput;
61
+ messageInput.addEventListener('input', this.handleMessageInput);
62
+ }
63
+ if (nftToggle) {
64
+ this._nftToggle = nftToggle;
65
+ nftToggle.addEventListener('change', this.handleNFTToggle);
66
+ }
67
+ }
68
+
69
+ removeEventListeners() {
70
+
71
+ if (this._messageInput) {
72
+ this._messageInput.removeEventListener('input', this.handleMessageInput);
73
+ }
74
+
75
+ if (this._nftToggle) {
76
+ this._nftToggle.removeEventListener('change', this.handleNFTToggle);
77
+ }
78
+ }
79
+
80
+ validateTransaction() {
81
+ const isMessageValid = this.state.message.length <= 140;
82
+ const isNFTValid = !this.state.nftMintingEnabled || this.state.nftBalance < 10;
83
+ const newIsValid = isMessageValid && isNFTValid;
84
+
85
+ // Only emit if validation state has changed
86
+ if (this.state.lastValidationState !== newIsValid) {
87
+ this.state.lastValidationState = newIsValid;
88
+ this.state.isValid = newIsValid;
89
+
90
+ this.eventBus.emit('transactionValidation', {
91
+ isValid: newIsValid,
92
+ errors: this.getValidationErrors()
93
+ });
94
+ }
95
+ }
96
+
97
+ getValidationErrors() {
98
+ const errors = [];
99
+
100
+ if (this.state.message.length > 140) {
101
+ errors.push('Message must be 140 characters or less');
102
+ }
103
+
104
+ if (this.state.nftMintingEnabled && this.state.nftBalance >= 10) {
105
+ errors.push('Cannot mint more than 10 NFTs');
106
+ }
107
+
108
+ return errors;
109
+ }
110
+
111
+ updateElements() {
112
+
113
+ // Update message input if it's not focused
114
+ const messageInput = this.element.querySelector('#messageInput');
115
+ const nftToggle = this.element.querySelector('#nftToggle');
116
+
117
+ if (messageInput && !messageInput.matches(':focus')) {
118
+ messageInput.value = this.state.message;
119
+ }
120
+
121
+ if (nftToggle) {
122
+ nftToggle.checked = this.state.nftMintingEnabled;
123
+ }
124
+
125
+ // Re-attach event listeners after updating elements
126
+ this.removeEventListeners();
127
+ this.addEventListeners();
128
+
129
+ // Update character count
130
+ const characterCount = this.element.querySelector('.character-count');
131
+ if (characterCount) {
132
+ characterCount.textContent = `${this.state.message.length}/140`;
133
+ characterCount.className = `character-count ${this.state.message.length > 140 ? 'error' : ''}`;
134
+ }
135
+
136
+ // Update validation status
137
+ const validationStatus = this.element.querySelector('.validation-status');
138
+ if (validationStatus) {
139
+ validationStatus.className = `validation-status ${this.state.isValid ? 'valid' : 'invalid'}`;
140
+ validationStatus.innerHTML = this.getValidationErrors()
141
+ .map(error => `<p class="error">${error}</p>`)
142
+ .join('');
143
+ }
144
+ }
145
+
146
+ handleMessageInput(event) {
147
+ const newMessage = event.target.value || '';
148
+
149
+ // Update state without triggering a re-render
150
+ this.state = {
151
+ ...this.state,
152
+ message: newMessage
153
+ };
154
+
155
+ // Update elements and ensure listeners
156
+ this.updateElements();
157
+
158
+ this.emitStateUpdate();
159
+ }
160
+
161
+ handleNFTToggle(event) {
162
+ const isEnabled = event.target.checked;
163
+
164
+ // Update state without triggering a re-render
165
+ this.state = {
166
+ ...this.state,
167
+ nftMintingEnabled: isEnabled
168
+ };
169
+
170
+ // Update elements and ensure listeners
171
+ this.updateElements();
172
+
173
+ this.emitStateUpdate();
174
+ }
175
+
176
+ // New method to emit state updates
177
+ emitStateUpdate() {
178
+ const updatePayload = {
179
+ message: this.state.message,
180
+ nftMintingEnabled: this.state.nftMintingEnabled
181
+ };
182
+ this.eventBus.emit('transactionOptions:update', updatePayload);
183
+ }
184
+
185
+ checkPhase2Status() {
186
+ const { isPhase2 } = this.state;
187
+ if (isPhase2 === null) {
188
+ // Phase not yet determined
189
+ return;
190
+ }
191
+ if (this.state.isPhase2 !== isPhase2) {
192
+ this.setState({ isPhase2 });
193
+ }
194
+ }
195
+
196
+ template() {
197
+ const { nftMintingEnabled, message, isValid, isPhase2, swapDirection } = this.state;
198
+
199
+ if (isPhase2 === null) {
200
+ return '';
201
+ }
202
+ if (isPhase2) {
203
+ return ''; // No options in Phase 2
204
+ }
205
+
206
+ return `
207
+ <div class="transaction-options">
208
+ <div class="option-group ${swapDirection === 'sell' ? 'hidden' : ''}">
209
+ <label class="nft-toggle">
210
+ <input type="checkbox"
211
+ ${nftMintingEnabled ? 'checked' : ''}
212
+ id="nftToggle">
213
+ Mint NFT with transaction
214
+ </label>
215
+ </div>
216
+
217
+ <div class="option-group">
218
+ <label for="messageInput">Transaction Message</label>
219
+ <textarea id="messageInput"
220
+ maxlength="140"
221
+ placeholder="Enter optional message..."
222
+ >${message}</textarea>
223
+ <span class="character-count ${message.length > 140 ? 'error' : ''}">
224
+ ${message.length}/140
225
+ </span>
226
+ </div>
227
+
228
+ <div class="validation-status ${isValid ? 'valid' : 'invalid'}">
229
+ ${this.getValidationErrors().map(error => `<p class="error">${error}</p>`).join('')}
230
+ </div>
231
+ </div>
232
+ `;
233
+ }
234
+
235
+ render() {
236
+ return this.template();
237
+ }
238
+ }
@@ -0,0 +1,159 @@
1
+ import { Component } from '@monygroupcorp/microact';
2
+
3
+ export class MessagePopup extends Component {
4
+ constructor(props) {
5
+ super(props);
6
+ this.state = {
7
+ messages: []
8
+ };
9
+ }
10
+
11
+ show(options) {
12
+ const { title = '', message = '', type = 'info', duration = 5000 } = options;
13
+ const id = Date.now() + Math.random();
14
+
15
+ const newMessage = { id, title, message, type };
16
+ this.setState({ messages: [...this.state.messages, newMessage] });
17
+
18
+ if (duration > 0) {
19
+ setTimeout(() => this.close(id), duration);
20
+ }
21
+ }
22
+
23
+ close(id) {
24
+ const messages = this.state.messages.filter(msg => msg.id !== id);
25
+ this.setState({ messages });
26
+ }
27
+
28
+ error(message, title = 'Error') {
29
+ this.show({ title, message, type: 'error' });
30
+ }
31
+
32
+ success(message, title = 'Success') {
33
+ this.show({ title, message, type: 'success' });
34
+ }
35
+
36
+ warning(message, title = 'Warning') {
37
+ this.show({ title, message, type: 'warning' });
38
+ }
39
+
40
+ info(message, title = 'Info') {
41
+ this.show({ title, message, type: 'info' });
42
+ }
43
+
44
+ render() {
45
+ return `
46
+ <div id="message-popup-container">
47
+ ${this.state.messages.map(msg => `
48
+ <div class="message-popup ${msg.type}" data-id="${msg.id}">
49
+ <div class="message-content">
50
+ ${msg.title ? `<div class="message-title">${msg.title}</div>` : ''}
51
+ <div class="message-text">${msg.message}</div>
52
+ </div>
53
+ <button class="close-button" data-id="${msg.id}">&times;</button>
54
+ </div>
55
+ `).join('')}
56
+ </div>
57
+ `;
58
+ }
59
+
60
+ events() {
61
+ return {
62
+ 'click .close-button': (e) => {
63
+ const id = parseFloat(e.target.dataset.id);
64
+ this.close(id);
65
+ }
66
+ };
67
+ }
68
+
69
+ static get styles() {
70
+ return `
71
+ #message-popup-container {
72
+ position: fixed;
73
+ top: 20px;
74
+ right: 20px;
75
+ z-index: 10000;
76
+ display: flex;
77
+ flex-direction: column;
78
+ gap: 10px;
79
+ }
80
+
81
+ .message-popup {
82
+ background: #ffffff;
83
+ border-radius: 8px;
84
+ padding: 15px 20px;
85
+ min-width: 300px;
86
+ max-width: 400px;
87
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
88
+ display: flex;
89
+ justify-content: space-between;
90
+ align-items: flex-start;
91
+ animation: slideIn 0.3s ease;
92
+ border-left: 4px solid #2196F3;
93
+ }
94
+
95
+ .message-popup.error {
96
+ border-left-color: #f44336;
97
+ }
98
+
99
+ .message-popup.success {
100
+ border-left-color: #4CAF50;
101
+ }
102
+
103
+ .message-popup.warning {
104
+ border-left-color: #ff9800;
105
+ }
106
+
107
+ .message-content {
108
+ flex-grow: 1;
109
+ margin-right: 10px;
110
+ }
111
+
112
+ .message-title {
113
+ font-weight: bold;
114
+ margin-bottom: 5px;
115
+ }
116
+
117
+ .message-text {
118
+ font-size: 14px;
119
+ color: #666;
120
+ }
121
+
122
+ .close-button {
123
+ background: none;
124
+ border: none;
125
+ color: #999;
126
+ cursor: pointer;
127
+ font-size: 20px;
128
+ padding: 0;
129
+ line-height: 1;
130
+ }
131
+
132
+ .close-button:hover {
133
+ color: #666;
134
+ }
135
+
136
+ @keyframes slideIn {
137
+ from {
138
+ transform: translateX(100%);
139
+ opacity: 0;
140
+ }
141
+ to {
142
+ transform: translateX(0);
143
+ opacity: 1;
144
+ }
145
+ }
146
+
147
+ @keyframes fadeOut {
148
+ from {
149
+ transform: translateX(0);
150
+ opacity: 1;
151
+ }
152
+ to {
153
+ transform: translateX(100%);
154
+ opacity: 0;
155
+ }
156
+ }
157
+ `;
158
+ }
159
+ }
@@ -0,0 +1,69 @@
1
+ import { Component } from '@monygroupcorp/microact';
2
+
3
+ export class WalletModal extends Component {
4
+ constructor(props) {
5
+ super(props);
6
+ this.providerMap = props.providerMap;
7
+ this.walletIcons = props.walletIcons;
8
+ this.onWalletSelected = props.onWalletSelected;
9
+
10
+ this.state = {
11
+ isVisible: false,
12
+ };
13
+ }
14
+
15
+ events() {
16
+ return {
17
+ 'click .wallet-modal-close': this.hide,
18
+ 'click .wallet-modal-overlay': (e) => {
19
+ if (e.target === e.currentTarget) this.hide();
20
+ },
21
+ 'click .wallet-option': async (e) => {
22
+ const walletType = e.currentTarget.dataset.wallet;
23
+ if (this.onWalletSelected) {
24
+ await this.onWalletSelected(walletType);
25
+ }
26
+ this.hide();
27
+ }
28
+ };
29
+ }
30
+
31
+ show() {
32
+ this.setState({ isVisible: true });
33
+ }
34
+
35
+ hide() {
36
+ this.setState({ isVisible: false });
37
+ }
38
+
39
+ render() {
40
+ if (!this.state.isVisible) {
41
+ return '<div class="wallet-modal-container" style="display: none;"></div>';
42
+ }
43
+
44
+ const walletOptions = Object.keys(this.providerMap).map(walletType => `
45
+ <button class="wallet-option" data-wallet="${walletType}">
46
+ <img src="${this.walletIcons[walletType]}" alt="${walletType}">
47
+ <span>${walletType.charAt(0).toUpperCase() + walletType.slice(1)}</span>
48
+ </button>
49
+ `).join('');
50
+
51
+ return `
52
+ <div class="wallet-modal-container">
53
+ <div class="wallet-modal-overlay">
54
+ <div class="wallet-modal">
55
+ <div class="wallet-modal-header">
56
+ <h3>Select Your Wallet</h3>
57
+ <button class="wallet-modal-close">&times;</button>
58
+ </div>
59
+ <div class="wallet-options">
60
+ ${walletOptions}
61
+ </div>
62
+ </div>
63
+ </div>
64
+ </div>
65
+ `;
66
+ }
67
+ }
68
+
69
+ export default WalletModal;