Skip to content

Commit c16b557

Browse files
gabehfadaexecadaexec
authored
feat: v0.0.10 (#23)
* feat: single SOT for themes + basic custom support * fix: adjust colors for yuu theme * feat: Allow loading of environment variables from file (#20) * feat: allow loading of environment variables from file * Panic if a file for an environment variable cannot be read * Use log.Fatalf + os.Exit instead of panic * fix: remove supurfluous call to os.Exit() --------- Co-authored-by: adaexec <nixos-git.s1pht@simplelogin.com> Co-authored-by: Gabe Farrell <90876006+gabehf@users.noreply.github.com> * chore: add pr test workflow * chore: changelog * feat: make all activity grids configurable * fix: adjust activity grid style * fix: make background gradient consistent size * revert: remove year from activity grid opts * style: adjust top item list min size to 200px * feat: add support for custom themes * fix: stabilized the order of top items * chore: update changelog * feat: native import & export * fix: use correct request body for alias requests * fix: clear input when closing edit modal * chore: changelog * docs: make endpoint clearer for some apps * feat: add ui and handler for export * fix: fix pr test workflow --------- Co-authored-by: adaexec <78047743+adaexec@users.noreply.github.com> Co-authored-by: adaexec <nixos-git.s1pht@simplelogin.com>
1 parent 486f5d0 commit c16b557

51 files changed

Lines changed: 1746 additions & 858 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/test.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Test
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
8+
jobs:
9+
test:
10+
name: Go Test
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- name: Set up Go
16+
uses: actions/setup-go@v5
17+
with:
18+
go-version-file: go.mod
19+
20+
- name: Install libvips
21+
run: |
22+
sudo apt-get update
23+
sudo apt-get install -y libvips-dev
24+
25+
- name: Verify libvips install
26+
run: vips --version
27+
28+
- name: Build
29+
run: go build -v ./...
30+
31+
- name: Test
32+
uses: robherley/go-test-action@v0

CHANGELOG.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,22 @@
1-
# v0.0.9
1+
# v0.0.10
2+
3+
## Features
4+
- Support for custom themes added! You can find the custom theme input in the Appearance menu.
5+
- Allow loading environment variables from files using the _FILE suffix (#20)
6+
- All activity grids (calendar heatmaps) are now configurable
7+
- Native import and export
8+
9+
## Enhancements
10+
- The activity grid on the home page is now configurable
211

312
## Fixes
4-
- Sub-second precision is stripped from incoming listens to ensure they can be deleted reliably
13+
- Sub-second precision is stripped from incoming listens to ensure they can be deleted reliably
14+
- Top items are now sorted by id for stability
15+
- Clear input when closing edit modal
16+
- Use correct request body for create and delete alias requests
17+
18+
## Updates
19+
- Adjusted colors for the "Yuu" theme
20+
- Themes now have a single source of truth in themes.css.ts
21+
- Configurable activity grids now have a re-styled, collapsible menu
22+
- The year option for activity grids has been removed

client/api/api.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ function getStats(period: string): Promise<Stats> {
5353
}
5454

5555
function search(q: string): Promise<SearchResponse> {
56+
q = encodeURIComponent(q)
5657
return fetch(`/apis/web/v1/search?q=${q}`).then(r => r.json() as Promise<SearchResponse>)
5758
}
5859

@@ -131,8 +132,12 @@ function deleteApiKey(id: number): Promise<Response> {
131132
})
132133
}
133134
function updateApiKeyLabel(id: number, label: string): Promise<Response> {
134-
return fetch(`/apis/web/v1/user/apikeys?id=${id}&label=${label}`, {
135-
method: "PATCH"
135+
const form = new URLSearchParams
136+
form.append('id', String(id))
137+
form.append('label', label)
138+
return fetch(`/apis/web/v1/user/apikeys`, {
139+
method: "PATCH",
140+
body: form,
136141
})
137142
}
138143

@@ -154,18 +159,30 @@ function getAliases(type: string, id: number): Promise<Alias[]> {
154159
return fetch(`/apis/web/v1/aliases?${type}_id=${id}`).then(r => r.json() as Promise<Alias[]>)
155160
}
156161
function createAlias(type: string, id: number, alias: string): Promise<Response> {
157-
return fetch(`/apis/web/v1/aliases?${type}_id=${id}&alias=${alias}`, {
158-
method: 'POST'
162+
const form = new URLSearchParams
163+
form.append(`${type}_id`, String(id))
164+
form.append('alias', alias)
165+
return fetch(`/apis/web/v1/aliases`, {
166+
method: 'POST',
167+
body: form,
159168
})
160169
}
161170
function deleteAlias(type: string, id: number, alias: string): Promise<Response> {
162-
return fetch(`/apis/web/v1/aliases?${type}_id=${id}&alias=${alias}`, {
163-
method: "DELETE"
171+
const form = new URLSearchParams
172+
form.append(`${type}_id`, String(id))
173+
form.append('alias', alias)
174+
return fetch(`/apis/web/v1/aliases/delete`, {
175+
method: "POST",
176+
body: form,
164177
})
165178
}
166179
function setPrimaryAlias(type: string, id: number, alias: string): Promise<Response> {
167-
return fetch(`/apis/web/v1/aliases/primary?${type}_id=${id}&alias=${alias}`, {
168-
method: "POST"
180+
const form = new URLSearchParams
181+
form.append(`${type}_id`, String(id))
182+
form.append('alias', alias)
183+
return fetch(`/apis/web/v1/aliases/primary`, {
184+
method: "POST",
185+
body: form,
169186
})
170187
}
171188
function getAlbum(id: number): Promise<Album> {
@@ -179,6 +196,8 @@ function deleteListen(listen: Listen): Promise<Response> {
179196
method: "DELETE"
180197
})
181198
}
199+
function getExport() {
200+
}
182201

183202
export {
184203
getLastListens,
@@ -207,6 +226,7 @@ export {
207226
updateApiKeyLabel,
208227
deleteListen,
209228
getAlbum,
229+
getExport,
210230
}
211231
type Track = {
212232
id: number

client/app/app.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,13 @@ input[type="text"]:focus {
139139
outline: none;
140140
border: 1px solid var(--color-fg-tertiary);
141141
}
142+
textarea {
143+
border: 1px solid var(--color-bg);
144+
}
145+
textarea:focus {
146+
outline: none;
147+
border: 1px solid var(--color-fg-tertiary);
148+
}
142149
input[type="password"] {
143150
border: 1px solid var(--color-bg);
144151
}

client/app/components/ActivityGrid.tsx

Lines changed: 68 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ export default function ActivityGrid({
4545
albumId = 0,
4646
trackId = 0,
4747
configurable = false,
48-
autoAdjust = false,
4948
}: Props) {
5049

5150
const [color, setColor] = useState(getPrimaryColor())
@@ -111,24 +110,26 @@ export default function ActivityGrid({
111110

112111
const getDarkenAmount = (v: number, t: number): number => {
113112

114-
if (autoAdjust) {
115-
// automatically adjust the target value based on step
116-
// the smartest way to do this would be to have the api return the
117-
// highest value in the range. too bad im not smart
118-
switch (stepState) {
119-
case 'day':
120-
t = 10
121-
break;
122-
case 'week':
123-
t = 20
124-
break;
125-
case 'month':
126-
t = 50
127-
break;
128-
case 'year':
129-
t = 100
130-
break;
131-
}
113+
// really ugly way to just check if this is for all items and not a specific item.
114+
// is it jsut better to just pass the target in as a var? probably.
115+
const adjustment = artistId == albumId && albumId == trackId && trackId == 0 ? 10 : 1
116+
117+
// automatically adjust the target value based on step
118+
// the smartest way to do this would be to have the api return the
119+
// highest value in the range. too bad im not smart
120+
switch (stepState) {
121+
case 'day':
122+
t = 10 * adjustment
123+
break;
124+
case 'week':
125+
t = 20 * adjustment
126+
break;
127+
case 'month':
128+
t = 50 * adjustment
129+
break;
130+
case 'year':
131+
t = 100 * adjustment
132+
break;
132133
}
133134

134135
v = Math.min(v, t)
@@ -142,45 +143,58 @@ export default function ActivityGrid({
142143
}
143144
}
144145

145-
return (<div className="flex flex-col items-start">
146-
<h2>Activity</h2>
147-
{configurable ? (
148-
<ActivityOptsSelector
149-
rangeSetter={setRange}
150-
currentRange={rangeState}
151-
stepSetter={setStep}
152-
currentStep={stepState}
153-
/>
154-
) : (
155-
''
156-
)}
157-
<div className="w-auto grid grid-flow-col grid-rows-7 gap-[3px] md:gap-[5px]">
158-
{data.map((item) => (
146+
const CHUNK_SIZE = 26 * 7;
147+
const chunks = [];
148+
149+
for (let i = 0; i < data.length; i += CHUNK_SIZE) {
150+
chunks.push(data.slice(i, i + CHUNK_SIZE));
151+
}
152+
153+
return (
154+
<div className="flex flex-col items-start">
155+
<h2>Activity</h2>
156+
{configurable ? (
157+
<ActivityOptsSelector
158+
rangeSetter={setRange}
159+
currentRange={rangeState}
160+
stepSetter={setStep}
161+
currentStep={stepState}
162+
/>
163+
) : null}
164+
165+
{chunks.map((chunk, index) => (
159166
<div
160-
key={new Date(item.start_time).toString()}
161-
className="w-[10px] sm:w-[12px] h-[10px] sm:h-[12px]"
167+
key={index}
168+
className="w-auto grid grid-flow-col grid-rows-7 gap-[3px] md:gap-[5px] mb-4"
162169
>
163-
<Popup
164-
position="top"
165-
space={12}
166-
extraClasses="left-2"
167-
inner={`${new Date(item.start_time).toLocaleDateString()} ${item.listens} plays`}
168-
>
170+
{chunk.map((item) => (
169171
<div
170-
style={{
171-
display: 'inline-block',
172-
background:
173-
item.listens > 0
174-
? LightenDarkenColor(color, getDarkenAmount(item.listens, 100))
175-
: 'var(--color-bg-secondary)',
176-
}}
177-
className={`w-[10px] sm:w-[12px] h-[10px] sm:h-[12px] rounded-[2px] md:rounded-[3px] ${item.listens > 0 ? '' : 'border-[0.5px] border-(--color-bg-tertiary)'}`}
178-
></div>
179-
</Popup>
172+
key={new Date(item.start_time).toString()}
173+
className="w-[10px] sm:w-[12px] h-[10px] sm:h-[12px]"
174+
>
175+
<Popup
176+
position="top"
177+
space={12}
178+
extraClasses="left-2"
179+
inner={`${new Date(item.start_time).toLocaleDateString()} ${item.listens} plays`}
180+
>
181+
<div
182+
style={{
183+
display: 'inline-block',
184+
background:
185+
item.listens > 0
186+
? LightenDarkenColor(color, getDarkenAmount(item.listens, 100))
187+
: 'var(--color-bg-secondary)',
188+
}}
189+
className={`w-[10px] sm:w-[12px] h-[10px] sm:h-[12px] rounded-[2px] md:rounded-[3px] ${
190+
item.listens > 0 ? '' : 'border-[0.5px] border-(--color-bg-tertiary)'
191+
}`}
192+
></div>
193+
</Popup>
194+
</div>
195+
))}
180196
</div>
181197
))}
182198
</div>
183-
</div>
184-
185199
);
186-
}
200+
}

0 commit comments

Comments
 (0)