Skip to content
Open
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
30 changes: 15 additions & 15 deletions src/lib/installPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,23 +82,23 @@ export default async function installPlugin(
},
);
} else {
// cordova http plugin for others
plugin = await new Promise((resolve, reject) => {
cordova.plugin.http.sendRequest(
const tempPath = cordova.file.cacheDirectory + "plugin_download.zip";

await new Promise((resolve, reject) => {
cordova.exec(resolve, reject, "Authenticator", "downloadPlugin", [
pluginUrl,
{
method: "GET",
responseType: "arraybuffer",
},
(response) => {
resolve(response.data);
loaderDialog.setMessage(`${strings.loading} 100%`);
},
(error) => {
reject(error);
},
);
tempPath,
]);
});

plugin = await fsOperation(tempPath).readFile(
undefined,
(loaded, total) => {
loaderDialog.setMessage(
`${strings.loading} ${((loaded / total) * 100).toFixed(2)}%`,
);
},
);
}

if (plugin) {
Expand Down
4 changes: 3 additions & 1 deletion src/pages/plugin/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,11 @@ export default async function PluginInclude(
]);
if (product) {
const purchase = await getPurchase(product.productId);
purchased = !!purchase;
purchased = !!purchase || remotePlugin.owned;
price = product.price;
purchaseToken = purchase?.purchaseToken;
} else if (remotePlugin.owned) {
purchased = true;
}
Comment thread
RohitKushvaha01 marked this conversation as resolved.
} catch (error) {
helpers.error(error);
Expand Down
197 changes: 120 additions & 77 deletions src/pages/plugins/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -416,95 +416,82 @@ export default function PluginsInclude(updates) {
}
}

async function retrieveFilteredPlugins(filterState) {
//fetch plugins with the auth token
function fetchPlugins(url) {
return new Promise((resolve, reject) => {
cordova.exec(
(items) => resolve(items),
(err) => reject(new Error(err)),
"Authenticator",
"fetchPlugins",
[url]
);
});
}

async function retrieveFilteredPlugins(filterState) {
if (!filterState) return { items: [], hasMore: false };

if (filterState.type === "orderBy") {
const page = filterState.nextPage || 1;
try {
let response;
if (filterState.value === "top_rated") {
response = await fetch(
withSupportedEditor(
`${constants.API_BASE}/plugins?explore=random&page=${page}&limit=${LIMIT}`,
),
);
} else {
response = await fetch(
withSupportedEditor(
`${constants.API_BASE}/plugin?orderBy=${filterState.value}&page=${page}&limit=${LIMIT}`,
),
);
}
const items = await response.json();
if (!Array.isArray(items)) {
return { items: [], hasMore: false };
const page = filterState.nextPage || 1;
try {
let url;
if (filterState.value === "top_rated") {
url = withSupportedEditor(`${constants.API_BASE}/plugins?explore=random&page=${page}&limit=${LIMIT}`);
} else {
url = withSupportedEditor(`${constants.API_BASE}/plugin?orderBy=${filterState.value}&page=${page}&limit=${LIMIT}`);
}

const items = await fetchPlugins(url);
filterState.nextPage = page + 1;
return { items, hasMore: items.length === LIMIT };
} catch (error) {
console.error("Failed to fetch ordered plugins:", error);
return { items: [], hasMore: false };
}
filterState.nextPage = page + 1;
const hasMoreResults = items.length === LIMIT;
return { items, hasMore: hasMoreResults };
} catch (error) {
console.error("Failed to fetch ordered plugins:", error);
return { items: [], hasMore: false };
}
}

if (!Array.isArray(filterState.buffer)) {
filterState.buffer = [];
}
if (filterState.hasMoreSource === undefined) {
filterState.hasMoreSource = true;
}
if (!filterState.nextPage) {
filterState.nextPage = 1;
}
if (!Array.isArray(filterState.buffer)) filterState.buffer = [];
if (filterState.hasMoreSource === undefined) filterState.hasMoreSource = true;
if (!filterState.nextPage) filterState.nextPage = 1;

const items = [];

while (items.length < LIMIT) {
if (filterState.buffer.length) {
items.push(filterState.buffer.shift());
continue;
}
if (filterState.buffer.length) {
items.push(filterState.buffer.shift());
continue;
}

if (filterState.hasMoreSource === false) break;
if (filterState.hasMoreSource === false) break;

try {
const page = filterState.nextPage;
const response = await fetch(
withSupportedEditor(`${constants.API_BASE}/plugins?page=${page}&limit=${LIMIT}`),
);
const data = await response.json();
filterState.nextPage = page + 1;
try {
const url = withSupportedEditor(`${constants.API_BASE}/plugins?page=${filterState.nextPage}&limit=${LIMIT}`);
const data = await fetchPlugins(url); // <-- java call
filterState.nextPage++;

if (!Array.isArray(data) || !data.length) {
filterState.hasMoreSource = false;
break;
}
if (!Array.isArray(data) || !data.length) {
filterState.hasMoreSource = false;
break;
}

if (data.length < LIMIT) {
filterState.hasMoreSource = false;
}
if (data.length < LIMIT) filterState.hasMoreSource = false;

const matched = data.filter((plugin) => matchesFilter(plugin, filterState));
filterState.buffer.push(...matched);
} catch (error) {
console.error("Failed to fetch filtered plugins:", error);
filterState.hasMoreSource = false;
break;
}
filterState.buffer.push(...data.filter(plugin => matchesFilter(plugin, filterState)));
} catch (error) {
console.error("Failed to fetch filtered plugins:", error);
filterState.hasMoreSource = false;
break;
}
}

while (items.length < LIMIT && filterState.buffer.length) {
items.push(filterState.buffer.shift());
items.push(filterState.buffer.shift());
}

const hasMoreResults =
(filterState.hasMoreSource !== false && filterState.nextPage) ||
filterState.buffer.length > 0;

const hasMoreResults = (filterState.hasMoreSource !== false && filterState.nextPage) || filterState.buffer.length > 0;
return { items, hasMore: Boolean(hasMoreResults) };
}
}

function matchesFilter(plugin, filterState) {
if (!plugin) return false;
Expand Down Expand Up @@ -559,6 +546,7 @@ export default function PluginsInclude(updates) {
return [];
}

//auth token is not needed here as this endpoint only returns public plugins and the server handles the filtering based on supported editor
async function getAllPlugins() {
if (currentFilter) return;
if (isLoading || !hasMore) return;
Expand Down Expand Up @@ -631,30 +619,85 @@ export default function PluginsInclude(updates) {
);
$list.installed.setAttribute("empty-msg", strings["no plugins found"]);
}
async function getOwned() {
$list.owned.setAttribute("empty-msg", strings["loading..."]);

async function getOwned() {
$list.owned.setAttribute("empty-msg", strings["loading..."]);
const disabledMap = settings.value.pluginsDisabled || {};
const addedIds = new Set();

// -------------------
// Google Play / IAP
// -------------------
try {
const purchases = await helpers.promisify(iap.getPurchases);
const disabledMap = settings.value.pluginsDisabled || {};

purchases.forEach(async ({ productIds }) => {
const [sku] = productIds;
const url = Url.join(constants.API_BASE, "plugin/owned", sku);
const plugin = await fsOperation(url).readFile("json");
const isInstalled = plugins.installed.find(({ id }) => id === plugin.id);
await Promise.all(
purchases.map(async ({ productIds }) => {
const [sku] = productIds;

try {
const url = Url.join(constants.API_BASE, "plugin/owned", sku);
const plugin = await fsOperation(url).readFile("json");

if (!plugin || addedIds.has(plugin.id)) return;

const isInstalled = plugins.installed.find(
({ id }) => id === plugin.id
);

plugin.installed = !!isInstalled;

if (plugin.installed) {
plugin.enabled = disabledMap[plugin.id] !== true;
plugin.onToggleEnabled = onToggleEnabled;
}

addedIds.add(plugin.id);
plugins.owned.push(plugin);
$list.owned.append(<Item {...plugin} updates={updates} />);
} catch (err) {
console.error("Failed to load owned IAP plugin:", err);
}
})
);
} catch (err) {
console.warn("IAP unavailable", err);
}

// -------------------
// Razorpay purchases
// -------------------
try {
const url = withSupportedEditor(
`${constants.API_BASE}/plugins?owned=true`
);

const ownedPlugins = await fetchPlugins(url);

ownedPlugins.forEach((plugin) => {
if (!plugin || addedIds.has(plugin.id)) return;

const isInstalled = plugins.installed.find(
({ id }) => id === plugin.id
);

plugin.installed = !!isInstalled;

if (plugin.installed) {
plugin.enabled = disabledMap[plugin.id] !== true;
plugin.onToggleEnabled = onToggleEnabled;
}

addedIds.add(plugin.id);
plugins.owned.push(plugin);
$list.owned.append(<Item {...plugin} updates={updates} />);
});
$list.owned.setAttribute("empty-msg", strings["no plugins found"]);
} catch (err) {
console.error("Failed to fetch owned plugins:", err);
}

$list.owned.setAttribute("empty-msg", strings["no plugins found"]);
}
function onInstall(plugin) {
if (updates) return;

Expand Down
1 change: 1 addition & 0 deletions src/plugins/auth/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<framework src="androidx.security:security-crypto:1.1.0" />

<source-file src="src/android/Authenticator.java" target-dir="src/com/foxdebug/acode/rk/auth" />
<source-file src="src/android/PluginRetriever.java" target-dir="src/com/foxdebug/acode/rk/auth" />
<source-file src="src/android/EncryptedPreferenceManager.java" target-dir="src/com/foxdebug/acode/rk/auth" />


Expand Down
33 changes: 30 additions & 3 deletions src/plugins/auth/src/android/Authenticator.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
import org.json.JSONObject;

public class Authenticator extends CordovaPlugin {
// Standard practice: use a TAG for easy filtering in Logcat
private static final String TAG = "AcodeAuth";
private static final String PREFS_FILENAME = "acode_auth_secure";
private static final String KEY_TOKEN = "auth_token";
Expand Down Expand Up @@ -42,6 +42,33 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo
prefManager.setString(KEY_TOKEN, token);
callbackContext.success();
return true;
case "downloadPlugin":
cordova.getThreadPool().execute(() -> {
try {
PluginRetriever.downloadPlugin(
prefManager.getString(KEY_TOKEN, null),
args.getString(0),
args.getString(1),
callbackContext
);
} catch (Exception e) {
Log.e(TAG, "downloadPlugin error: " + e.getMessage(), e);
callbackContext.error("Error: " + e.getMessage());
}
});
return true;
case "fetchPlugins":
cordova.getThreadPool().execute(() -> {
try {
String url = args.getString(0);
JSONArray items = PluginRetriever.fetchJsonArray(url, prefManager.getString(KEY_TOKEN, null));
callbackContext.success(items != null ? items : new JSONArray());
} catch (Exception e) {
Log.e(TAG, "fetchPlugins error: " + e.getMessage(), e);
callbackContext.error("Error: " + e.getMessage());
}
});
return true;
default:
Log.w(TAG, "Attempted to call unknown action: " + action);
return false;
Expand Down Expand Up @@ -114,12 +141,12 @@ private String validateToken(String token) {
HttpURLConnection conn = null;
try {
Log.d(TAG, "Network Request: Connecting to https://acode.app/api/login");
URL url = new URL("https://acode.app/api/login"); // Changed from /api to /api/login
URL url = new URL("https://acode.app/api/login");
conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("x-auth-token", token);
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000); // Add read timeout too
conn.setReadTimeout(5000);

int code = conn.getResponseCode();
Log.d(TAG, "Server responded with status code: " + code);
Expand Down
Loading