This commit is contained in:
Rene Pfeuffer
2020-02-20 14:36:13 +01:00
parent 4bc3d16aa9
commit c4a801a7be
5 changed files with 137 additions and 78 deletions

View File

@@ -112,10 +112,11 @@ public final class BrowseCommandRequest extends FileBaseCommandRequest
final BrowseCommandRequest other = (BrowseCommandRequest) obj; final BrowseCommandRequest other = (BrowseCommandRequest) obj;
return super.equals(obj) && Objects.equal(recursive, other.recursive) return super.equals(obj)
&& Objects.equal(recursive, other.recursive)
&& Objects.equal(disableLastCommit, other.disableLastCommit) && Objects.equal(disableLastCommit, other.disableLastCommit)
&& Objects.equal(disableSubRepositoryDetection, && Objects.equal(disableSubRepositoryDetection, other.disableSubRepositoryDetection)
other.disableSubRepositoryDetection); && Objects.equal(offset, other.offset);
} }
/** /**
@@ -128,7 +129,7 @@ public final class BrowseCommandRequest extends FileBaseCommandRequest
public int hashCode() public int hashCode()
{ {
return Objects.hashCode(super.hashCode(), recursive, disableLastCommit, return Objects.hashCode(super.hashCode(), recursive, disableLastCommit,
disableSubRepositoryDetection); disableSubRepositoryDetection, offset);
} }
/** /**
@@ -147,6 +148,7 @@ public final class BrowseCommandRequest extends FileBaseCommandRequest
.add("recursive", recursive) .add("recursive", recursive)
.add("disableLastCommit", disableLastCommit) .add("disableLastCommit", disableLastCommit)
.add("disableSubRepositoryDetection", disableSubRepositoryDetection) .add("disableSubRepositoryDetection", disableSubRepositoryDetection)
.add("offset", offset)
.toString(); .toString();
//J+ //J+
} }

View File

@@ -16,9 +16,9 @@ export type File = {
length?: number; length?: number;
commitDate?: string; commitDate?: string;
subRepository?: SubRepository; // TODO subRepository?: SubRepository; // TODO
partialResult: boolean; partialResult?: boolean;
computationAborted: boolean; computationAborted?: boolean;
truncated: boolean; truncated?: boolean;
_links: Links; _links: Links;
_embedded: { _embedded: {
children: File[] | null | undefined; children: File[] | null | undefined;

View File

@@ -12,7 +12,7 @@ import {
getFetchSourcesFailure, getFetchSourcesFailure,
getHunkCount, getHunkCount,
getSources, getSources,
isFetchSourcesPending, isUpdateSourcePending isFetchSourcesPending
} from "../modules/sources"; } from "../modules/sources";
import FileTreeLeaf from "./FileTreeLeaf"; import FileTreeLeaf from "./FileTreeLeaf";
import Button from "@scm-manager/ui-components/src/buttons/Button"; import Button from "@scm-manager/ui-components/src/buttons/Button";
@@ -86,7 +86,7 @@ class FileTree extends React.Component<Props, State> {
} }
loadMore = () => { loadMore = () => {
// console.log("smth"); this.props.fetchSources(this.props.repository, this.props.revision, this.props.path, this.props.hunks.length);
}; };
render() { render() {
@@ -124,22 +124,6 @@ class FileTree extends React.Component<Props, State> {
}); });
} }
const compareFiles = function(f1: File, f2: File): number {
if (f1.directory) {
if (f2.directory) {
return f1.name.localeCompare(f2.name);
} else {
return -1;
}
} else {
if (f2.directory) {
return 1;
} else {
return f1.name.localeCompare(f2.name);
}
}
};
hunks hunks
.filter(hunk => !hunk.loading) .filter(hunk => !hunk.loading)
.forEach(hunk => { .forEach(hunk => {
@@ -149,7 +133,9 @@ class FileTree extends React.Component<Props, State> {
} }
}); });
if (files && files.length > 0) { const loading = hunks.filter(hunk => hunk.loading).length > 0;
if (loading || (files && files.length > 0)) {
let baseUrlWithRevision = baseUrl; let baseUrlWithRevision = baseUrl;
if (revision) { if (revision) {
baseUrlWithRevision += "/" + encodeURIComponent(revision); baseUrlWithRevision += "/" + encodeURIComponent(revision);
@@ -195,7 +181,6 @@ const mapDispatchToProps = (dispatch: any, ownProps: Props) => {
const mapStateToProps = (state: any, ownProps: Props) => { const mapStateToProps = (state: any, ownProps: Props) => {
const { repository, revision, path } = ownProps; const { repository, revision, path } = ownProps;
const loading = isFetchSourcesPending(state, repository, revision, path, 0);
const error = getFetchSourcesFailure(state, repository, revision, path, 0); const error = getFetchSourcesFailure(state, repository, revision, path, 0);
const hunkCount = getHunkCount(state, repository, revision, path); const hunkCount = getHunkCount(state, repository, revision, path);
const hunks = []; const hunks = [];
@@ -212,7 +197,6 @@ const mapStateToProps = (state: any, ownProps: Props) => {
return { return {
revision, revision,
path, path,
loading,
error, error,
hunks hunks
}; };

View File

@@ -37,6 +37,9 @@ const collection = {
length: 176, length: 176,
revision: "76aae4bb4ceacf0e88938eb5b6832738b7d537b4", revision: "76aae4bb4ceacf0e88938eb5b6832738b7d537b4",
subRepository: undefined, subRepository: undefined,
truncated: true,
partialResult: false,
computationAborted: false,
_links: { _links: {
self: { self: {
href: href:
@@ -120,12 +123,23 @@ describe("sources fetch", () => {
const expectedActions = [ const expectedActions = [
{ {
type: FETCH_SOURCES_PENDING, type: FETCH_SOURCES_PENDING,
itemId: "scm/core/_/" itemId: "scm/core/_//",
payload: {
hunk: 0,
updatePending: false,
pending: true,
sources: {}
}
}, },
{ {
type: FETCH_SOURCES_SUCCESS, type: FETCH_SOURCES_SUCCESS,
itemId: "scm/core/_/", itemId: "scm/core/_//",
payload: { updatePending: false, sources: collection } payload: {
hunk: 0,
updatePending: false,
pending: false,
sources: collection
}
} }
]; ];
@@ -136,17 +150,28 @@ describe("sources fetch", () => {
}); });
it("should fetch the sources of the repository with the given revision and path", () => { it("should fetch the sources of the repository with the given revision and path", () => {
fetchMock.getOnce(sourcesUrl + "abc/src", collection); fetchMock.getOnce(sourcesUrl + "abc/src?offset=0", collection);
const expectedActions = [ const expectedActions = [
{ {
type: FETCH_SOURCES_PENDING, type: FETCH_SOURCES_PENDING,
itemId: "scm/core/abc/src" itemId: "scm/core/abc/src/",
payload: {
hunk: 0,
updatePending: false,
pending: true,
sources: {}
}
}, },
{ {
type: FETCH_SOURCES_SUCCESS, type: FETCH_SOURCES_SUCCESS,
itemId: "scm/core/abc/src", itemId: "scm/core/abc/src/",
payload: { updatePending: false, sources: collection } payload: {
hunk: 0,
updatePending: false,
pending: false,
sources: collection
}
} }
]; ];
@@ -166,7 +191,7 @@ describe("sources fetch", () => {
const actions = store.getActions(); const actions = store.getActions();
expect(actions[0].type).toBe(FETCH_SOURCES_PENDING); expect(actions[0].type).toBe(FETCH_SOURCES_PENDING);
expect(actions[1].type).toBe(FETCH_SOURCES_FAILURE); expect(actions[1].type).toBe(FETCH_SOURCES_FAILURE);
expect(actions[1].itemId).toBe("scm/core/_/"); expect(actions[1].itemId).toBe("scm/core/_//");
expect(actions[1].payload).toBeDefined(); expect(actions[1].payload).toBeDefined();
}); });
}); });
@@ -180,16 +205,18 @@ describe("reducer tests", () => {
it("should store the collection, without revision and path", () => { it("should store the collection, without revision and path", () => {
const expectedState = { const expectedState = {
"scm/core/_/": { updatePending: false, sources: collection } "scm/core/_//0": { pending: false, updatePending: false, sources: collection },
"scm/core/_//hunkCount": 1
}; };
expect(reducer({}, fetchSourcesSuccess(repository, "", "", collection))).toEqual(expectedState); expect(reducer({}, fetchSourcesSuccess(repository, "", "", 0, collection))).toEqual(expectedState);
}); });
it("should store the collection, with revision and path", () => { it("should store the collection, with revision and path", () => {
const expectedState = { const expectedState = {
"scm/core/abc/src/main": { updatePending: false, sources: collection } "scm/core/abc/src/main/0": { pending: false, updatePending: false, sources: collection },
"scm/core/abc/src/main/hunkCount": 1
}; };
expect(reducer({}, fetchSourcesSuccess(repository, "abc", "src/main", collection))).toEqual(expectedState); expect(reducer({}, fetchSourcesSuccess(repository, "abc", "src/main", 0, collection))).toEqual(expectedState);
}); });
}); });
@@ -197,7 +224,7 @@ describe("selector tests", () => {
it("should return false if it is no directory", () => { it("should return false if it is no directory", () => {
const state = { const state = {
sources: { sources: {
"scm/core/abc/src/main/package.json": { "scm/core/abc/src/main/package.json/0": {
sources: { noDirectory } sources: { noDirectory }
} }
} }
@@ -208,7 +235,7 @@ describe("selector tests", () => {
it("should return true if it is directory", () => { it("should return true if it is directory", () => {
const state = { const state = {
sources: { sources: {
"scm/core/abc/src": noDirectory "scm/core/abc/src/0": noDirectory
} }
}; };
expect(isDirectory(state, repository, "abc", "src")).toBe(true); expect(isDirectory(state, repository, "abc", "src")).toBe(true);
@@ -221,7 +248,7 @@ describe("selector tests", () => {
it("should return the source collection without revision and path", () => { it("should return the source collection without revision and path", () => {
const state = { const state = {
sources: { sources: {
"scm/core/_/": { "scm/core/_//0": {
sources: collection sources: collection
} }
} }
@@ -232,7 +259,7 @@ describe("selector tests", () => {
it("should return the source collection with revision and path", () => { it("should return the source collection with revision and path", () => {
const state = { const state = {
sources: { sources: {
"scm/core/abc/src/main": { "scm/core/abc/src/main/0": {
sources: collection sources: collection
} }
} }
@@ -242,15 +269,26 @@ describe("selector tests", () => {
it("should return true, when fetch sources is pending", () => { it("should return true, when fetch sources is pending", () => {
const state = { const state = {
pending: { sources: {
[FETCH_SOURCES + "/scm/core/_/"]: true "scm/core/_//0": {
pending: true,
sources: {}
}
} }
}; };
expect(isFetchSourcesPending(state, repository, "", "")).toEqual(true); expect(isFetchSourcesPending(state, repository, "", "", 0)).toEqual(true);
}); });
it("should return false, when fetch sources is not pending", () => { it("should return false, when fetch sources is not pending", () => {
expect(isFetchSourcesPending({}, repository, "", "")).toEqual(false); const state = {
sources: {
"scm/core/_//0": {
pending: false,
sources: {}
}
}
};
expect(isFetchSourcesPending(state, repository, "", "", 0)).toEqual(false);
}); });
const error = new Error("incredible error from hell"); const error = new Error("incredible error from hell");
@@ -258,13 +296,13 @@ describe("selector tests", () => {
it("should return error when fetch sources did fail", () => { it("should return error when fetch sources did fail", () => {
const state = { const state = {
failure: { failure: {
[FETCH_SOURCES + "/scm/core/_/"]: error [FETCH_SOURCES + "/scm/core/_//"]: error
} }
}; };
expect(getFetchSourcesFailure(state, repository, "", "")).toEqual(error); expect(getFetchSourcesFailure(state, repository, "", "", 0)).toEqual(error);
}); });
it("should return undefined when fetch sources did not fail", () => { it("should return undefined when fetch sources did not fail", () => {
expect(getFetchSourcesFailure({}, repository, "", "")).toBe(undefined); expect(getFetchSourcesFailure({}, repository, "", "", 0)).toBe(undefined);
}); });
}); });

View File

@@ -1,7 +1,6 @@
import * as types from "../../../modules/types"; import * as types from "../../../modules/types";
import { Action, File, Link, Repository } from "@scm-manager/ui-types"; import { Action, File, Link, Repository } from "@scm-manager/ui-types";
import { apiClient } from "@scm-manager/ui-components"; import { apiClient } from "@scm-manager/ui-components";
import { isPending } from "../../../modules/pending";
import { getFailure } from "../../../modules/failure"; import { getFailure } from "../../../modules/failure";
export const FETCH_SOURCES = "scm/repos/FETCH_SOURCES"; export const FETCH_SOURCES = "scm/repos/FETCH_SOURCES";
@@ -27,8 +26,18 @@ export function fetchSources(repository: Repository, revision: string, path: str
updateSourcesPending(repository, revision, path, hunk, getSources(state, repository, revision, path, hunk)) updateSourcesPending(repository, revision, path, hunk, getSources(state, repository, revision, path, hunk))
); );
} }
let offset = 0;
const hunkCount = getHunkCount(state, repository, revision, path);
for (let i = 0; i < hunkCount; ++i) {
const sources = getSources(state, repository, revision, path, i);
if (sources?._embedded.children) {
offset += sources._embedded.children.length;
}
}
return apiClient return apiClient
.get(createUrl(repository, revision, path, hunk)) .get(createUrl(repository, revision, path, offset))
.then(response => response.json()) .then(response => response.json())
.then((sources: File) => { .then((sources: File) => {
dispatch(fetchSourcesSuccess(repository, revision, path, hunk, sources)); dispatch(fetchSourcesSuccess(repository, revision, path, hunk, sources));
@@ -39,7 +48,7 @@ export function fetchSources(repository: Repository, revision: string, path: str
}; };
} }
function createUrl(repository: Repository, revision: string, path: string, hunk: number) { function createUrl(repository: Repository, revision: string, path: string, offset: number) {
const base = (repository._links.sources as Link).href; const base = (repository._links.sources as Link).href;
if (!revision && !path) { if (!revision && !path) {
return base; return base;
@@ -47,14 +56,14 @@ function createUrl(repository: Repository, revision: string, path: string, hunk:
// TODO handle trailing slash // TODO handle trailing slash
const pathDefined = path ? path : ""; const pathDefined = path ? path : "";
return `${base}${encodeURIComponent(revision)}/${pathDefined}?hunk=${hunk}`; return `${base}${encodeURIComponent(revision)}/${pathDefined}?offset=${offset}`;
} }
export function fetchSourcesPending(repository: Repository, revision: string, path: string, hunk: number): Action { export function fetchSourcesPending(repository: Repository, revision: string, path: string, hunk: number): Action {
return { return {
type: FETCH_SOURCES_PENDING, type: FETCH_SOURCES_PENDING,
itemId: createItemId(repository, revision, path), itemId: createItemId(repository, revision, path, ""),
payload: { hunk, pending: true, sources: {} } payload: { hunk, pending: true, updatePending: false, sources: {} }
}; };
} }
@@ -63,12 +72,12 @@ export function updateSourcesPending(
revision: string, revision: string,
path: string, path: string,
hunk: number, hunk: number,
currentSources: any currentSources: File
): Action { ): Action {
return { return {
type: FETCH_UPDATES_PENDING, type: FETCH_UPDATES_PENDING,
payload: { hunk, updatePending: true, sources: currentSources }, payload: { hunk, pending: false, updatePending: true, sources: currentSources },
itemId: createItemId(repository, revision, path) itemId: createItemId(repository, revision, path, "")
}; };
} }
@@ -82,7 +91,7 @@ export function fetchSourcesSuccess(
return { return {
type: FETCH_SOURCES_SUCCESS, type: FETCH_SOURCES_SUCCESS,
payload: { hunk, pending: false, updatePending: false, sources }, payload: { hunk, pending: false, updatePending: false, sources },
itemId: createItemId(repository, revision, path) itemId: createItemId(repository, revision, path, "")
}; };
} }
@@ -96,14 +105,14 @@ export function fetchSourcesFailure(
return { return {
type: FETCH_SOURCES_FAILURE, type: FETCH_SOURCES_FAILURE,
payload: error, payload: error,
itemId: createItemId(repository, revision, path) itemId: createItemId(repository, revision, path, "")
}; };
} }
function createItemId(repository: Repository, revision: string | undefined, path: string) { function createItemId(repository: Repository, revision: string | undefined, path: string, hunk: number | string) {
const revPart = revision ? revision : "_"; const revPart = revision ? revision : "_";
const pathPart = path ? path : ""; const pathPart = path ? path : "";
return `${repository.namespace}/${repository.name}/${decodeURIComponent(revPart)}/${pathPart}`; return `${repository.namespace}/${repository.name}/${decodeURIComponent(revPart)}/${pathPart}/${hunk}`;
} }
// reducer // reducer
@@ -114,19 +123,39 @@ export default function reducer(
type: "UNKNOWN" type: "UNKNOWN"
} }
): any { ): any {
if (action.itemId && (action.type === FETCH_SOURCES_SUCCESS || action.type === FETCH_UPDATES_PENDING)) { if (action.itemId && action.type === FETCH_SOURCES_SUCCESS) {
console.log("adding payload to " + action.itemId + "/" + action.payload.hunk);
return { return {
...state, ...state,
[action.itemId + "/hunkCount"]: action.payload.hunk + 1, [action.itemId + "hunkCount"]: action.payload.hunk + 1,
[action.itemId + "/" + action.payload.hunk]: { [action.itemId + action.payload.hunk]: {
sources: action.payload.sources, sources: action.payload.sources,
loading: false updatePending: false,
pending: false
} }
}; };
} else if (action.itemId && action.type === FETCH_UPDATES_PENDING) {
return {
...state,
[action.itemId + "hunkCount"]: action.payload.hunk + 1,
[action.itemId + action.payload.hunk]: {
sources: action.payload.sources,
updatePending: true,
pending: false
} }
};
} else if (action.itemId && action.type === FETCH_SOURCES_PENDING) {
return {
...state,
[action.itemId + "hunkCount"]: action.payload.hunk + 1,
[action.itemId + action.payload.hunk]: {
updatePending: false,
pending: true
}
};
} else {
return state; return state;
} }
}
// selectors // selectors
@@ -141,7 +170,7 @@ export function isDirectory(state: any, repository: Repository, revision: string
export function getHunkCount(state: any, repository: Repository, revision: string | undefined, path: string): number { export function getHunkCount(state: any, repository: Repository, revision: string | undefined, path: string): number {
if (state.sources) { if (state.sources) {
const count = state.sources[createItemId(repository, revision, path) + "/hunkCount"]; const count = state.sources[createItemId(repository, revision, path, "hunkCount")];
return count ? count : 0; return count ? count : 0;
} }
return 0; return 0;
@@ -152,10 +181,10 @@ export function getSources(
repository: Repository, repository: Repository,
revision: string | undefined, revision: string | undefined,
path: string, path: string,
hunk: number hunk = 0
): File | null | undefined { ): File | null | undefined {
if (state.sources) { if (state.sources) {
return state.sources[createItemId(repository, revision, path) + "/" + hunk]?.sources; return state.sources[createItemId(repository, revision, path, hunk)]?.sources;
} }
return null; return null;
} }
@@ -165,9 +194,12 @@ export function isFetchSourcesPending(
repository: Repository, repository: Repository,
revision: string, revision: string,
path: string, path: string,
hunk: number hunk = 0
): boolean { ): boolean {
return state && isPending(state, FETCH_SOURCES, createItemId(repository, revision, path)); if (state.sources) {
return state.sources[createItemId(repository, revision, path, hunk)]?.pending;
}
return false;
} }
export function isUpdateSourcePending( export function isUpdateSourcePending(
@@ -177,7 +209,10 @@ export function isUpdateSourcePending(
path: string, path: string,
hunk: number hunk: number
): boolean { ): boolean {
return state?.sources && state.sources[createItemId(repository, revision, path) + "/" + hunk]?.updatePending; if (state.sources) {
return state.sources[createItemId(repository, revision, path, hunk)]?.updatePending;
}
return false;
} }
export function getFetchSourcesFailure( export function getFetchSourcesFailure(
@@ -185,7 +220,7 @@ export function getFetchSourcesFailure(
repository: Repository, repository: Repository,
revision: string, revision: string,
path: string, path: string,
hunk: number hunk = 0
): Error | null | undefined { ): Error | null | undefined {
return getFailure(state, FETCH_SOURCES, createItemId(repository, revision, path)); return getFailure(state, FETCH_SOURCES, createItemId(repository, revision, path, ""));
} }