diff --git a/package-lock.json b/package-lock.json
index f397e14..9d51b77 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -40,6 +40,7 @@
"react-number-format": "^5.3.1",
"react-redux": "^8.1.1",
"react-router-dom": "^6.20.1",
+ "react-window": "^1.8.10",
"redux": "^4.2.1",
"redux-persist": "^6.0.0",
"remove-markdown": "^0.5.0",
@@ -5840,6 +5841,11 @@
"node": ">= 4.0.0"
}
},
+ "node_modules/memoize-one": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
+ "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
+ },
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@@ -7104,6 +7110,22 @@
"react-dom": ">=16.6.0"
}
},
+ "node_modules/react-window": {
+ "version": "1.8.10",
+ "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz",
+ "integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==",
+ "dependencies": {
+ "@babel/runtime": "^7.0.0",
+ "memoize-one": ">=3.1.1 <6"
+ },
+ "engines": {
+ "node": ">8.0.0"
+ },
+ "peerDependencies": {
+ "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
diff --git a/package.json b/package.json
index bb4a097..480112c 100644
--- a/package.json
+++ b/package.json
@@ -34,8 +34,8 @@
"css-minimizer-webpack-plugin": "^5.0.1",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.5.3",
- "lemmy-js-client18": "npm:lemmy-js-client@^0.18.1",
"lemmy-js-client": "npm:lemmy-js-client@^0.19.0-rc.19",
+ "lemmy-js-client18": "npm:lemmy-js-client@^0.18.1",
"moment": "^2.29.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -45,9 +45,10 @@
"react-number-format": "^5.3.1",
"react-redux": "^8.1.1",
"react-router-dom": "^6.20.1",
+ "react-window": "^1.8.10",
"redux": "^4.2.1",
- "remove-markdown": "^0.5.0",
"redux-persist": "^6.0.0",
+ "remove-markdown": "^0.5.0",
"sass": "^1.69.5",
"sass-loader": "^13.3.2",
"sonner": "^1.2.4",
diff --git a/src/components/InstanceSelect.jsx b/src/components/InstanceSelect.jsx
new file mode 100644
index 0000000..1ba159b
--- /dev/null
+++ b/src/components/InstanceSelect.jsx
@@ -0,0 +1,195 @@
+import React from "react";
+// import { connect } from "react-redux";
+
+// import useQueryCache from "../../hooks/useQueryCache";
+
+import { FixedSizeList } from "react-window";
+
+import { Popper } from "@mui/base/Popper";
+import Autocomplete, { createFilterOptions } from "@mui/joy/Autocomplete";
+import AutocompleteListbox from "@mui/joy/AutocompleteListbox";
+import AutocompleteOption from "@mui/joy/AutocompleteOption";
+import FormControl from "@mui/joy/FormControl";
+import ListItemDecorator from "@mui/joy/ListItemDecorator";
+
+import Add from "@mui/icons-material/Add";
+
+// import { setHomeInstance } from "../../reducers/configReducer";
+
+import useLVQueryCache from "../hooks/useLVQueryCache";
+/**
+ * This component renders a button that allows the user to select a home instance.
+ *
+ * It uses a react-window Virtualized List to render the list of instances.
+ */
+
+const filterOptions = createFilterOptions({
+ // matchFrom: "start",
+ stringify: (option) => option.base,
+ trim: true,
+ ignoreCase: true,
+});
+
+const LISTBOX_PADDING = 6; // px
+
+function renderRow(props) {
+ const { data, index, style } = props;
+ const dataSet = data[index];
+ const inlineStyle = {
+ ...style,
+ top: style.top + LISTBOX_PADDING,
+ overflow: "hidden",
+ textOverflow: "ellipsis",
+ whiteSpace: "nowrap",
+ };
+
+ return (
+
+ {dataSet[1].name?.startsWith('Add "') && (
+
+
+
+ )}
+ {typeof dataSet[1] == "string" && dataSet[1]}
+ {dataSet[1].base && (
+ <>
+ {dataSet[1].name} ({dataSet[1].base})
+ >
+ )}
+
+ );
+}
+
+const OuterElementContext = React.createContext({});
+
+const OuterElementType = React.forwardRef((props, ref) => {
+ const outerProps = React.useContext(OuterElementContext);
+ return (
+
+ );
+});
+
+// Adapter for react-window
+const ListboxComponent = React.forwardRef(function ListboxComponent(props, ref) {
+ const { children, anchorEl, open, modifiers, ...other } = props;
+ const itemData = [];
+
+ children[0].forEach((item) => {
+ if (item) {
+ itemData.push(item);
+ itemData.push(...(item.children || []));
+ }
+ });
+
+ const itemCount = itemData.length;
+ const itemSize = 40;
+
+ return (
+
+
+ ({
+ zIndex: theme.zIndex.modal + 1000,
+ })}
+ >
+ {renderRow}
+
+
+
+ );
+});
+
+export default function InstanceSelect({
+ placeholder,
+ value,
+ onChange,
+ sx,
+ variant,
+ color,
+ disabled,
+ ...props
+}) {
+ const { isLoading, error, data } = useLVQueryCache("instanceMinData", "instance.min");
+
+ const handleChange = (event, newValue) => {
+ console.log("onChange", newValue);
+
+ onChange(newValue ? newValue.base : "");
+ };
+
+ return (
+ option.code === value.code}
+ renderOption={(props, option) => [props, option]}
+ // TODO: Post React 18 update - validate this conversion, look like a hidden bug
+ // renderGroup={(params) => params}
+ filterOptions={(options, params) => {
+ const filtered = filterOptions(options, params);
+
+ const { inputValue } = params;
+
+ // Suggest the creation of a new value
+ const isExisting = options.some((option) => inputValue === option.base);
+ if (inputValue !== "" && !isExisting) {
+ const cleanedUrl = inputValue
+ .replace("http:", "")
+ .replace("https:", "")
+ .replace("//", "")
+ .replace("/", "");
+ filtered.push({
+ name: `Other`,
+ base: cleanedUrl,
+ });
+ }
+
+ return filtered;
+ }}
+ getOptionLabel={(option) => {
+ // console.log("getOptionLabel", option);
+ // Value selected with enter, right from the input
+ if (typeof option === "string") {
+ return option;
+ }
+
+ // Regular option
+ return option.base;
+ }}
+ />
+ );
+}
diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx
index ba771cc..ef88414 100644
--- a/src/pages/Login.jsx
+++ b/src/pages/Login.jsx
@@ -18,6 +18,8 @@ import ListItemContent from "@mui/joy/ListItemContent";
import IconButton from "@mui/joy/IconButton";
import Delete from "@mui/icons-material/Delete";
+import InstanceSelect from "../components/InstanceSelect.jsx";
+
import LemmyHttpMixed from "../lib/LemmyHttpMixed";
import { LemmyHttp } from "lemmy-js-client";
@@ -154,7 +156,17 @@ export default function LoginForm() {
width: "60%",
}}
>
- (domainLock ? null : setInstanceBase(newValue))}
+ sx={{ mb: 1, width: "100%" }}
+ disabled={domainLock || accountIsLoading}
+ variant="outlined"
+ color="neutral"
+ />
+
+ {/* (domainLock ? null : setInstanceBase(e.target.value))}
@@ -162,7 +174,7 @@ export default function LoginForm() {
color="neutral"
sx={{ mb: 1, width: "100%" }}
disabled={domainLock || accountIsLoading}
- />
+ /> */}