Skip to content

Commit

Permalink
add user selection modal with autocomplete
Browse files Browse the repository at this point in the history
  • Loading branch information
slatinsky committed Nov 5, 2023
1 parent fed788b commit aff618c
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 23 deletions.
9 changes: 5 additions & 4 deletions backend/fastapi/Autocomplete.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,13 +238,14 @@ def autocomplete_users(guild_id: str, partial_user_name: str, limit: int):
])
authors= []
for author in cursor:
print(author)
authors.append({
new_author = {
"key": author['names'][0],
"description": ", ".join(author['nicknames']),
"description2": str(author['msg_count']) + " messages",
"icon": author['avatar']
})
"icon": author['avatar'],
"id": author['_id']
}
authors.append(new_author)
return authors

def autocomplete_has(guild_id: str, partial_has: str, limit: int):
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/messages/NewMessage.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import type { Message } from 'src/js/interfaces';
import { renderDate, renderTimestamp } from 'src/js/time';
import { contextMenuItems} from '../menu/menuStore';
import { currentUserId, linkHandler, timestampFormat } from 'src/components/settings/settingsStore';
import { currentUserId, linkHandler, setCurrentUser, timestampFormat } from 'src/components/settings/settingsStore';
import MessageAttachments from './MessageAttachments.svelte';
import MessageEmbeds from './MessageEmbeds.svelte';
import MessageMarkdown from './MessageMarkdown.svelte';
Expand Down Expand Up @@ -125,7 +125,7 @@
{
"name": "View discord as this user",
"action": () => {
$currentUserId = message.author._id
setCurrentUser(message.author._id, message.author.name, message.author.nickname, checkUrl(message.author.avatar))
}
},
{
Expand Down
59 changes: 59 additions & 0 deletions frontend/src/components/search/AutocompleteUser.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<script lang="ts">
export let photo: string;
export let name1: string;
export let name2: string;
</script>

<div class="user-switcher" on:click>
<img src={photo} alt="" on:error={e => e.target.src = '/favicon.png'} />
<div>
<div class="user-switcher-name1">{name1}</div>
<div class="user-switcher-name2">{name2}</div>
</div>
</div>


<style>
.user-switcher {
height: 31px;
margin-right: 8px;
display: flex;
align-items: center;
padding: 5px 10px 5px 5px;
border-radius: 4px;
cursor: pointer;
overflow: hidden;
}
.user-switcher img {
margin-right: 8px;
border-radius: 50%;
width: 32px;
height: 32px;
}
.user-switcher .user-switcher-name1 {
font-size: 14px;
line-height: 18px;
font-weight: 400;
color: #F2F3F5;
}
.user-switcher .user-switcher-name2 {
font-size: 12px;
line-height: 16px;
font-weight: 400;
color: #B5BAC1;
}
.user-switcher .user-switcher-name1,
.user-switcher .user-switcher-name2 {
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
}
.user-switcher:hover {
background-color: #3d3e45;
}
</style>
17 changes: 16 additions & 1 deletion frontend/src/components/settings/settingsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,18 @@ export const nameRenderer = writable("nickname");
export const locale = writable('en'); // for date formatting
export const dateFormat = writable('DD/MM/YYYY');
export const timeFormat = writable('HH:mm');
export const currentUserId = writable(''); // discord snowflake of the current user, '' if not "logged in"

export const currentUserId = writable('');
export const currentUserName1 = writable('Anonymous');
export const currentUserName2 = writable('click to change');
export const currentUserPhoto = writable('/favicon.png');

export function setCurrentUser(id: string, name1: string, name2: string, photo: string) {
currentUserId.set(id);
currentUserName1.set(name1);
currentUserName2.set(name2);
currentUserPhoto.set(photo);
}

// for {#key} blocks
export const timestampFormat = derived([dateFormat, timeFormat, locale], ([dateFormat, timeFormat, locale]) => {
Expand Down Expand Up @@ -56,6 +67,10 @@ withLocalStorage(channelScrollPosition, "channelScrollPosition", "string");
withLocalStorage(hideSpoilers, "hideSpoilers", "bool");
withLocalStorage(font, "font", "string");
withLocalStorage(currentUserId, "currentUserId", "string");
withLocalStorage(currentUserName1, "currentUserName1", "string");
withLocalStorage(currentUserName2, "currentUserName2", "string");
withLocalStorage(currentUserPhoto, "currentUserPhoto", "string");


// old localstorage keys that should never be used again for backwards compatibility:
// `dateFormat`, `timeFormat`
42 changes: 26 additions & 16 deletions frontend/src/routes/channels/[guildId]/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
import ChannelsMenu from '../../../components/channels/MenuCategories.svelte';
import Container from 'src/components/containers/Container.svelte';
import { settingsShown } from 'src/components/settings/settingsStore';
import { currentUserName1, currentUserName2, currentUserPhoto, settingsShown } from 'src/components/settings/settingsStore';
import { isMenuHidden } from 'src/components/menu/menuStore';
import MenuOpenOverlay from 'src/components/menu/MenuOpenOverlay.svelte';
import UserSelectionModal from './[channelId]/UserSelectionModal.svelte';
import AutocompleteUser from 'src/components/search/AutocompleteUser.svelte';
let currentGuildId: string = data.guildId;
function guildChanged(_) { // fix crash if shifting between guilds and searching at the same time
Expand All @@ -22,6 +24,8 @@
console.log('current guild', data.guild);
}
$: guildChanged(data);
let showUserSelectionModal = false;
</script>


Expand All @@ -39,11 +43,15 @@
<div id="channelList">
<ChannelsMenu selectedGuildId={data.guildId} channels={data.channels} selectedChannelId={data.channelId} />
</div>
<div class="settings" on:click={()=>$settingsShown = true}>
<div id="bottom-bar">
<div style="width: 170px;">
<AutocompleteUser photo={$currentUserPhoto} name1={$currentUserName1} name2={$currentUserName2} on:click={() => showUserSelectionModal = true}/>
</div>
<div id="settings" on:click={()=>$settingsShown = true}>
<svg aria-hidden="true" role="img" width="20" height="20" viewBox="0 0 24 24"><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M19.738 10H22V14H19.739C19.498 14.931 19.1 15.798 18.565 16.564L20 18L18 20L16.565 18.564C15.797 19.099 14.932 19.498 14 19.738V22H10V19.738C9.069 19.498 8.203 19.099 7.436 18.564L6 20L4 18L5.436 16.564C4.901 15.799 4.502 14.932 4.262 14H2V10H4.262C4.502 9.068 4.9 8.202 5.436 7.436L4 6L6 4L7.436 5.436C8.202 4.9 9.068 4.502 10 4.262V2H14V4.261C14.932 4.502 15.797 4.9 16.565 5.435L18 3.999L20 5.999L18.564 7.436C19.099 8.202 19.498 9.069 19.738 10ZM12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16Z"></path></svg>
Settings
</div>
</div>
</div>
<div id="header">
{#key data.channelId}
<Header channel={data.channel} thread={data.thread} guildId={data.guildId} />
Expand All @@ -61,6 +69,8 @@
{/if}
{/key}

<UserSelectionModal bind:showModal={showUserSelectionModal} guildId={currentGuildId}/>

<style>
#guild-layout {
display: grid;
Expand Down Expand Up @@ -136,27 +146,27 @@
display: none;
}
.settings {
margin-top: auto;
padding: 10px 0 10px 14px;
font-size: 14px;
font-weight: 600;
#bottom-bar {
position: sticky;
bottom: 0;
display: flex;
align-items: center;
background-color: #232428;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.1);
height: 30px;
height: 52px;
padding: 5px 0 5px 5px;
}
display: flex;
align-items: center;
gap: 5px;
/* padding: 10px 10px 10px 0; */
#settings {
display: grid;
place-items: center;
width: 32px;
height: 32px;
border-radius: 4px;
cursor: pointer;
}
.settings:hover {
#settings:hover {
background-color: #3d3e45;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<script lang="ts">
import AutocompleteUser from 'src/components/search/AutocompleteUser.svelte';
import { currentUserId, setCurrentUser } from 'src/components/settings/settingsStore';
import { checkUrl } from "src/js/helpers";
export let showModal = false;
export let guildId: string;
let searchText = '';
let inputDom: HTMLInputElement;
$: if (showModal) {
setTimeout(() => {
if (inputDom)
inputDom.focus()
}, 100);
}
interface AutocompletedUser {
name1: string;
name2: string;
photo: string;
id: string;
}
let users: AutocompletedUser[] = []
async function autocompleteUsers(searchText: string, guildId: string, _: any) {
let response = await fetch(`/api/search-autocomplete?guild_id=${encodeURIComponent(guildId)}&key=users&value=${encodeURIComponent(searchText)}`);
let json = await response.json();
let newUsers = [];
if ($currentUserId != '' && 'anonymous'.toLowerCase().includes(searchText)) {
newUsers.push({
name1: "Anonymous",
name2: "",
photo: "/favicon.png",
id: ""
})
}
for (let user of json) {
newUsers.push({
name1: user.key,
name2: user.description,
photo: checkUrl(user.icon),
id: user.id
})
}
users = newUsers;
console.log('users', users);
}
$: autocompleteUsers(searchText, guildId, $currentUserId);
function selectUser(user: AutocompletedUser) {
console.log('selected user', user);
// setCurrentUser(id: string, name1: string, name2: string, photo: string)
setCurrentUser(user.id, user.name1, user.name2, user.photo);
showModal = false;
}
</script>



{#if showModal}
<div class="gallery-wrapper" on:click={()=>showModal=false}>
<div class="gallery-closebtn" on:click={()=>showModal=false}>&times;</div>
<div id="userselect" on:click|stopPropagation>
<div id="userselect-inner">
<div id="input-wrapper">
<input type="text" placeholder="Search for a user" bind:value={searchText} bind:this={inputDom} on:keydown={(e) => {if (e.key == 'Enter') selectUser(users[0])}}/>
</div>
{#each users as user}
<AutocompleteUser photo={user.photo} name1={user.name1} name2={user.name2} on:click={() => selectUser(user)}/>
{/each}
</div>
</div>
</div>
{/if}
<style>
.gallery-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.8);
z-index: 1000;
display: flex;
justify-content: center;
align-items: center;
display: flex;
flex-direction: column;
text-align: left;
}
.gallery-closebtn {
position: absolute;
top: -15px;
right: 0;
padding: 10px;
color: white;
cursor: pointer;
font-size: 3rem;
font-weight: 600;
z-index: 1001;
}
#userselect {
width: 250px;
max-width: 95svw;
height: 400px;
max-height: 95svh;
color: #f2f3f5;
position: relative;
display: flex;
flex-direction: row;
}
#userselect-inner {
background-color: #2b2d31;
width: 100%;
height: calc(100% - 20px);
overflow-y: auto;
padding: 0 10px 10px 10px;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
#input-wrapper {
position: sticky;
top: 0;
background-color: #2b2d31;
padding: 12px 0;
}
input {
width: calc(100% - 30px);
background-color: #202225;
color: white;
height: 25px;
border: 0px;
border-radius: 3px;
padding: 0px 10px;
outline: none;
}
input::placeholder {
color: #909297;
}
</style>

0 comments on commit aff618c

Please sign in to comment.