Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 37 additions & 11 deletions kinode/packages/homepage/homepage/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![feature(let_chains)]
use kinode_process_lib::{
await_message, call_init,
await_message, call_init, get_blob,
http::{
bind_http_path, bind_http_static_path, send_response, serve_ui, HttpServerError,
HttpServerRequest, StatusCode,
Expand All @@ -22,6 +22,7 @@ struct HomepageApp {
label: String,
base64_icon: Option<String>,
widget: Option<String>,
order: Option<u16>,
}

wit_bindgen::generate!({
Expand Down Expand Up @@ -66,6 +67,7 @@ fn init(our: Address) {

bind_http_path("/apps", true, false).expect("failed to bind /apps");
bind_http_path("/version", true, false).expect("failed to bind /version");
bind_http_path("/order", true, false).expect("failed to bind /order");

loop {
let Ok(ref message) = await_message() else {
Expand Down Expand Up @@ -107,6 +109,7 @@ fn init(our: Address) {
label,
base64_icon: icon,
widget,
order: None,
},
);
}
Expand All @@ -126,16 +129,11 @@ fn init(our: Address) {
"Content-Type".to_string(),
"application/json".to_string(),
)])),
format!(
"[{}]",
app_data
.values()
.map(|app| serde_json::to_string(app).unwrap())
.collect::<Vec<String>>()
.join(",")
)
.as_bytes()
.to_vec(),
{
let mut apps: Vec<_> = app_data.values().collect();
apps.sort_by_key(|app| app.order.unwrap_or(255));
serde_json::to_vec(&apps).unwrap_or_else(|_| Vec::new())
},
);
}
"/version" => {
Expand All @@ -145,6 +143,34 @@ fn init(our: Address) {
version_from_cargo_toml().as_bytes().to_vec(),
);
}
"/order" => {
// POST of a list of package names.
// go through the list and update each app in app_data to have the index of its name in the list as its order
if let Some(body) = get_blob() {
let apps: Vec<String> =
serde_json::from_slice(&body.bytes).unwrap();
for (i, app) in apps.iter().enumerate() {
println!("setting order for {app} to {i}");
if let Some(app) = app_data.get_mut(app) {
app.order = Some(i as u16);
}
}
send_response(
StatusCode::OK,
Some(HashMap::from([(
"Content-Type".to_string(),
"application/json".to_string(),
)])),
vec![],
);
} else {
send_response(
StatusCode::BAD_REQUEST,
Some(HashMap::new()),
vec![],
);
}
}
_ => {
send_response(
StatusCode::OK,
Expand Down
87 changes: 0 additions & 87 deletions kinode/packages/homepage/pkg/ui/assets/index-C2uJ-HA5.js

This file was deleted.

154 changes: 154 additions & 0 deletions kinode/packages/homepage/pkg/ui/assets/index-IOrZqiKW.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion kinode/packages/homepage/pkg/ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1.00001, viewport-fit=cover" />
<script type="module" crossorigin src="/assets/index-C2uJ-HA5.js"></script>
<script type="module" crossorigin src="/assets/index-IOrZqiKW.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-C1M4NwvJ.css">
</head>

Expand Down
2 changes: 1 addition & 1 deletion kinode/packages/homepage/ui/dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1.00001, viewport-fit=cover" />
<script type="module" crossorigin src="/assets/index-C2uJ-HA5.js"></script>
<script type="module" crossorigin src="/assets/index-IOrZqiKW.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-C1M4NwvJ.css">
</head>

Expand Down
4 changes: 3 additions & 1 deletion kinode/packages/homepage/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dependencies": {
"classnames": "^2.5.1",
"react": "^18.2.0",
"react-beautiful-dnd": "^13.1.1",
"react-dom": "^18.2.0",
"react-icons": "^5.1.0",
"react-router-dom": "^6.23.0",
Expand All @@ -21,6 +22,7 @@
},
"devDependencies": {
"@types/react": "^18.2.66",
"@types/react-beautiful-dnd": "^13.1.8",
"@types/react-dom": "^18.2.22",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
Expand All @@ -31,4 +33,4 @@
"typescript": "^5.2.2",
"vite": "^5.2.0"
}
}
}
107 changes: 97 additions & 10 deletions kinode/packages/homepage/ui/src/components/AppsDock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,113 @@ import usePersistentStore from "../store/persistentStore"
import { useEffect, useState } from "react"
import { isMobileCheck } from "../utilities/dimensions"
import classNames from "classnames"
import { DragDropContext, Draggable, DropResult, Droppable } from 'react-beautiful-dnd'

