diff --git a/source/app/mocks/api/github/graphql/sponsors.default.mjs b/source/app/mocks/api/github/graphql/sponsors.default.mjs
new file mode 100644
index 00000000..3f886807
--- /dev/null
+++ b/source/app/mocks/api/github/graphql/sponsors.default.mjs
@@ -0,0 +1,28 @@
+/**Mocked data */
+export default function({faker, query, login = faker.internet.userName()}) {
+ console.debug("metrics/compute/mocks > mocking graphql api result > sponsors/default")
+ return ({
+ user:{
+ sponsorsListing:{
+ fullDescription:faker.lorem.sentences(),
+ activeGoal:{
+ percentComplete:faker.datatype.number(100),
+ title:faker.lorem.sentence(),
+ description:faker.lorem.sentence(),
+ }
+ },
+ sponsorshipsAsMaintainer:{
+ totalCount:faker.datatype.number(100),
+ nodes:new Array(10).fill(null).map(_ => ({
+ sponsorEntity:{
+ login:faker.internet.userName(),
+ avatarUrl:null,
+ },
+ tier:{
+ monthlyPriceInDollars:faker.datatype.number(10),
+ }
+ }))
+ }
+ },
+ })
+}
\ No newline at end of file
diff --git a/source/app/web/statics/app.placeholder.js b/source/app/web/statics/app.placeholder.js
index 44f60d21..2afa7419 100644
--- a/source/app/web/statics/app.placeholder.js
+++ b/source/app/web/statics/app.placeholder.js
@@ -353,6 +353,26 @@
}
})
: null),
+ //Sponsors
+ ...(set.plugins.enabled.sponsors
+ ? ({
+ sponsors: {
+ sections: options["sponsors.sections"].split(",").map(x => x.trim()),
+ about: "A new way to contribute to open source",
+ list: new Array(Number(faker.datatype.number(40))).fill(null).map(_ => ({
+ login: faker.internet.userName(),
+ amount: faker.datatype.number(10),
+ avatar: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOcOnfpfwAGfgLYttYINwAAAABJRU5ErkJggg==",
+ })),
+ count: faker.datatype.number(100),
+ goal: {
+ progress: faker.datatype.number(100),
+ title: `$${faker.datatype.number(100)*10} per month`,
+ description: "Invest in the software that powers your world"
+ }
+ }
+ })
+ : null),
//Languages
...(set.plugins.enabled.languages
? ({
diff --git a/source/plugins/sponsors/README.md b/source/plugins/sponsors/README.md
new file mode 100644
index 00000000..4be4ad84
--- /dev/null
+++ b/source/plugins/sponsors/README.md
@@ -0,0 +1,25 @@
+### đ GitHub Sponsors
+
+The *sponsors* plugin lets you display your sponsors and introduction text from [GitHub sponsors](https://github.com/sponsors/).
+
+
+
+
+ With GitHub sponsors introduction
+
+
+
+ |
+
+
+#### âšī¸ Examples workflows
+
+[âĄī¸ Available options for this plugin](metadata.yml)
+
+```yaml
+- uses: lowlighter/metrics@latest
+ with:
+ # ... other options
+ plugin_sponsors: yes
+ plugin_sponsors_sections: goal, about # Display goal and about sections
+```
\ No newline at end of file
diff --git a/source/plugins/sponsors/index.mjs b/source/plugins/sponsors/index.mjs
new file mode 100644
index 00000000..2f467de0
--- /dev/null
+++ b/source/plugins/sponsors/index.mjs
@@ -0,0 +1,26 @@
+//Setup
+export default async function({login, q, imports, data, graphql, queries, account}, {enabled = false} = {}) {
+ //Plugin execution
+ try {
+ //Check if plugin is enabled and requirements are met
+ if ((!enabled)||(!q.sponsors))
+ return null
+
+ //Load inputs
+ const {sections} = await imports.metadata.plugins.sponsors.inputs({data, account, q})
+
+ //Query sponsors and goal
+ const {[account]:{sponsorsListing:{fullDescription, activeGoal}, sponsorshipsAsMaintainer:{nodes, totalCount:count}}} = await graphql(queries.sponsors({login, account}))
+ const about = await imports.markdown(fullDescription, {mode:"multiline"})
+ const goal = activeGoal ? {progress:activeGoal.percentComplete, title:activeGoal.title, description:await imports.markdown(activeGoal.description)} : null
+ const list = nodes.map(({sponsorEntity:{login, avatarUrl}, tier:{monthlyPriceInDollars:amount}}) => ({login, avatarUrl, amount}))
+ await Promise.all(list.map(async user => user.avatar = await imports.imgb64(user.avatarUrl)))
+
+ //Results
+ return {sections, about, list, count, goal}
+ }
+ //Handle errors
+ catch (error) {
+ throw {error:{message:"An error occured", instance:error}}
+ }
+}
diff --git a/source/plugins/sponsors/metadata.yml b/source/plugins/sponsors/metadata.yml
new file mode 100644
index 00000000..81966e64
--- /dev/null
+++ b/source/plugins/sponsors/metadata.yml
@@ -0,0 +1,25 @@
+name: "đ GitHub Sponsors"
+cost: 1 GraphQL request
+category: github
+index: 23
+supports:
+ - user
+ - organization
+inputs:
+
+ # Enable or disable plugin
+ plugin_sponsors:
+ description: Display GitHub sponsors
+ type: boolean
+ default: no
+
+ # Sections to display
+ plugin_sponsors_sections:
+ description: Sections to display
+ type: array
+ format: comma-separated
+ default: goal, about
+ example: goal, about
+ values:
+ - goal # Display your GitHub active goal
+ - about # Display your GitHub sponsors introduction
\ No newline at end of file
diff --git a/source/plugins/sponsors/queries/sponsors.graphql b/source/plugins/sponsors/queries/sponsors.graphql
new file mode 100644
index 00000000..18eb1cc3
--- /dev/null
+++ b/source/plugins/sponsors/queries/sponsors.graphql
@@ -0,0 +1,26 @@
+query SponsorsDefault {
+ $account(login: "$login") {
+ sponsorsListing {
+ fullDescription
+ activeGoal {
+ percentComplete
+ title
+ description
+ }
+ }
+ sponsorshipsAsMaintainer(first: 100) {
+ totalCount
+ nodes {
+ sponsorEntity {
+ ... on User {
+ login
+ avatarUrl(size: 36)
+ }
+ }
+ tier {
+ monthlyPriceInDollars
+ }
+ }
+ }
+ }
+}
diff --git a/source/plugins/sponsors/tests.yml b/source/plugins/sponsors/tests.yml
new file mode 100644
index 00000000..716c1d28
--- /dev/null
+++ b/source/plugins/sponsors/tests.yml
@@ -0,0 +1,5 @@
+- name: Sponsors plugin (default)
+ uses: lowlighter/metrics@latest
+ with:
+ token: MOCKED_TOKEN
+ plugin_sponsors: yes
\ No newline at end of file
diff --git a/source/templates/classic/partials/_.json b/source/templates/classic/partials/_.json
index c02068e3..96e20eed 100644
--- a/source/templates/classic/partials/_.json
+++ b/source/templates/classic/partials/_.json
@@ -32,5 +32,6 @@
"stock",
"achievements",
"screenshot",
- "code"
+ "code",
+ "sponsors"
]
diff --git a/source/templates/classic/partials/sponsors.ejs b/source/templates/classic/partials/sponsors.ejs
new file mode 100644
index 00000000..415fa791
--- /dev/null
+++ b/source/templates/classic/partials/sponsors.ejs
@@ -0,0 +1,60 @@
+<% if (plugins.sponsors) { %>
+
+
+
+ Sponsor me!
+
+ <% if (plugins.sponsors.error) { %>
+
+
+
+
+ <%= plugins.sponsors.error.message %>
+
+
+
+ <% } else { %>
+ <% for (const section of plugins.sponsors.sections) { %>
+ <% if ((section === "goal")&&(plugins.sponsors.goal)) { %>
+
+
+
+ <% } else if (section === "about") { %>
+
+
+
+ <% } %>
+ <% } %>
+ <% } %>
+
+<% } %>
\ No newline at end of file
diff --git a/source/templates/classic/style.css b/source/templates/classic/style.css
index ba0c7f24..1385d50b 100644
--- a/source/templates/classic/style.css
+++ b/source/templates/classic/style.css
@@ -931,11 +931,26 @@
margin-right: 0;
}
-/* Introduction */
- .introduction {
+/* Introduction and sponsors */
+ .introduction, .sponsors {
white-space: normal;
margin: 0 13px 2px;
}
+ .sponsors.goal {
+ padding: 6px 8px;
+ border-radius: 5px;
+ background-color: #7777771F;
+ }
+ .sponsors .goal-text {
+ display: flex;
+ justify-content: space-between;
+ font-style: italic;
+ font-size: 10px;
+ margin-bottom: 4px;
+ }
+ .sponsors .avatar {
+ margin: 2px;
+ }
/* Stackoverflow */
.stackoverflow {
@@ -1137,6 +1152,22 @@
display: inline-block;
width: 97%;
}
+ .markdown p {
+ margin: 8px 0;
+ }
+ .markdown ul {
+ padding-left: 24px;
+ }
+ .markdown a {
+ color: #58a6ff;
+ text-decoration: none;
+ }
+ .markdown blockquote {
+ border-left: 4px solid #7777771F;
+ color: #777777;
+ margin: 0;
+ padding-left: 16px;
+ }
code {
background-color: #7777771F;
display: inline-block;