Skip to content

Commit

Permalink
Merge pull request #120 from sametcodes/develop
Browse files Browse the repository at this point in the history
Created Flock component, commit streak and contributors queries
  • Loading branch information
sametcodes authored Apr 7, 2023
2 parents f161401 + 46c9274 commit 3b666e1
Show file tree
Hide file tree
Showing 8 changed files with 1,341 additions and 5 deletions.
128 changes: 128 additions & 0 deletions lib/@dsvgui/components/flock/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { Document } from "@/lib/@dsvgui";
import { getTextWidth } from "@/lib/@dsvgui/utils";

export type IFlock = {
title?: string;
subtitle?: string;
items_per_row: number;
members: {
image: {
value: string;
width: number;
height: number;
};
caption: string;
}[];
};

export const Flock: React.FC<IFlock> = ({
title,
subtitle,
items_per_row,
members,
}) => {
let gap = 35;
const numRows = Math.ceil(members.length / items_per_row);

const titleFontSize = title ? 16 : 0;
const subtitleFontSize = subtitle ? 12 : 0;
const padding = 25;

const head_start = { x: title ? 10 : 0, y: title ? 25 : 10 };
const box_start = {
x: 25,
y: head_start.y + titleFontSize + subtitleFontSize + padding / 2,
};

const titleWidth = title
? getTextWidth(title, { fontSize: titleFontSize })
: 0;
const subtitleWidth = subtitle
? getTextWidth(subtitle, { fontSize: subtitleFontSize })
: 0;

const documentWidth =
15 +
Math.max(
titleWidth,
subtitleWidth,
(members.length > items_per_row ? items_per_row : members.length) * gap
);
const documentHeight =
head_start.y + titleFontSize + subtitleFontSize + numRows * gap;

return (
<Document w={documentWidth} h={documentHeight} padding={10}>
{title && (
<text
xmlns="http://www.w3.org/2000/svg"
className="title"
xmlSpace="preserve"
fontFamily="Roboto"
fontSize={titleFontSize}
fontWeight="500"
letterSpacing="-0.02em"
>
<tspan x={head_start.x} y={head_start.y}>
{title}
</tspan>
</text>
)}
{subtitle && (
<text
xmlns="http://www.w3.org/2000/svg"
className="subtitle"
xmlSpace="preserve"
fontFamily="Roboto"
fontSize={subtitleFontSize}
fontWeight="400"
letterSpacing="-0.02em"
>
<tspan x={head_start.x} y={head_start.y + titleFontSize}>
{subtitle}
</tspan>
</text>
)}
{members.map((member, index) => {
const row = Math.floor(index / items_per_row);
const col = index % items_per_row;

return (
<>
<circle
xmlns="http://www.w3.org/2000/svg"
cx={box_start.x + gap * col}
cy={box_start.y + row * gap}
r="15.5"
fill={`url(#member${index})`}
stroke="#ddd"
/>
<defs>
<pattern
xmlns="http://www.w3.org/2000/svg"
id={`member${index}`}
patternContentUnits="objectBoundingBox"
width="1"
height="1"
>
<use
xmlnsXlink="http://www.w3.org/1999/xlink"
xlinkHref={`#image${index}`}
transform="scale(0.00217391)"
/>
</pattern>
<image
xmlns="http://www.w3.org/2000/svg"
id={`image${index}`}
width="460"
height="460"
xmlnsXlink="http://www.w3.org/1999/xlink"
xlinkHref={`data:image/png;base64,${member.image.value}`}
/>
</defs>
</>
);
})}
</Document>
);
};
1 change: 1 addition & 0 deletions lib/@dsvgui/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from "./metrics";
export * from "./article";
export * from "./line";
export * from "./progress";
export * from "./flock";
3 changes: 2 additions & 1 deletion lib/@dsvgui/document/style.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@ export const Style: React.FC = () => {
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
text{ font-family: 'Manrope', 'Open Sans', 'Segoe UI', Ubuntu, 'Helvetica Neue', Sans-Serif; fill: #32373e; }
text.title{ fill: #32373e; }
text.subtitle{ fill: #9c9c9c; }
.dsvgui-container{
fill: #fbfbfb;
}
stop[offset='0']{ stop-color: #fbfbfb; }
stop[offset='1']{ stop-color: #fbfbfb; }
@media (prefers-color-scheme: dark) {
.dsvgui-container { fill: #32373e }
stop[offset='0']{ stop-color: #32373e; }
Expand Down
32 changes: 32 additions & 0 deletions platforms/github/query/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,35 @@ export const getUserActiveSponsorGoal: QueryService = async (

return response;
};

/**
* @name getContributors
* @title Get contribuors of a repository
* @query_type Private
* @cache_time 3600
* @description Get the list of contributors of a repository. Only the most contributed 100 contributors are returned.
*/
export const getContributors: QueryService = async (connection, config) => {
const { queryConfig } = config as any;

const { owner_name, repository_name } = queryConfig as {
owner_name: string;
repository_name: string;
};
const query = `{
repository(owner: "${owner_name}", name: "${repository_name}") {
name
mentionableUsers(first: 100) {
totalCount
nodes {
login
name
avatarUrl
}
}
}
}`;

const response = await request(query, connection);
return response;
};
17 changes: 17 additions & 0 deletions platforms/github/query/validations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,20 @@ export const getUserActiveSponsorGoal = object({
})
.required()
.noUnknown(true);

export const getUserCommitStreak = object().required().noUnknown(true);

export const getContributors = object({
owner_name: string().required().meta({
label: "Owner username",
placeholder: "Owner",
description: "The name of the owner of the repository.",
}),
repository_name: string().required().meta({
label: "Repository Name",
placeholder: "Repository Name",
description: "The name of the repository.",
}),
})
.required()
.noUnknown(true);
95 changes: 92 additions & 3 deletions platforms/github/view/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { Progress, IProgress } from "@/lib/@dsvgui/components";

import {
Progress,
IProgress,
Metrics,
Flock,
IFlock,
} from "@/lib/@dsvgui/components";
import { GithubIcon } from "@/lib/@dsvgui/icons";
import { Metrics } from "@/lib/@dsvgui/components";
import { ViewComponent } from "@/platforms/types";
import getImageSize from "image-size";
import qs from "qs";

export const getContributionsSummary: ViewComponent = (result, config) => {
const {
Expand Down Expand Up @@ -262,3 +268,86 @@ export const getUserActiveSponsorGoal: ViewComponent = (result, config) => {
/>
);
};

export const getUserCommitStreak: ViewComponent = (result, config) => {
const dates = new Set<string>();

result.data.viewer.contributionsCollection.commitContributionsByRepository.forEach(
(repo: any) => {
repo.contributions.nodes.forEach((commit: any) => {
const date = new Date(commit.occurredAt).toISOString().split("T")[0];
dates.add(date);
});
}
);

const sortedDates = Array.from(dates).sort();
let streak = 0;
let currentStreak = 0;

for (let i = 1; i < sortedDates.length; i++) {
const previousDate = new Date(sortedDates[i - 1]);
const currentDate = new Date(sortedDates[i]);
const dayDifference =
(currentDate.getTime() - previousDate.getTime()) / (1000 * 60 * 60 * 24);

if (dayDifference === 1) {
currentStreak++;
} else {
currentStreak = 0;
}

streak = Math.max(streak, currentStreak);
}

return (
<Metrics
icon={GithubIcon}
data={[{ title: "Commit Streak", value: streak }]}
/>
);
};

export const getContributors: ViewComponent = async (result, config) => {
const { title, subtitle, items_per_row } = config.viewConfig as any;

const contributors = result.data.repository.mentionableUsers.nodes;

const promise_thumbnails: IFlock["members"] = contributors.map(
async (contributor: any, key: number) => {
const url = new URL(contributor.avatarUrl);
let params = qs.parse(url.search, { ignoreQueryPrefix: true });
url.search = qs.stringify({ ...params, s: "128" });

const response = await fetch(url.toString());
const arrayBuffer = await response.arrayBuffer();

const buffer = Buffer.from(arrayBuffer);
const imageData = getImageSize(buffer);

return {
value: buffer.toString("base64"),
width: imageData.width,
height: imageData.height,
};
}
);

const thumbnails = await Promise.all(promise_thumbnails);

const members: IFlock["members"] = contributors.map(
(contributor: any, key: number) => ({
image: thumbnails[key],
caption: contributor.login,
})
);

return (
<Flock
title={title}
subtitle={subtitle}
items_per_row={items_per_row}
members={members}
/>
);
};
Loading

1 comment on commit 3b666e1

@vercel
Copy link

@vercel vercel bot commented on 3b666e1 Apr 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

devstats – ./

devstats-git-main-sametcodes.vercel.app
devstats-sametcodes.vercel.app
readme.rocks
www.readme.rocks

Please sign in to comment.