const AppsDock: React.FC = () => {
const { apps } = useHomepageStore()
const { favoriteApps } = usePersistentStore()
const { favoriteApps, setFavoriteApps } = usePersistentStore()
const [dockedApps, setDockedApps] = useState<HomepageApp[]>([])

useEffect(() => {
setDockedApps(apps.filter(a => favoriteApps[a.package_name]))
let final: HomepageApp[] = []
const dockedApps = Object.keys(favoriteApps)
.map(name => ({ ...apps.find(a => a.package_name === name), order: favoriteApps[name].order }))
.filter(a => a) as HomepageApp[]
const orderedApps = dockedApps.filter(a => a.order !== undefined && a.order !== null)
const unorderedApps = dockedApps.filter(a => a.order === undefined || a.order === null)

for (let i = 0; i < orderedApps.length; i++) {
final[orderedApps[i].order!] = orderedApps[i]
}

final = final.filter(a => a)
unorderedApps.forEach(a => final.push(a))
console.log({ final })
setDockedApps(final)
}, [apps, favoriteApps])

const isMobile = isMobileCheck()

return <div className={classNames('flex-center flex-wrap border border-orange bg-orange/25 p-2 rounded !rounded-xl', {
'gap-8 mb-4': !isMobile,
'gap-4 mb-2': isMobile
})}>
{dockedApps.length === 0
? <div>Favorite an app to pin it to your dock.</div>
: dockedApps.map(app => <AppDisplay app={app} />)}
</div>
// a little function to help us with reordering the result
const reorder = (list: HomepageApp[], startIndex: number, endIndex: number) => {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);

return result;
};

const onDragEnd = (result: DropResult) => {
// dropped outside the list
if (!result.destination) {
return;
}

const items = reorder(
dockedApps,
result.source.index,
result.destination.index
);

const packageNames = items.map(app => app.package_name);

const faves = { ...favoriteApps }

packageNames.forEach((name, i) => {
console.log('setting order for', name, 'to', i)
faves[name].order = i
})

setFavoriteApps(faves)

console.log({ favoriteApps })

fetch('/order', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(packageNames)
})
.then(data => {
console.log({ data })
})
.catch(e => console.error(e));
}

return <DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable" direction="horizontal">
{(provided, _snapshot) => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
className={classNames('flex-center flex-wrap border border-orange bg-orange/25 p-2 rounded !rounded-xl', {
'gap-8 mb-4': !isMobile,
'gap-4 mb-2': isMobile,
})}
>
{dockedApps.length === 0
? <div>Favorite an app to pin it to your dock.</div>
: dockedApps.map(app => <Draggable
key={app.package_name}
draggableId={app.package_name}
index={dockedApps.indexOf(app)}
>
{(provided, _snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<AppDisplay app={app} />
</div>
)}
</Draggable>)}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
}

export default AppsDock
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ const WidgetsSettingsModal = () => {
</div>
</div>
</div>)}
<button
className="clear"
onClick={() => window.location.href = '/settings:settings:sys'}
>
Looking for system settings?
</button>
</div>
</Modal>
}
Expand Down
1 change: 1 addition & 0 deletions kinode/packages/homepage/ui/src/store/homepageStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface HomepageApp {
our_version: string
}
widget?: string
order?: number
}

export interface HomepageStore {
Expand Down
12 changes: 10 additions & 2 deletions kinode/packages/homepage/ui/src/store/persistentStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ export interface PersistentStore {
toggleWidgetVisibility: (package_name: string) => void
setWidgetSize: (package_name: string, size: 'small' | 'large') => void,
favoriteApps: {
[key: string]: boolean
[key: string]: {
favorite: boolean
order?: number
}
}
setFavoriteApps: (favoriteApps: PersistentStore['favoriteApps']) => void
favoriteApp: (package_name: string) => void
}

Expand All @@ -27,6 +31,7 @@ const usePersistentStore = create<PersistentStore>()(
widgetSettings: {},
favoriteApps: {},
setWidgetSettings: (widgetSettings: PersistentStore['widgetSettings']) => set({ widgetSettings }),
setFavoriteApps: (favoriteApps: PersistentStore['favoriteApps']) => set({ favoriteApps }),
toggleWidgetVisibility: (package_name: string) => {
const { widgetSettings } = get()
set({
Expand Down Expand Up @@ -56,7 +61,10 @@ const usePersistentStore = create<PersistentStore>()(
set({
favoriteApps: {
...favoriteApps,
[package_name]: !favoriteApps[package_name]
[package_name]: {
...favoriteApps[package_name],
favorite: !favoriteApps[package_name]?.favorite
}
}
})
},
Expand Down
Loading