-
API Provider
- {providersWithCustom.map((provider, i) => (
-
- ))}
-
- {showCustomProviderInputs && (
-
-
Custom Provider
-
- handleCustomProviderChange("apiEndpoint", e.target.value)
- }
- className="input input-bordered w-full mb-2"
- />
-
- handleCustomProviderChange("rpcEndpoint", e.target.value)
- }
- className="input input-bordered w-full mb-2"
- />
- {validationError && (
-
{validationError}
- )}
+
+
+
+ {activeTab === 'api' && (
+
+ {providersWithCustom.map((provider, i) => (
+
+ ))}
+ {showCustomProviderInputs && (
+
+
Custom Provider
+
+ handleCustomProviderChange("apiEndpoint", e.target.value)
+ }
+ className="input input-bordered w-full mb-2"
+ />
+
+ handleCustomProviderChange("rpcEndpoint", e.target.value)
+ }
+ className="input input-bordered w-full mb-2"
+ />
+ {validationError && (
+
{validationError}
+ )}
+
+
+ )}
+
+ )}
+
+ {activeTab === 'storage' && (
+
+ {allStorageProviders.map((provider, i) => (
+
+ ))}
)}
diff --git a/components/repository/cloneRepoInfo.js b/components/repository/cloneRepoInfo.js
index 1664a469..9431ff55 100644
--- a/components/repository/cloneRepoInfo.js
+++ b/components/repository/cloneRepoInfo.js
@@ -1,40 +1,51 @@
import { useEffect, useState } from "react";
import { connect } from "react-redux";
import { notify } from "reapop";
+import { useApiClient } from "../../context/ApiClientContext";
-function CloneRepoInfo({ remoteUrl, backups, ...props }) {
+function CloneRepoInfo({ remoteUrl, repositoryId, ...props }) {
const [tab, setTab] = useState("gitopia");
const [cloneCmd, setCloneCmd] = useState("git clone " + remoteUrl);
- const [isIpfsEnabled, setIsIpfsEnabled] = useState(false);
- const [ipfsLatestCid, setIpfsLatestCid] = useState("");
- const [isArweaveEnabled, setIsArweaveEnabled] = useState(false);
- const [arweaveLatestCid, setArweaveLatestCid] = useState("");
+ const [storageInfo, setStorageInfo] = useState(null);
+ const { storageApiClient } = useApiClient();
useEffect(() => {
if (tab === "gitopia") {
setCloneCmd("git clone " + remoteUrl);
- } else if (tab === "ipfs") {
- setCloneCmd("ipfs_clone " + remoteUrl);
- } else if (tab === "arweave") {
- setCloneCmd("arweave_clone " + remoteUrl);
}
}, [tab, remoteUrl]);
useEffect(() => {
- setIsIpfsEnabled(false);
- setIsArweaveEnabled(false);
- if (backups) {
- backups.map((b) => {
- if (b.store === "IPFS" && b.refs?.length) {
- setIsIpfsEnabled(true);
- setIpfsLatestCid(b.refs[b.refs.length - 1]);
- } else if (b.store === "ARWEAVE" && b.refs?.length) {
- setIsArweaveEnabled(true);
- setArweaveLatestCid(b.refs[b.refs.length - 1]);
+ const fetchStorageInfo = async () => {
+ if (repositoryId && storageApiClient) {
+ try {
+ const response = await storageApiClient.queryRepositoryPackfile(repositoryId);
+ // log response
+ console.log(response);
+ setStorageInfo(response.data.packfile);
+ } catch (error) {
+ console.error("Error fetching storage info:", error);
}
- });
+ }
+ };
+
+ if (tab === "storage") {
+ fetchStorageInfo();
+ }
+ }, [tab, repositoryId, storageApiClient]);
+
+ const formatSize = (bytes) => {
+ const units = ['B', 'KB', 'MB', 'GB'];
+ let size = bytes;
+ let unitIndex = 0;
+
+ while (size >= 1024 && unitIndex < units.length - 1) {
+ size /= 1024;
+ unitIndex++;
}
- }, [backups]);
+
+ return `${size.toFixed(2)} ${units[unitIndex]}`;
+ };
return (
@@ -66,36 +77,17 @@ function CloneRepoInfo({ remoteUrl, backups, ...props }) {
>
Gitopia Server
- {isIpfsEnabled ? (
-
- ) : (
- ""
- )}
- {isArweaveEnabled ? (
-
- ) : (
- ""
- )}
+
{tab === "gitopia" ? (
<>
@@ -130,95 +122,80 @@ function CloneRepoInfo({ remoteUrl, backups, ...props }) {
Learn more
- >
- ) : (
- ""
- )}
-
- {tab === "ipfs" || tab === "arweave" ? (
-
-
- {tab === "ipfs" ? (
-

- ) : (
-

- )}
-
-
-
-
- ) : (
- ""
- )}
- {tab === "gitopia" ? (
-
-
-
- ) : (
- ""
- )}
-
+ >
+ ) : tab === "storage" ? (
+
+ {storageInfo ? (
+
+
+

+
Storage Information
+
+
+
+ Provider:
+
+ {storageInfo.creator}
+
+
+
+ Size:
+ {formatSize(parseInt(storageInfo.size))}
+
+
+ IPFS CID:
+
+ {storageInfo.cid}
+
+
+
+ Root Hash:
+
+ {storageInfo.root_hash}
+
+
+
+
+ ) : (
+
Loading storage information...
+ )}
+
+ ) : null}
);
diff --git a/components/repository/deleteRepository.js b/components/repository/deleteRepository.js
index e80811b6..812ea9a2 100644
--- a/components/repository/deleteRepository.js
+++ b/components/repository/deleteRepository.js
@@ -12,8 +12,12 @@ function DeleteRepository({
const [isDeleting, setIsDeleting] = useState(false);
const [confirmDelete, setConfirmDelete] = useState(false);
const [typedData, setTypedData] = useState("");
- const { apiClient, cosmosBankApiClient, cosmosFeegrantApiClient } =
- useApiClient();
+ const {
+ apiClient,
+ cosmosBankApiClient,
+ cosmosFeegrantApiClient,
+ storageProviderAddress,
+ } = useApiClient();
return (
@@ -27,7 +31,7 @@ function DeleteRepository({
@@ -87,14 +91,15 @@ function DeleteRepository({
apiClient,
cosmosBankApiClient,
cosmosFeegrantApiClient,
+ storageProviderAddress,
{
ownerId: currentOwnerId,
name: repoName,
}
)
.then(async (res) => {
- if (res.code == 0) {
- if (onSuccess) await onSuccess;
+ if (res && res.code == 0) {
+ if (onSuccess) await onSuccess();
setConfirmDelete(false);
setIsDeleting(false);
setTypedData("");
diff --git a/components/repository/diffView.js b/components/repository/diffView.js
index 7c9fa5d5..87c5c252 100644
--- a/components/repository/diffView.js
+++ b/components/repository/diffView.js
@@ -23,9 +23,9 @@ function DiffView({
commentsAllowed = true,
comments = [],
refreshComments,
- onViewTypeChange = () => {},
+ onViewTypeChange = () => { },
showFile = null,
- getCommentView = () => {},
+ getCommentView = () => { },
isPullDiff = false,
...props
}) {
@@ -38,7 +38,7 @@ function DiffView({
const [loading, setLoading] = useState(false);
const [scrollingToFile, setScrollingToFile] = useState(false);
const paginationLimit = 10;
- const { apiClient, cosmosBankApiClient, cosmosFeegrantApiClient } =
+ const { apiClient, cosmosBankApiClient, cosmosFeegrantApiClient, storageApiUrl } =
useApiClient();
const renderGutter = ({
@@ -329,6 +329,7 @@ function DiffView({
let data;
if (isPullDiff) {
data = await getPullDiff(
+ storageApiUrl,
baseRepoId,
repoId,
previousSha,
@@ -337,7 +338,7 @@ function DiffView({
limit
);
} else {
- data = await getDiff(repoId, currentSha, previousSha, offset, limit);
+ data = await getDiff(storageApiUrl, repoId, currentSha, previousSha, offset, limit);
}
let newFiles = [...oldFiles];
diff --git a/components/repository/mergePullRequestView.js b/components/repository/mergePullRequestView.js
index 9a90bf5f..a9a96191 100644
--- a/components/repository/mergePullRequestView.js
+++ b/components/repository/mergePullRequestView.js
@@ -3,14 +3,12 @@ import { useEffect, useState } from "react";
import { notify } from "reapop";
import {
updatePullRequestState,
- authorizeGitServer,
mergePullRequest,
mergePullRequestForDao,
} from "../../store/actions/repository";
import pullRequestStateClass from "../../helpers/pullRequestStateClass";
import mergePullRequestCheck from "../../helpers/mergePullRequestCheck";
import getPullRequestMergePermission from "../../helpers/getPullRequestMergePermission";
-import getGitServerAuthorization from "../../helpers/getGitServerAuthStatus";
import getDao from "../../helpers/getDao";
import { useApiClient } from "../../context/ApiClientContext";
import { useRouter } from "next/router";
@@ -25,10 +23,6 @@ function MergePullRequestView({
const [stateClass, setStateClass] = useState("");
const [iconType, setIconType] = useState("check");
const [message, setMessage] = useState("");
- const [pullMergeAccess, setPullMergeAccess] = useState(false);
- const [pullMergeAccessDialogShown, setPullMergeAccessDialogShown] =
- useState(false);
- const [isGrantingAccess, setIsGrantingAccess] = useState(false);
const [requiresProposal, setRequiresProposal] = useState(false);
const [isCreatingProposal, setIsCreatingProposal] = useState(false);
const {
@@ -36,6 +30,8 @@ function MergePullRequestView({
cosmosBankApiClient,
cosmosFeegrantApiClient,
cosmosGroupApiClient,
+ storageProviderAddress,
+ storageApiUrl,
} = useApiClient();
const [daoInfo, setDaoInfo] = useState(null);
const router = useRouter();
@@ -63,6 +59,7 @@ function MergePullRequestView({
}
setIsMerging(true);
const res = await mergePullRequestCheck(
+ storageApiUrl,
pullRequest.iid,
pullRequest.base.repositoryId,
pullRequest.head.repositoryId,
@@ -100,6 +97,7 @@ function MergePullRequestView({
cosmosBankApiClient,
cosmosFeegrantApiClient,
cosmosGroupApiClient,
+ storageProviderAddress,
{
repositoryId: repository.id,
iid: pullRequest.iid,
@@ -135,20 +133,11 @@ function MergePullRequestView({
);
if (user && user.havePermission) {
- let access = await getGitServerAuthorization(
- apiClient,
- props.selectedAddress
- );
- if (!access) {
- setPullMergeAccessDialogShown(true);
- setIsMerging(false);
- return;
- }
-
const res = await props.mergePullRequest(
apiClient,
cosmosBankApiClient,
cosmosFeegrantApiClient,
+ storageProviderAddress,
{
repositoryId: repository.id,
iid: pullRequest.iid,
@@ -183,17 +172,6 @@ function MergePullRequestView({
}
}, [pullRequest, props.selectedAddress, requiresProposal]);
- const refreshPullMergeAccess = async (mergeAfter = false) => {
- setPullMergeAccess(
- await getGitServerAuthorization(apiClient, props.selectedAddress)
- );
- if (mergeAfter) setTimeout(mergePull, 0);
- };
-
- useEffect(() => {
- refreshPullMergeAccess();
- }, [props.selectedAddress]);
-
const getMergeButtonText = () => {
if (isCreatingProposal) return "Creating Proposal...";
if (isMerging) return "Merging...";
@@ -270,9 +248,8 @@ function MergePullRequestView({
{pullRequest.state === "OPEN" && (
-
-
-
-
-
- Gitopia data server does not have repository merge access on behalf
- of your account.
-
-
Server Address:
-
- {process.env.NEXT_PUBLIC_GIT_SERVER_WALLET_ADDRESS}
-
-
-
-
-
-
-
);
}
@@ -347,7 +274,6 @@ const mapStateToProps = (state) => {
export default connect(mapStateToProps, {
notify,
updatePullRequestState,
- authorizeGitServer,
mergePullRequest,
mergePullRequestForDao,
})(MergePullRequestView);
diff --git a/components/repository/releaseView.js b/components/repository/releaseView.js
index cf0fa33d..c3f6dcac 100644
--- a/components/repository/releaseView.js
+++ b/components/repository/releaseView.js
@@ -5,18 +5,20 @@ import shrinkSha from "../../helpers/shrinkSha";
import { useState } from "react";
import MarkdownWrapper from "../markdownWrapper";
import AccountCard from "../account/card";
+import { useApiClient } from "../../context/ApiClientContext";
export default function ReleaseView({
release,
repository,
latest = false,
showEditControls = false,
- onDelete = () => {},
+ onDelete = () => { },
noLink = false,
...props
}) {
const [isDeleting, setIsDeleting] = useState(false);
const [confirmDelete, setConfirmDelete] = useState(false);
+ const { storageApiUrl } = useApiClient();
return (
@@ -140,7 +142,7 @@ export default function ReleaseView({
target="_blank"
rel="noreferrer"
href={
- process.env.NEXT_PUBLIC_OBJECTS_URL +
+ storageApiUrl +
"/releases/" +
repository.owner.id +
"/" +
diff --git a/components/repository/toggleGitServerAuthorization.js b/components/repository/toggleGitServerAuthorization.js
deleted file mode 100644
index 3bda903a..00000000
--- a/components/repository/toggleGitServerAuthorization.js
+++ /dev/null
@@ -1,76 +0,0 @@
-import { useEffect, useState } from "react";
-import { connect } from "react-redux";
-import { updateAddressGrant } from "../../store/actions/user";
-import getGitServerAuthStatus from "../../helpers/getGitServerAuthStatus";
-import { useApiClient } from "../../context/ApiClientContext";
-
-function ToggleGitServerAuthorization({ address, onSuccess, ...props }) {
- const [currentState, setCurrentState] = useState(false);
- const [isToggling, setIsToggling] = useState(true);
- const { apiClient, cosmosBankApiClient, cosmosFeeegrantApiClient } =
- useApiClient();
-
- const toggleGrant = async () => {
- setIsToggling(true);
- const res = await props.updateAddressGrant(
- apiClient,
- cosmosBankApiClient,
- cosmosFeeegrantApiClient,
- address,
- 0,
- !currentState
- );
- if (res && res.code === 0) {
- if (onSuccess) await onSuccess(!currentState);
- setCurrentState(!currentState);
- }
- setIsToggling(false);
- };
-
- useEffect(() => {
- async function initAddress() {
- setIsToggling(true);
- setCurrentState(await getGitServerAuthStatus(apiClient, address));
- setIsToggling(false);
- }
- initAddress();
- }, [address]);
-
- return (
-
- );
-}
-
-const mapStateToProps = (state) => {
- return {
- selectedAddress: state.wallet.selectedAddress,
- };
-};
-
-export default connect(mapStateToProps, { updateAddressGrant })(
- ToggleGitServerAuthorization
-);
diff --git a/components/repository/toggleStorageBridgeAuthorization.js b/components/repository/toggleStorageBridgeAuthorization.js
deleted file mode 100644
index d87f1102..00000000
--- a/components/repository/toggleStorageBridgeAuthorization.js
+++ /dev/null
@@ -1,76 +0,0 @@
-import { useEffect, useState } from "react";
-import { connect } from "react-redux";
-import { updateAddressGrant } from "../../store/actions/user";
-import getStorageBridgeAuthStatus from "../../helpers/getStorageBridgeAuthStatus";
-import { useApiClient } from "../../context/ApiClientContext";
-
-function ToggleStorageBridgeAuthorization({ address, onSuccess, ...props }) {
- const [currentState, setCurrentState] = useState(false);
- const [isToggling, setIsToggling] = useState(true);
- const { apiClient, cosmosBankApiClient, cosmosFeegrantApiClient } =
- useApiClient();
-
- const toggleGrant = async () => {
- setIsToggling(true);
- const res = await props.updateAddressGrant(
- apiClient,
- cosmosBankApiClient,
- cosmosFeegrantApiClient,
- address,
- 1,
- !currentState
- );
- if (res && res.code === 0) {
- if (onSuccess) await onSuccess(!currentState);
- setCurrentState(!currentState);
- }
- setIsToggling(false);
- };
-
- useEffect(() => {
- async function initAddress() {
- setIsToggling(true);
- setCurrentState(await getStorageBridgeAuthStatus(apiClient, address));
- setIsToggling(false);
- }
- initAddress();
- }, [address]);
-
- return (
-
- );
-}
-
-const mapStateToProps = (state) => {
- return {
- selectedAddress: state.wallet.selectedAddress,
- };
-};
-
-export default connect(mapStateToProps, { updateAddressGrant })(
- ToggleStorageBridgeAuthorization
-);
diff --git a/context/ApiClientContext.js b/context/ApiClientContext.js
index 46b899c8..8449ef7a 100644
--- a/context/ApiClientContext.js
+++ b/context/ApiClientContext.js
@@ -6,7 +6,11 @@ import { Api as CosmosFeegrantApi } from "../store/cosmos.feegrant.v1beta1/rest"
import { Api as CosmosGovApi } from "../store/cosmos.gov.v1beta1/module/rest";
import { Api as IbcAppTransferApi } from "../store/ibc.applications.transfer.v1/module/rest";
import { Api as CosmosGroupApi } from "../store/cosmos.group.v1/rest";
+import { Api as StorageApi } from "../store/gitopia.gitopia.storage/rest";
import selectProvider from "../helpers/providerSelector";
+import selectStorageProvider, {
+ getSavedStorageProvider,
+} from "../helpers/storageProviderSelector";
import { setConfig } from "../store/actions/env";
const ApiClientContext = createContext();
@@ -22,9 +26,14 @@ export const ApiClientProvider = ({ children }) => {
const [cosmosGovApiClient, setCosmosGovApiClient] = useState(null);
const [ibcAppTransferApiClient, setIbcAppTransferApiClient] = useState(null);
const [cosmosGroupApiClient, setCosmosGroupApiClient] = useState(null);
+ const [storageApiClient, setStorageApiClient] = useState(null);
const [providerName, setProviderName] = useState(null);
const [apiUrl, setApiUrl] = useState(null);
const [rpcUrl, setRpcUrl] = useState(null);
+ const [storageApiUrl, setStorageApiUrl] = useState(null);
+ const [storageProviderAddress, setStorageProviderAddress] = useState(null);
+ const [storageProviderName, setStorageProviderName] = useState(null);
+ const [allStorageProviders, setAllStorageProviders] = useState([]);
const [loading, setLoading] = useState(true);
const dispatch = useDispatch();
@@ -53,6 +62,13 @@ export const ApiClientProvider = ({ children }) => {
});
setCosmosGroupApiClient(newCosmosGroupApiClient);
+ const newStorageApiClient = new StorageApi({
+ baseURL: apiNode,
+ });
+ setStorageApiClient(newStorageApiClient);
+
+ updateStorageProvider(newStorageApiClient);
+
setProviderName(name);
setApiUrl(apiNode);
setRpcUrl(rpcNode);
@@ -67,6 +83,39 @@ export const ApiClientProvider = ({ children }) => {
dispatch(setConfig({ config: { apiNode, rpcNode } }));
};
+ const updateStorageProvider = async (client) => {
+ const res = await client.queryActiveProviders();
+ const providers = res.data.provider ?? [];
+ setAllStorageProviders(providers);
+
+ if (providers.length > 0) {
+ const savedProvider = getSavedStorageProvider();
+ if (
+ savedProvider &&
+ providers.some((p) => p.creator === savedProvider.creator)
+ ) {
+ setActiveStorageProvider(savedProvider);
+ return savedProvider;
+ }
+
+ const provider = await selectStorageProvider(providers);
+ if (provider) {
+ setActiveStorageProvider(provider);
+ return provider;
+ }
+ }
+ return null;
+ };
+
+ const setActiveStorageProvider = (provider) => {
+ if (provider) {
+ setStorageApiUrl(provider.apiUrl.replace(/\/$/, "")); // trim trailing slash
+ setStorageProviderAddress(provider.creator);
+ setStorageProviderName(provider.description);
+ localStorage.setItem("storageProviderInfo", JSON.stringify(provider));
+ }
+ };
+
useEffect(() => {
const cachedProviderInfo = localStorage.getItem("providerInfo");
@@ -107,7 +156,14 @@ export const ApiClientProvider = ({ children }) => {
cosmosGovApiClient,
ibcAppTransferApiClient,
cosmosGroupApiClient,
+ storageApiClient,
+ storageApiUrl,
+ storageProviderAddress,
+ storageProviderName,
+ allStorageProviders,
updateApiClient,
+ updateStorageProvider,
+ setActiveStorageProvider,
}}
>
{!loading && children}
diff --git a/helpers/checkLatency.js b/helpers/checkLatency.js
index 0519db7e..34dfddcc 100644
--- a/helpers/checkLatency.js
+++ b/helpers/checkLatency.js
@@ -1,13 +1,13 @@
import axios from "axios";
-const checkLatency = async (provider) => {
+const checkLatency = async (url) => {
const startTime = Date.now();
try {
- await axios.get(provider.rpcEndpoint);
+ await axios.get(url);
const endTime = Date.now();
return endTime - startTime;
} catch (error) {
- console.error(`Error checking latency for ${provider.rpcEndpoint}:`, error);
+ console.error(`Error checking latency for ${url}:`, error);
return Infinity;
}
};
diff --git a/helpers/forkRepositoryFiles.js b/helpers/forkRepositoryFiles.js
deleted file mode 100644
index 97672be3..00000000
--- a/helpers/forkRepositoryFiles.js
+++ /dev/null
@@ -1,24 +0,0 @@
-export default async function forkRepositoryFiles(sourceRepoId, targetRepoId) {
- if (!sourceRepoId || !targetRepoId) return;
- const baseUrl =
- process.env.NODE_ENV === "development"
- ? "/api/fork"
- : process.env.NEXT_PUBLIC_OBJECTS_URL + "/fork";
- let obj = {};
- let params = {
- source_repository_id: Number(sourceRepoId),
- target_repository_id: Number(targetRepoId),
- };
- await fetch(baseUrl, {
- method: "POST", // *GET, POST, PUT, DELETE, etc.
- mode: "cors", // no-cors, *cors, same-origin
- cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
- body: JSON.stringify(params),
- })
- .then((response) => {
- obj = response.json();
- })
- .catch((err) => console.error(err));
-
- return obj;
-}
diff --git a/helpers/getCommit.js b/helpers/getCommit.js
index e8ce531a..d59d8a6f 100644
--- a/helpers/getCommit.js
+++ b/helpers/getCommit.js
@@ -1,7 +1,7 @@
const validSha = new RegExp(/^[a-f0-9]{40}$/);
import axios from "../helpers/axiosFetch";
-export default async function getCommit(repoId = null, commitSha = null) {
+export default async function getCommit(storageApiUrl, repoId = null, commitSha = null) {
let obj = {};
if (repoId === null) {
return obj;
@@ -9,10 +9,7 @@ export default async function getCommit(repoId = null, commitSha = null) {
if (!validSha.test(commitSha)) {
return obj;
}
- let baseUrl =
- process.env.NODE_ENV === "development"
- ? "/api/commits"
- : process.env.NEXT_PUBLIC_OBJECTS_URL + "/commits";
+ let baseUrl = storageApiUrl + "/commits";
let params = {
repository_id: Number(repoId),
};
diff --git a/helpers/getCommitHistory.js b/helpers/getCommitHistory.js
index 0803ecb3..25e55034 100644
--- a/helpers/getCommitHistory.js
+++ b/helpers/getCommitHistory.js
@@ -3,6 +3,7 @@ import axios from "../helpers/axiosFetch";
const validSha = new RegExp(/^[a-f0-9]{40}$/);
export default async function getCommitHistory(
+ storageApiUrl,
repoId = null,
initCommitSha = null,
path = null,
@@ -14,7 +15,7 @@ export default async function getCommitHistory(
if (!validSha.test(initCommitSha)) {
return obj;
}
- let baseUrl = process.env.NEXT_PUBLIC_OBJECTS_URL + "/commits";
+ let baseUrl = storageApiUrl + "/commits";
let params = {
repository_id: Number(repoId),
init_commit_id: initCommitSha,
diff --git a/helpers/getContent.js b/helpers/getContent.js
index c017825d..7f1c6603 100644
--- a/helpers/getContent.js
+++ b/helpers/getContent.js
@@ -2,6 +2,7 @@ const validSha = new RegExp(/^[a-f0-9]{40}$/);
import axios from "../helpers/axiosFetch";
export default async function getContent(
+ storageApiUrl,
repoId = null,
commitSha = null,
path = null,
@@ -13,10 +14,7 @@ export default async function getContent(
if (!validSha.test(commitSha)) {
return obj;
}
- const baseUrl =
- process.env.NODE_ENV === "development"
- ? "/api/content"
- : process.env.NEXT_PUBLIC_OBJECTS_URL + "/content";
+ const baseUrl = storageApiUrl + "/content";
let params = {
repository_id: Number(repoId),
ref_id: commitSha,
diff --git a/helpers/getDiff.js b/helpers/getDiff.js
index 91347868..515822fa 100644
--- a/helpers/getDiff.js
+++ b/helpers/getDiff.js
@@ -2,6 +2,7 @@ import isNumber from "lodash/isNumber";
const validSha = new RegExp(/^[a-f0-9]{40}$/);
export default async function getDiff(
+ storageApiUrl,
repoId = null,
commitSha = null,
prevCommitSha = null,
@@ -16,10 +17,7 @@ export default async function getDiff(
if (!isNumber(numRepoId)) {
return obj;
}
- const baseUrl =
- process.env.NODE_ENV === "development"
- ? "/api/diff"
- : process.env.NEXT_PUBLIC_OBJECTS_URL + "/diff";
+ const baseUrl = storageApiUrl + "/diff";
let params = {
repository_id: numRepoId,
commit_sha: commitSha,
diff --git a/helpers/getDiffStats.js b/helpers/getDiffStats.js
index 2fe901a6..f425bf28 100644
--- a/helpers/getDiffStats.js
+++ b/helpers/getDiffStats.js
@@ -2,6 +2,7 @@ import isNumber from "lodash/isNumber";
const validSha = new RegExp(/^[a-f0-9]{40}$/);
export default async function getDiffStats(
+ storageApiUrl,
repoId = null,
commitSha = null,
prevCommitSha,
@@ -14,10 +15,7 @@ export default async function getDiffStats(
if (!isNumber(numRepoId)) {
return obj;
}
- const baseUrl =
- process.env.NODE_ENV === "development"
- ? "/api/diff"
- : process.env.NEXT_PUBLIC_OBJECTS_URL + "/diff";
+ const baseUrl = storageApiUrl + "/diff";
let params = {
repository_id: numRepoId,
commit_sha: commitSha,
diff --git a/helpers/getGitServerAuthStatus.js b/helpers/getGitServerAuthStatus.js
deleted file mode 100644
index a01edb57..00000000
--- a/helpers/getGitServerAuthStatus.js
+++ /dev/null
@@ -1,17 +0,0 @@
-export default async function getGitServerAuthStatus(apiClient, userAddress) {
- if (!userAddress) return null;
- try {
- const res = await apiClient.queryCheckGitServerAuthorization(
- userAddress,
- process.env.NEXT_PUBLIC_GIT_SERVER_WALLET_ADDRESS
- );
- if (res.status === 200 && res.data.haveAuthorization) {
- return true;
- } else {
- return false;
- }
- } catch (e) {
- console.error(e);
- return null;
- }
-}
diff --git a/helpers/getPullDiff.js b/helpers/getPullDiff.js
index 4d05f45e..06881d70 100644
--- a/helpers/getPullDiff.js
+++ b/helpers/getPullDiff.js
@@ -2,6 +2,7 @@ import isNumber from "lodash/isNumber";
const validSha = new RegExp(/^[a-f0-9]{40}$/);
export default async function getPullDiff(
+ storageApiUrl,
baseRepoId = null,
headRepoId = null,
baseCommitSha = null,
@@ -18,10 +19,7 @@ export default async function getPullDiff(
if (!isNumber(baseRepoIdNum) || !isNumber(headRepoIdNum)) {
return obj;
}
- const baseUrl =
- process.env.NODE_ENV === "development"
- ? "/api/pull/diff"
- : process.env.NEXT_PUBLIC_OBJECTS_URL + "/pull/diff";
+ const baseUrl = storageApiUrl + "/pull/diff";
let params = {
base_repository_id: baseRepoIdNum,
head_repository_id: headRepoIdNum,
diff --git a/helpers/getPullDiffStats.js b/helpers/getPullDiffStats.js
index 33caa3af..ce55bcf2 100644
--- a/helpers/getPullDiffStats.js
+++ b/helpers/getPullDiffStats.js
@@ -2,6 +2,7 @@ import isNumber from "lodash/isNumber";
const validSha = new RegExp(/^[a-f0-9]{40}$/);
export default async function getPullDiffStats(
+ storageApiUrl,
baseRepoId = null,
headRepoId = null,
baseCommitSha = null,
@@ -16,10 +17,7 @@ export default async function getPullDiffStats(
if (!isNumber(baseRepoIdNum) || !isNumber(headRepoIdNum)) {
return obj;
}
- const baseUrl =
- process.env.NODE_ENV === "development"
- ? "/api/pull/diff"
- : process.env.NEXT_PUBLIC_OBJECTS_URL + "/pull/diff";
+ const baseUrl = storageApiUrl + "/pull/diff";
let params = {
base_repository_id: baseRepoIdNum,
head_repository_id: headRepoIdNum,
diff --git a/helpers/getPullRequestCommits.js b/helpers/getPullRequestCommits.js
index cf7d02dd..d5c0b5f7 100644
--- a/helpers/getPullRequestCommits.js
+++ b/helpers/getPullRequestCommits.js
@@ -1,6 +1,7 @@
const validSha = new RegExp(/^[a-f0-9]{40}$/);
export default async function getPullRequestCommits(
+ storageApiUrl,
baseRepoId = null,
headRepoId = null,
baseBranch = null,
@@ -18,10 +19,7 @@ export default async function getPullRequestCommits(
return obj;
}
- const baseUrl =
- process.env.NODE_ENV === "development"
- ? "/api/pull/commits"
- : process.env.NEXT_PUBLIC_OBJECTS_URL + "/pull/commits";
+ const baseUrl = storageApiUrl + "/pull/commits";
let params = {
base_repository_id: Number(baseRepoId),
head_repository_id: Number(headRepoId),
diff --git a/helpers/getStorageBridgeAuthStatus.js b/helpers/getStorageBridgeAuthStatus.js
deleted file mode 100644
index 9dfecd77..00000000
--- a/helpers/getStorageBridgeAuthStatus.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import { useApiClient } from "../context/ApiClientContext";
-
-export default async function getStorageBridgeAuthStatus(
- apiClient,
- userAddress
-) {
- if (!userAddress) return null;
- try {
- const res = await apiClient.queryCheckStorageProviderAuthorization(
- userAddress,
- process.env.NEXT_PUBLIC_STORAGE_BRIDGE_WALLET_ADDRESS
- );
- if (res.status === 200 && res.data.haveAuthorization) {
- return true;
- } else {
- return false;
- }
- } catch (e) {
- console.error(e);
- return null;
- }
-}
diff --git a/helpers/gitopiaLive.js b/helpers/gitopiaLive.js
index 6308392d..a1dbcd06 100644
--- a/helpers/gitopiaLive.js
+++ b/helpers/gitopiaLive.js
@@ -289,6 +289,7 @@ function GitopiaLive(props) {
"/" +
props.repository.name
}
+ repositoryId={props.repository.id}
/>
) : (
diff --git a/helpers/mergePullRequest.js b/helpers/mergePullRequest.js
deleted file mode 100644
index 835c651f..00000000
--- a/helpers/mergePullRequest.js
+++ /dev/null
@@ -1,40 +0,0 @@
-export default async function mergePullRequest(
- pullReqIid,
- baseRepoId,
- headRepoId,
- baseBranch,
- headBranch,
- mergeStyle,
- userName,
- userEmail,
- sender
-) {
- const baseUrl =
- process.env.NODE_ENV === "development"
- ? "/api/pull/merge"
- : process.env.NEXT_PUBLIC_OBJECTS_URL + "/pull/merge";
- let obj = {};
- let params = {
- base_repository_id: Number(baseRepoId),
- head_repository_id: Number(headRepoId),
- base_branch: baseBranch,
- head_branch: headBranch,
- merge_style: mergeStyle,
- user_name: userName,
- user_email: userEmail,
- sender: sender,
- pull_request_iid: Number(pullReqIid),
- };
- await fetch(baseUrl, {
- method: "POST", // *GET, POST, PUT, DELETE, etc.
- mode: "cors", // no-cors, *cors, same-origin
- cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
- body: JSON.stringify(params),
- })
- .then((response) => {
- obj = response.json();
- })
- .catch((err) => console.error(err));
-
- return obj;
-}
diff --git a/helpers/mergePullRequestCheck.js b/helpers/mergePullRequestCheck.js
index e74388ac..eedf82b6 100644
--- a/helpers/mergePullRequestCheck.js
+++ b/helpers/mergePullRequestCheck.js
@@ -1,4 +1,5 @@
export default async function mergePullRequestCheck(
+ storageApiUrl,
pullReqIid,
baseRepoId,
headRepoId,
@@ -9,10 +10,7 @@ export default async function mergePullRequestCheck(
userEmail,
sender
) {
- const baseUrl =
- process.env.NODE_ENV === "development"
- ? "/api/pull/check"
- : process.env.NEXT_PUBLIC_OBJECTS_URL + "/pull/check";
+ const baseUrl = storageApiUrl + "/pull/check";
let obj = {};
let params = {
base_repository_id: Number(baseRepoId),
diff --git a/helpers/providerSelector.js b/helpers/providerSelector.js
index b7edd13d..0bd8ac66 100644
--- a/helpers/providerSelector.js
+++ b/helpers/providerSelector.js
@@ -3,7 +3,9 @@ import providers from "../providers.json";
const selectProvider = async () => {
if (process.env.NODE_ENV === "production") {
- const latencies = await Promise.all(providers.map(checkLatency));
+ const latencies = await Promise.all(
+ providers.map((p) => checkLatency(p.rpcEndpoint))
+ );
const lowestLatencyIndex = latencies.indexOf(Math.min(...latencies));
return providers[lowestLatencyIndex];
}
diff --git a/helpers/storageProviderSelector.js b/helpers/storageProviderSelector.js
new file mode 100644
index 00000000..976e6226
--- /dev/null
+++ b/helpers/storageProviderSelector.js
@@ -0,0 +1,28 @@
+import checkLatency from "./checkLatency";
+
+const selectStorageProvider = async (providers) => {
+ try {
+ if (providers.length === 0) {
+ return null;
+ }
+
+ const latencies = await Promise.all(
+ providers.map((p) => checkLatency(p.apiUrl))
+ );
+ const lowestLatencyIndex = latencies.indexOf(Math.min(...latencies));
+ return providers[lowestLatencyIndex];
+ } catch (error) {
+ console.error("Failed to select storage provider:", error);
+ return null;
+ }
+}
+
+export default selectStorageProvider;
+
+export const getSavedStorageProvider = () => {
+ const providerInfo = localStorage.getItem("storageProviderInfo");
+ if (providerInfo) {
+ return JSON.parse(providerInfo);
+ }
+ return null;
+};
diff --git a/next.config.js b/next.config.js
index 8f589f4c..df8ccae6 100644
--- a/next.config.js
+++ b/next.config.js
@@ -63,55 +63,19 @@ module.exports = withBundleAnalyzer({
},
...(process.env.NODE_ENV === "production"
? {
- compiler: {
- removeConsole: {
- exclude: ["error"],
- },
- reactRemoveProperties: { properties: ["^data-test$"] },
+ compiler: {
+ removeConsole: {
+ exclude: ["error"],
},
- }
+ reactRemoveProperties: { properties: ["^data-test$"] },
+ },
+ }
: {}),
async rewrites() {
return [
{
source: "/api/faucet",
destination: process.env.NEXT_PUBLIC_FAUCET_URL,
- },
- {
- source: "/api/objects/:path*",
- destination: process.env.NEXT_PUBLIC_OBJECTS_URL + "/objects/:path*",
- },
- {
- source: "/api/diff",
- destination: process.env.NEXT_PUBLIC_OBJECTS_URL + "/diff",
- },
- {
- source: "/api/pull/diff",
- destination: process.env.NEXT_PUBLIC_OBJECTS_URL + "/pull/diff",
- },
- {
- source: "/api/fork",
- destination: process.env.NEXT_PUBLIC_OBJECTS_URL + "/fork",
- },
- {
- source: "/api/pull/merge",
- destination: process.env.NEXT_PUBLIC_OBJECTS_URL + "/pull/merge",
- },
- {
- source: "/api/pull/commits",
- destination: process.env.NEXT_PUBLIC_OBJECTS_URL + "/pull/commits",
- },
- {
- source: "/api/pull/check",
- destination: process.env.NEXT_PUBLIC_OBJECTS_URL + "/pull/check",
- },
- {
- source: "/api/content",
- destination: process.env.NEXT_PUBLIC_OBJECTS_URL + "/content",
- },
- {
- source: "/api/commits/:path*",
- destination: process.env.NEXT_PUBLIC_OBJECTS_URL + "/commits/:path*",
}
];
},
diff --git a/pages/[userId]/[repositoryId]/commit/[commitId].js b/pages/[userId]/[repositoryId]/commit/[commitId].js
index 1048d6f5..14992d10 100644
--- a/pages/[userId]/[repositoryId]/commit/[commitId].js
+++ b/pages/[userId]/[repositoryId]/commit/[commitId].js
@@ -14,6 +14,7 @@ import CommitDetailRow from "../../../../components/repository/commitDetailRow";
import DiffView from "../../../../components/repository/diffView";
import getCommit from "../../../../helpers/getCommit";
import { useErrorStatus } from "../../../../hooks/errorHandler";
+import { useApiClient } from "../../../../context/ApiClientContext";
export async function getStaticProps() {
return { props: {} };
@@ -30,6 +31,7 @@ function RepositoryCommitDiffView(props) {
const router = useRouter();
const { repository } = useRepository();
const { setErrorStatusCode } = useErrorStatus();
+ const { storageApiUrl } = useApiClient();
const [viewType, setViewType] = useState("unified");
@@ -44,9 +46,10 @@ function RepositoryCommitDiffView(props) {
useEffect(() => {
async function initDiff() {
if (repository.id) {
- const c = await getCommit(repository.id, router.query.commitId);
+ const c = await getCommit(storageApiUrl, repository.id, router.query.commitId);
if (c && c.id) {
const data = await getDiffStats(
+ storageApiUrl,
Number(repository.id),
router.query.commitId
);
diff --git a/pages/[userId]/[repositoryId]/commits/[[...branch]].js b/pages/[userId]/[repositoryId]/commits/[[...branch]].js
index 0b7321b7..08b713c0 100644
--- a/pages/[userId]/[repositoryId]/commits/[[...branch]].js
+++ b/pages/[userId]/[repositoryId]/commits/[[...branch]].js
@@ -16,6 +16,7 @@ import CommitDetailRow from "../../../../components/repository/commitDetailRow";
import getCommitHistory from "../../../../helpers/getCommitHistory";
import { useErrorStatus } from "../../../../hooks/errorHandler";
import pluralize from "../../../../helpers/pluralize";
+import { useApiClient } from "../../../../context/ApiClientContext";
export async function getStaticProps() {
return { props: {} };
@@ -38,6 +39,7 @@ function RepositoryCommitTreeView(props) {
const [loadingMore, setLoadingMore] = useState(false);
const [branchName, setBranchName] = useState("");
const [commitsLength, setCommitsLength] = useState(0);
+ const { storageApiUrl } = useApiClient();
useEffect(() => {
async function initBranch() {
@@ -73,6 +75,7 @@ function RepositoryCommitTreeView(props) {
if (branchName === "") return;
setLoadingMore(true);
const res = await getCommitHistory(
+ storageApiUrl,
repository.id,
getBranchSha(branchName, repository.branches, repository.tags),
null,
diff --git a/pages/[userId]/[repositoryId]/compare/[[...branchTuple]].js b/pages/[userId]/[repositoryId]/compare/[[...branchTuple]].js
index 9d458aa5..5d93b989 100644
--- a/pages/[userId]/[repositoryId]/compare/[[...branchTuple]].js
+++ b/pages/[userId]/[repositoryId]/compare/[[...branchTuple]].js
@@ -70,7 +70,7 @@ function RepositoryCompareView(props) {
const [textEntered, setEnteredText] = useState("");
const [issueList, setIssueList] = useState([]);
const [issueArray, setIssueArray] = useState([]);
- const { apiClient, cosmosBankApiClient, cosmosFeegrantApiClient } =
+ const { apiClient, cosmosBankApiClient, cosmosFeegrantApiClient, storageApiUrl } =
useApiClient();
const setDefaultBranches = (r) => {
@@ -290,12 +290,14 @@ function RepositoryCompareView(props) {
async function initStats() {
const [diff, commits] = await Promise.all([
getPullDiffStats(
+ storageApiUrl,
compare.target.repository.id,
compare.source.repository.id,
compare.target.sha,
compare.source.sha
),
await getPullRequestCommits(
+ storageApiUrl,
compare.target.repository.id,
compare.source.repository.id,
compare.target.name,
@@ -558,7 +560,7 @@ function RepositoryCompareView(props) {
>
{issue.title.split(" ").length > 4
? issue.title.split(" ").splice(0, 4).join(" ") +
- "..."
+ "..."
: issue.title}
{labels.length
? labels.map((l, i) => {
- let label = find(repository.labels, {
- id: l,
- }) || {
- name: "",
- color: "",
- };
- return (
-
-
-
- );
- })
+ let label = find(repository.labels, {
+ id: l,
+ }) || {
+ name: "",
+ color: "",
+ };
+ return (
+
+
+
+ );
+ })
: "None yet"}
@@ -819,17 +821,17 @@ function RepositoryCompareView(props) {
{pullRequest.labels.length
? pullRequest.labels.map((l, i) => {
- let label = find(repository.labels, { id: l }) || {
- name: "",
- color: "",
- };
- return (
-
-
-
- );
- })
+ let label = find(repository.labels, { id: l }) || {
+ name: "",
+ color: "",
+ };
+ return (
+
+
+
+ );
+ })
: "None yet"}
diff --git a/pages/[userId]/[repositoryId]/releases/edit/[tagName].js b/pages/[userId]/[repositoryId]/releases/edit/[tagName].js
index 35f59126..24e8c3e1 100644
--- a/pages/[userId]/[repositoryId]/releases/edit/[tagName].js
+++ b/pages/[userId]/[repositoryId]/releases/edit/[tagName].js
@@ -22,6 +22,7 @@ import Uploady, {
useItemProgressListener,
useItemStartListener,
useItemFinishListener,
+ useRequestPreSend,
} from "@rpldy/uploady";
import UploadButton from "@rpldy/upload-button";
import UploadDropZone from "@rpldy/upload-drop-zone";
@@ -29,6 +30,7 @@ import getBranchSha from "../../../../../helpers/getBranchSha";
import getRepositoryRelease from "../../../../../helpers/getRepositoryRelease";
import useRepository from "../../../../../hooks/useRepository";
import { useApiClient } from "../../../../../context/ApiClientContext";
+import { signUploadFileMessage } from "../../../../../store/actions/user";
export async function getStaticProps() {
return { props: {} };
@@ -59,7 +61,7 @@ function RepositoryReleaseEditView(props) {
const [uploadingAttachment, setUploadingAttachment] = useState({ file: {} });
const [newTagOptionShown, setNewTagOptionShown] = useState(false);
const [creatingTag, setCreatingTag] = useState(false);
- const { apiClient, cosmosBankApiClient, cosmosFeegrantApiClient } =
+ const { apiClient, cosmosBankApiClient, cosmosFeegrantApiClient, storageProviderAddress } =
useApiClient();
const validateIssue = () => {
@@ -94,17 +96,18 @@ function RepositoryReleaseEditView(props) {
apiClient,
cosmosBankApiClient,
cosmosFeegrantApiClient,
+ storageProviderAddress,
issue,
true
);
if (res && res.code === 0) {
router.push(
"/" +
- repository.owner.id +
- "/" +
- repository.name +
- "/releases/tag/" +
- tagName
+ repository.owner.id +
+ "/" +
+ repository.name +
+ "/releases/tag/" +
+ tagName
);
}
}
@@ -144,6 +147,48 @@ function RepositoryReleaseEditView(props) {
}, [repository]);
const LogProgress = () => {
+ useRequestPreSend(async ({ items, options }) => {
+ try {
+ // Read file as ArrayBuffer using modern async/await approach
+ const arrayBuffer = await items[0].file.arrayBuffer();
+
+ // Calculate SHA256 using native Web Crypto API
+ const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer);
+
+ // Convert hash to hex string
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
+ const sha256 = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
+
+ const data = {
+ action: "edit-release",
+ repositoryId: repository.id,
+ tagName,
+ name: items[0].file.name,
+ size: items[0].file.size,
+ sha256,
+ };
+
+ // Generate signature
+ const signature = await props.signUploadFileMessage(
+ apiClient,
+ cosmosBankApiClient,
+ cosmosFeegrantApiClient,
+ data
+ );
+
+ return {
+ options: {
+ params: {
+ signature: signature,
+ },
+ }
+ };
+ } catch (error) {
+ console.error('Error processing file:', error);
+ throw error;
+ }
+ });
+
useItemStartListener((item) => {
console.log("started", item);
setUploadingAttachment(item);
@@ -458,6 +503,6 @@ const mapStateToProps = (state) => {
};
};
-export default connect(mapStateToProps, { createRelease, createTag })(
+export default connect(mapStateToProps, { createRelease, createTag, signUploadFileMessage })(
RepositoryReleaseEditView
);
diff --git a/pages/[userId]/[repositoryId]/releases/new.js b/pages/[userId]/[repositoryId]/releases/new.js
index 85209353..b6b424a5 100644
--- a/pages/[userId]/[repositoryId]/releases/new.js
+++ b/pages/[userId]/[repositoryId]/releases/new.js
@@ -25,12 +25,14 @@ import Uploady, {
useItemProgressListener,
useItemStartListener,
useItemFinishListener,
+ useRequestPreSend,
} from "@rpldy/uploady";
import UploadButton from "@rpldy/upload-button";
import UploadDropZone from "@rpldy/upload-drop-zone";
import getBranchSha from "../../../../helpers/getBranchSha";
import useRepository from "../../../../hooks/useRepository";
import { useApiClient } from "../../../../context/ApiClientContext";
+import { signUploadFileMessage } from "../../../../store/actions/user";
export async function getStaticProps() {
return { props: {} };
@@ -48,6 +50,7 @@ const RepositoryReleaseView = ({
createRelease,
createReleaseForDao,
createTag,
+ signUploadFileMessage,
}) => {
const router = useRouter();
const { repository, refreshRepository } = useRepository();
@@ -65,6 +68,8 @@ const RepositoryReleaseView = ({
cosmosBankApiClient,
cosmosFeegrantApiClient,
cosmosGroupApiClient,
+ storageProviderAddress,
+ storageApiUrl,
} = useApiClient();
const [isDao, setIsDao] = useState(false);
const [daoInfo, setDaoInfo] = useState(null);
@@ -113,6 +118,7 @@ const RepositoryReleaseView = ({
cosmosBankApiClient,
cosmosFeegrantApiClient,
cosmosGroupApiClient,
+ storageProviderAddress,
{
...releaseData,
groupId: daoInfo.group_id,
@@ -123,6 +129,7 @@ const RepositoryReleaseView = ({
apiClient,
cosmosBankApiClient,
cosmosFeegrantApiClient,
+ storageProviderAddress,
releaseData
);
}
@@ -175,7 +182,49 @@ const RepositoryReleaseView = ({
useEffect(updateTags, [repository]);
const LogProgress = () => {
- useItemStartListener((item) => {
+ useRequestPreSend(async ({ items, options }) => {
+ try {
+ // Read file as ArrayBuffer using modern async/await approach
+ const arrayBuffer = await items[0].file.arrayBuffer();
+
+ // Calculate SHA256 using native Web Crypto API
+ const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer);
+
+ // Convert hash to hex string
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
+ const sha256 = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
+
+ const data = {
+ action: "new-release",
+ repositoryId: repository.id,
+ tagName,
+ name: items[0].file.name,
+ size: items[0].file.size,
+ sha256,
+ };
+
+ // Generate signature
+ const signature = await signUploadFileMessage(
+ apiClient,
+ cosmosBankApiClient,
+ cosmosFeegrantApiClient,
+ data
+ );
+
+ return {
+ options: {
+ params: {
+ signature: signature,
+ },
+ }
+ };
+ } catch (error) {
+ console.error('Error processing file:', error);
+ throw error;
+ }
+ });
+
+ useItemStartListener(async (item) => {
console.log("started", item);
setUploadingAttachment(item);
});
@@ -249,11 +298,10 @@ const RepositoryReleaseView = ({
type="text"
placeholder="v1.0.0"
data-test="tag-name"
- className={`w-full input input-sm input-bordered focus:outline-none ${
- tagName.length > 0
- ? "border-green-500"
- : "border-pink-500"
- }`}
+ className={`w-full input input-sm input-bordered focus:outline-none ${tagName.length > 0
+ ? "border-green-500"
+ : "border-pink-500"
+ }`}
value={tagName}
onChange={(e) => setTagName(e.target.value)}
/>
@@ -271,9 +319,8 @@ const RepositoryReleaseView = ({