diff --git a/qortal-ui-core/language/us.json b/qortal-ui-core/language/us.json
index 33ce763e..1e66e515 100644
--- a/qortal-ui-core/language/us.json
+++ b/qortal-ui-core/language/us.json
@@ -636,7 +636,14 @@
"bchange33": "Instant publish",
"bchange34": "Filename",
"bchange35": "Do you give this application permission to send coins?",
- "bchange36": "Do you want to publish instant to QDN without computing proof-of-work?"
+ "bchange36": "Do you want to publish instant to QDN without computing proof-of-work?",
+ "bchange37": "Do you give this application permission to add to this list?",
+ "bchange38": "Do you give this application permission to delete from this list?",
+ "bchange39": "Always allow lists to be retrieved automatically",
+ "bchange40": "List",
+ "bchange41": "Do you give this application permission to access this list?",
+ "bchange42": "Items"
+
},
"datapage": {
"dchange1": "Data Management",
diff --git a/qortal-ui-core/src/components/login-view/login-view.js b/qortal-ui-core/src/components/login-view/login-view.js
index 257bbf36..928de419 100644
--- a/qortal-ui-core/src/components/login-view/login-view.js
+++ b/qortal-ui-core/src/components/login-view/login-view.js
@@ -14,7 +14,7 @@ import './login-section.js'
import '../qort-theme-toggle.js'
import settings from '../../functional-components/settings-page.js'
-import { addAutoLoadImageChat, removeAutoLoadImageChat, addChatLastSeen, allowQAPPAutoAuth, removeQAPPAutoAuth } from '../../redux/app/app-actions.js'
+import { addAutoLoadImageChat, removeAutoLoadImageChat, addChatLastSeen, allowQAPPAutoAuth, removeQAPPAutoAuth, removeQAPPAutoLists, allowQAPPAutoLists } from '../../redux/app/app-actions.js'
window.reduxStore = store
window.reduxAction = {
@@ -22,7 +22,9 @@ window.reduxAction = {
removeAutoLoadImageChat: removeAutoLoadImageChat,
addChatLastSeen: addChatLastSeen,
allowQAPPAutoAuth: allowQAPPAutoAuth,
- removeQAPPAutoAuth: removeQAPPAutoAuth
+ removeQAPPAutoAuth: removeQAPPAutoAuth,
+ allowQAPPAutoLists: allowQAPPAutoLists,
+ removeQAPPAutoLists: removeQAPPAutoLists
}
const animationDuration = 0.7 // Seconds
diff --git a/qortal-ui-core/src/components/settings-view/security-view.js b/qortal-ui-core/src/components/settings-view/security-view.js
index f9facaea..aaee8212 100644
--- a/qortal-ui-core/src/components/settings-view/security-view.js
+++ b/qortal-ui-core/src/components/settings-view/security-view.js
@@ -1,7 +1,7 @@
import { LitElement, html, css } from 'lit'
import { connect } from 'pwa-helpers'
import { store } from '../../store.js'
-import { allowQAPPAutoAuth, removeQAPPAutoAuth } from '../../redux/app/app-actions.js'
+import { allowQAPPAutoAuth, removeQAPPAutoAuth, removeQAPPAutoLists, allowQAPPAutoLists } from '../../redux/app/app-actions.js'
import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate'
import '@material/mwc-checkbox'
@@ -115,6 +115,12 @@ class SecurityView extends connect(store)(LitElement) {
this.checkForAuth(e)} ?checked=${store.getState().app.qAPPAutoAuth}>
+
+
+ this.checkForLists(e)} ?checked=${store.getState().app.qAPPAutoLists}>
+
`
}
@@ -129,6 +135,13 @@ class SecurityView extends connect(store)(LitElement) {
store.dispatch(allowQAPPAutoAuth(true))
}
}
+ checkForLists(e) {
+ if (e.target.checked) {
+ store.dispatch(removeQAPPAutoLists(false))
+ } else {
+ store.dispatch(allowQAPPAutoLists(true))
+ }
+ }
checkForDownload() {
const checkPass = this.shadowRoot.getElementById('downloadBackupPassword').value
diff --git a/qortal-ui-core/src/redux/app/actions/app-core.js b/qortal-ui-core/src/redux/app/actions/app-core.js
index 43d5d57a..35767dae 100644
--- a/qortal-ui-core/src/redux/app/actions/app-core.js
+++ b/qortal-ui-core/src/redux/app/actions/app-core.js
@@ -1,5 +1,5 @@
// Core App Actions here...
-import { UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, CHAT_HEADS, ACCOUNT_INFO, COPY_MENU_SWITCH, PASTE_MENU_SWITCH, FRAME_PASTE_MENU_SWITCH, ADD_AUTO_LOAD_IMAGES_CHAT, REMOVE_AUTO_LOAD_IMAGES_CHAT, ALLOW_QAPP_AUTO_AUTH, REMOVE_QAPP_AUTO_AUTH, SET_CHAT_LAST_SEEN, ADD_CHAT_LAST_SEEN } from '../app-action-types.js'
+import { UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, CHAT_HEADS, ACCOUNT_INFO, COPY_MENU_SWITCH, PASTE_MENU_SWITCH, FRAME_PASTE_MENU_SWITCH, ADD_AUTO_LOAD_IMAGES_CHAT, REMOVE_AUTO_LOAD_IMAGES_CHAT, ALLOW_QAPP_AUTO_AUTH, REMOVE_QAPP_AUTO_AUTH, SET_CHAT_LAST_SEEN, ADD_CHAT_LAST_SEEN, ALLOW_QAPP_AUTO_LISTS, REMOVE_QAPP_AUTO_LISTS } from '../app-action-types.js'
export const doUpdateBlockInfo = (blockObj) => {
return (dispatch, getState) => {
@@ -133,6 +133,19 @@ export const removeQAPPAutoAuth = (payload) => {
payload
}
}
+export const allowQAPPAutoLists = (payload) => {
+ return {
+ type: ALLOW_QAPP_AUTO_LISTS,
+ payload
+ }
+}
+
+export const removeQAPPAutoLists = (payload) => {
+ return {
+ type: REMOVE_QAPP_AUTO_LISTS,
+ payload
+ }
+}
export const setChatLastSeen = (payload) => {
return {
diff --git a/qortal-ui-core/src/redux/app/app-action-types.js b/qortal-ui-core/src/redux/app/app-action-types.js
index 4125e6b1..5ace372c 100644
--- a/qortal-ui-core/src/redux/app/app-action-types.js
+++ b/qortal-ui-core/src/redux/app/app-action-types.js
@@ -24,5 +24,7 @@ export const ADD_AUTO_LOAD_IMAGES_CHAT = 'ADD_AUTO_LOAD_IMAGES_CHAT'
export const REMOVE_AUTO_LOAD_IMAGES_CHAT = 'REMOVE_AUTO_LOAD_IMAGES_CHAT'
export const ALLOW_QAPP_AUTO_AUTH = 'ALLOW_QAPP_AUTO_AUTH'
export const REMOVE_QAPP_AUTO_AUTH = 'REMOVE_QAPP_AUTO_AUTH'
+export const ALLOW_QAPP_AUTO_LISTS = 'ALLOW_QAPP_AUTO_LISTS'
+export const REMOVE_QAPP_AUTO_LISTS = 'REMOVE_QAPP_AUTO_LISTS'
export const SET_CHAT_LAST_SEEN = 'SET_CHAT_LAST_SEEN'
export const ADD_CHAT_LAST_SEEN = 'ADD_CHAT_LAST_SEEN'
diff --git a/qortal-ui-core/src/redux/app/app-reducer.js b/qortal-ui-core/src/redux/app/app-reducer.js
index 6e3849d1..0855ecbf 100644
--- a/qortal-ui-core/src/redux/app/app-reducer.js
+++ b/qortal-ui-core/src/redux/app/app-reducer.js
@@ -1,6 +1,6 @@
// Loading state, login state, isNavDrawOpen state etc. None of this needs to be saved to localstorage.
import { loadStateFromLocalStorage, saveStateToLocalStorage } from '../../localStorageHelpers.js'
-import { LOG_IN, LOG_OUT, NETWORK_CONNECTION_STATUS, INIT_WORKERS, ADD_PLUGIN_URL, ADD_PLUGIN, ADD_NEW_PLUGIN_URL, NAVIGATE, SELECT_ADDRESS, ACCOUNT_INFO, CHAT_HEADS, UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, LOAD_NODE_CONFIG, SET_NODE, ADD_NODE, PAGE_URL, COPY_MENU_SWITCH, PASTE_MENU_SWITCH, FRAME_PASTE_MENU_SWITCH, ADD_AUTO_LOAD_IMAGES_CHAT, REMOVE_AUTO_LOAD_IMAGES_CHAT, ALLOW_QAPP_AUTO_AUTH, REMOVE_QAPP_AUTO_AUTH, SET_CHAT_LAST_SEEN, ADD_CHAT_LAST_SEEN } from './app-action-types.js'
+import { LOG_IN, LOG_OUT, NETWORK_CONNECTION_STATUS, INIT_WORKERS, ADD_PLUGIN_URL, ADD_PLUGIN, ADD_NEW_PLUGIN_URL, NAVIGATE, SELECT_ADDRESS, ACCOUNT_INFO, CHAT_HEADS, UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, LOAD_NODE_CONFIG, SET_NODE, ADD_NODE, PAGE_URL, COPY_MENU_SWITCH, PASTE_MENU_SWITCH, FRAME_PASTE_MENU_SWITCH, ADD_AUTO_LOAD_IMAGES_CHAT, REMOVE_AUTO_LOAD_IMAGES_CHAT, ALLOW_QAPP_AUTO_AUTH, REMOVE_QAPP_AUTO_AUTH, SET_CHAT_LAST_SEEN, ADD_CHAT_LAST_SEEN, ALLOW_QAPP_AUTO_LISTS, REMOVE_QAPP_AUTO_LISTS } from './app-action-types.js'
import { initWorkersReducer } from './reducers/init-workers.js'
import { loginReducer } from './reducers/login-reducer.js'
import { setNode, addNode } from './reducers/manage-node.js'
@@ -51,6 +51,7 @@ const INITIAL_STATE = {
},
autoLoadImageChats: loadStateFromLocalStorage('autoLoadImageChats') || [],
qAPPAutoAuth: loadStateFromLocalStorage('qAPPAutoAuth') || false,
+ qAPPAutoLists: loadStateFromLocalStorage('qAPPAutoLists') || false,
chatLastSeen: []
}
@@ -192,6 +193,21 @@ export default (state = INITIAL_STATE, action) => {
qAPPAutoAuth: action.payload
}
}
+ case ALLOW_QAPP_AUTO_LISTS: {
+ saveStateToLocalStorage("qAPPAutoLists", true)
+ return {
+ ...state,
+ qAPPAutoLists: action.payload
+ }
+ }
+
+ case REMOVE_QAPP_AUTO_LISTS: {
+ saveStateToLocalStorage("qAPPAutoLists", false)
+ return {
+ ...state,
+ qAPPAutoLists: action.payload
+ }
+ }
case SET_CHAT_LAST_SEEN: {
return {
diff --git a/qortal-ui-plugins/plugins/core/components/qdn-action-types.js b/qortal-ui-plugins/plugins/core/components/qdn-action-types.js
index b51caa6f..1eb816a6 100644
--- a/qortal-ui-plugins/plugins/core/components/qdn-action-types.js
+++ b/qortal-ui-plugins/plugins/core/components/qdn-action-types.js
@@ -26,4 +26,13 @@ export const GET_WALLET_BALANCE = 'GET_WALLET_BALANCE';
export const SEND_COIN = 'SEND_COIN';
// PUBLISH_MULTIPLE_QDN_RESOURCES
-export const PUBLISH_MULTIPLE_QDN_RESOURCES = 'PUBLISH_MULTIPLE_QDN_RESOURCES'
\ No newline at end of file
+export const PUBLISH_MULTIPLE_QDN_RESOURCES = 'PUBLISH_MULTIPLE_QDN_RESOURCES'
+
+// GET_LIST_ITEMS
+export const GET_LIST_ITEMS = 'GET_LIST_ITEMS'
+
+// ADD_LIST_ITEMS
+export const ADD_LIST_ITEMS = 'ADD_LIST_ITEMS'
+
+// DELETE_LIST_ITEM
+export const DELETE_LIST_ITEM = 'DELETE_LIST_ITEM'
\ No newline at end of file
diff --git a/qortal-ui-plugins/plugins/core/qdn/browser/browser.src.js b/qortal-ui-plugins/plugins/core/qdn/browser/browser.src.js
index 8e69858b..13d0ec42 100644
--- a/qortal-ui-plugins/plugins/core/qdn/browser/browser.src.js
+++ b/qortal-ui-plugins/plugins/core/qdn/browser/browser.src.js
@@ -258,7 +258,7 @@ class WebBrowser extends LitElement {
${this.renderFollowUnfollowButton()}
-
@@ -526,7 +526,8 @@ class WebBrowser extends LitElement {
let data = event.data;
switch (data.action) {
- case actions.GET_USER_ACCOUNT:
+ case actions.GET_USER_ACCOUNT: {
+
let skip = false;
if (window.parent.reduxStore.getState().app.qAPPAutoAuth) {
skip = true;
@@ -555,11 +556,199 @@ class WebBrowser extends LitElement {
response = JSON.stringify(data);
break;
}
+ }
+ case actions.GET_LIST_ITEMS: {
+ const requiredFields = ['list_name'];
+ const missingFields = [];
+
+ requiredFields.forEach((field) => {
+ if (!data[field]) {
+ missingFields.push(field);
+ }
+ });
+
+ if (missingFields.length > 0) {
+ const missingFieldsString = missingFields.join(', ');
+ const errorMsg = `Missing fields: ${missingFieldsString}`
+ let data = {};
+ data['error'] = errorMsg;
+ response = JSON.stringify(data);
+ break
+ }
+ let skip = false;
+ if (window.parent.reduxStore.getState().app.qAPPAutoLists) {
+ skip = true;
+ }
+ let res1;
+ if (!skip) {
+ res1 = await showModalAndWait(
+ actions.GET_LIST_ITEMS,
+ {
+ list_name: data.list_name
+ }
+ );
+ };
+
+
+ if (res1 && res1.action === 'accept' || skip) {
+
+ try {
+ const list = await parentEpml.request('apiCall', {
+ type: 'api',
+ url: `/lists/${data.list_name}?apiKey=${this.getApiKey()}`,
+ });
+ response = JSON.stringify(list);
+
+ } catch (error) {
+ const data = {};
+ const errorMsg = "Error in retrieving list"
+ data['error'] = errorMsg;
+ response = JSON.stringify(data);
+ } finally {
+ break;
+ }
+
+ } else {
+ const data = {};
+ const errorMsg = "User declined to share list"
+ data['error'] = errorMsg;
+ response = JSON.stringify(data);
+ break;
+ }
+ };
+ case actions.ADD_LIST_ITEMS: {
+ const requiredFields = ['list_name', 'items'];
+ const missingFields = [];
+
+ requiredFields.forEach((field) => {
+ if (!data[field]) {
+ missingFields.push(field);
+ }
+ });
+
+ if (missingFields.length > 0) {
+ const missingFieldsString = missingFields.join(', ');
+ const errorMsg = `Missing fields: ${missingFieldsString}`
+ let data = {};
+ data['error'] = errorMsg;
+ response = JSON.stringify(data);
+ break
+ }
+ const items = data.items
+ const list_name = data.list_name
+ const res = await showModalAndWait(
+ actions.ADD_LIST_ITEMS,
+ {
+ list_name: list_name,
+ items: items
+ }
+ );
+
+ if (res && res.action === 'accept') {
+
+ try {
+ const body = {
+ items: items,
+ };
+
+ const bodyToString = JSON.stringify(body);
+ const data = await parentEpml.request('apiCall', {
+ type: 'api',
+ method: 'POST',
+ url: `/lists/${list_name}?apiKey=${this.getApiKey()}`,
+ body: bodyToString,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
+ response = data
+ } catch (error) {
+ const data = {};
+ const errorMsg = "Error in adding to list"
+ data['error'] = errorMsg;
+ response = JSON.stringify(data);
+ } finally {
+ break;
+ }
+
+ } else {
+ const data = {};
+ const errorMsg = "User declined add to list"
+ data['error'] = errorMsg;
+ response = JSON.stringify(data);
+ break;
+ }
+ };
+ case actions.DELETE_LIST_ITEM: {
+ const requiredFields = ['list_name', 'item'];
+ const missingFields = [];
+
+ requiredFields.forEach((field) => {
+ if (!data[field]) {
+ missingFields.push(field);
+ }
+ });
+
+ if (missingFields.length > 0) {
+ const missingFieldsString = missingFields.join(', ');
+ const errorMsg = `Missing fields: ${missingFieldsString}`
+ let data = {};
+ data['error'] = errorMsg;
+ response = JSON.stringify(data);
+ break
+ }
+ const item = data.item
+ const list_name = data.list_name
+ const res = await showModalAndWait(
+ actions.DELETE_LIST_ITEM,
+ {
+ list_name: list_name,
+ item: item
+ }
+ );
+
+ if (res && res.action === 'accept') {
+
+ try {
+ const body = {
+ items: [item],
+ };
+
+ const bodyToString = JSON.stringify(body);
+
+ const data = await parentEpml.request('apiCall', {
+ type: 'api',
+ method: 'DELETE',
+ url: `/lists/${list_name}?apiKey=${this.getApiKey()}`,
+ body: bodyToString,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
+ response = data
+ } catch (error) {
+ const data = {};
+ const errorMsg = "Error in adding to list"
+ data['error'] = errorMsg;
+ response = JSON.stringify(data);
+ } finally {
+ break;
+ }
+
+ } else {
+ const data = {};
+ const errorMsg = "User declined add to list"
+ data['error'] = errorMsg;
+ response = JSON.stringify(data);
+ break;
+ }
+ };
+
+
case actions.LINK_TO_QDN_RESOURCE:
case actions.QDN_RESOURCE_DISPLAYED:
// Links are handled by the core, but the UI also listens for these actions in order to update the address bar.
// Note: don't update this.url here, as we don't want to force reload the iframe each time.
-
if (this.preview != null && this.preview.length > 0) {
this.displayUrl = translate("appspage.schange40");
return;
@@ -2501,7 +2690,32 @@ async function showModalAndWait(type, data) {
${get("browserpage.bchange20")}
` : ''}
-
+ ${type === actions.GET_LIST_ITEMS ? `
+
+
${get("browserpage.bchange41")}
+
${get("browserpage.bchange40")}: ${data.list_name}
+
+
+
+
+
+ ` : ''}
+ ${type === actions.ADD_LIST_ITEMS ? `
+
+
${get("browserpage.bchange37")}
+
${get("browserpage.bchange40")}: ${data.list_name}
+
${get("browserpage.bchange42")}: ${data.items.join(', ')}
+
+ ` : ''}
+ ${type === actions.DELETE_LIST_ITEM ? `
+
+
${get("browserpage.bchange38")}
+
${get("browserpage.bchange40")}: ${data.list_name}
+
${get("browserpage.bchange42")}: ${data.item}
+
+ ` : ''}
${type === actions.SEND_CHAT_MESSAGE ? `
${get("browserpage.bchange22")}
` : ''}
@@ -2563,6 +2777,22 @@ async function showModalAndWait(type, data) {
window.parent.reduxStore.dispatch(window.parent.reduxAction.allowQAPPAutoAuth(true))
})
}
+ const labelButton2 = modal.querySelector('#listsButtonLabel');
+ if (labelButton2) {
+ labelButton2.addEventListener('click', () => {
+ this.shadowRoot.getElementById('listsButton').click();
+ })
+ }
+ const checkbox2 = modal.querySelector('#listsButton');
+ if (checkbox2) {
+ checkbox2.addEventListener('click', (e) => {
+ if (e.target.checked) {
+ window.parent.reduxStore.dispatch(window.parent.reduxAction.removeQAPPAutoLists(false))
+ return
+ }
+ window.parent.reduxStore.dispatch(window.parent.reduxAction.allowQAPPAutoLists(true))
+ })
+ }
});
}
@@ -2766,6 +2996,8 @@ const styles = `
font-weight: 300;
color: var(--black);
margin: 0;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
}
.capitalize-first {