Skip to content

Commit

Permalink
Merge pull request #125 from tgxn/instance_search
Browse files Browse the repository at this point in the history
add instance select
  • Loading branch information
tgxn authored Dec 2, 2023
2 parents 72417c4 + b22d1e8 commit 249af59
Show file tree
Hide file tree
Showing 4 changed files with 234 additions and 4 deletions.
22 changes: 22 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
195 changes: 195 additions & 0 deletions src/components/InstanceSelect.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<AutocompleteOption key={dataSet[1].base} {...dataSet[0]} style={inlineStyle}>
{dataSet[1].name?.startsWith('Add "') && (
<ListItemDecorator>
<Add />
</ListItemDecorator>
)}
{typeof dataSet[1] == "string" && dataSet[1]}
{dataSet[1].base && (
<>
{dataSet[1].name} ({dataSet[1].base})
</>
)}
</AutocompleteOption>
);
}

const OuterElementContext = React.createContext({});

const OuterElementType = React.forwardRef((props, ref) => {
const outerProps = React.useContext(OuterElementContext);
return (
<AutocompleteListbox
{...props}
{...outerProps}
component="div"
ref={ref}
sx={{
// zIndex: 1300,
"& ul": {
padding: 0,
// zIndex: 1300,
margin: 0,
flexShrink: 0,
},
}}
/>
);
});

// 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 (
<Popper ref={ref} anchorEl={anchorEl} open={open} modifiers={modifiers} style={{ zIndex: 10000 }}>
<OuterElementContext.Provider value={other}>
<FixedSizeList
itemData={itemData}
height={itemSize * 8}
width="100%"
outerElementType={OuterElementType}
innerElementType="ul"
itemSize={itemSize}
overscanCount={5}
itemCount={itemCount}
sx={(theme) => ({
zIndex: theme.zIndex.modal + 1000,
})}
>
{renderRow}
</FixedSizeList>
</OuterElementContext.Provider>
</Popper>
);
});

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 (
<Autocomplete
sx={{ zIndex: 14000, ...sx }}
value={value || ""}
disabled={disabled}
onChange={handleChange}
selectOnFocus //to help the user clear the selected value.
handleHomeEndKeys // to move focus inside the popup with the Home and End keys.
freeSolo
disableListWrap
placeholder="Select Instance"
slots={{
listbox: ListboxComponent,
}}
options={data || []}
loading={data == null}
getOptionSelected={(option, value) => 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;
}}
/>
);
}
16 changes: 14 additions & 2 deletions src/pages/Login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -154,15 +156,25 @@ export default function LoginForm() {
width: "60%",
}}
>
<Input
<InstanceSelect
placeholder="Instance URL"
value={instanceBase}
onChange={(newValue) => (domainLock ? null : setInstanceBase(newValue))}
sx={{ mb: 1, width: "100%" }}
disabled={domainLock || accountIsLoading}
variant="outlined"
color="neutral"
/>

{/* <Input
placeholder="Instance URL"
value={instanceBase}
onChange={(e) => (domainLock ? null : setInstanceBase(e.target.value))}
variant="outlined"
color="neutral"
sx={{ mb: 1, width: "100%" }}
disabled={domainLock || accountIsLoading}
/>
/> */}
<Input
placeholder="Username or Email"
value={username}
Expand Down

0 comments on commit 249af59

Please sign in to comment.