Skip to content

Commit

Permalink
Merge pull request #26 from DevlinRocha/server-settings
Browse files Browse the repository at this point in the history
Server settings created - server owners can now edit the names and icons for their servers after creation
  • Loading branch information
DevlinRocha authored Jan 19, 2022
2 parents f8666eb + ae91296 commit 7dddebb
Showing 21 changed files with 584 additions and 29 deletions.
10 changes: 9 additions & 1 deletion firebase.ts
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@ import {
} from "firebase/auth";
import { getDownloadURL, getStorage, ref, uploadBytes } from "firebase/storage";
import { UserData } from "./src/features/user";
import { ServerData } from "./src/features/servers";

// import { getAnalytics } from "firebase/analytics";

@@ -285,6 +286,11 @@ async function setServerOwner(serverID: string, userID: string) {
});
}

export async function saveServerChanges(newServer: ServerData) {
await updateServerDatabase(newServer.serverID, "img", newServer.img);
await updateServerDatabase(newServer.serverID, "name", newServer.name);
}

export async function uploadServerImagePreview(file: File, userID: string) {
const storage = getStorage();

@@ -301,7 +307,7 @@ async function getServerImagePreviewURL(userID: string) {
return await getDownloadURL(ref(storage, `users/${userID}/temp/serverImage`));
}

async function uploadServerImage(file: File, serverID: string) {
export async function uploadServerImage(file: File, serverID: string) {
const storage = getStorage();

const serverImageRef = ref(storage, `servers/${serverID}/serverImage`);
@@ -311,6 +317,8 @@ async function uploadServerImage(file: File, serverID: string) {
const serverImageURL = await getServerImageURL(serverID);

await updateServerDatabase(serverID, "img", serverImageURL);

return serverImageURL;
}

async function getServerImageURL(serverID: string) {
67 changes: 60 additions & 7 deletions src/components/UnsavedChanges.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,60 @@
import tw from "tailwind-styled-components/dist/tailwind";
import { saveUserProfileChanges, uploadAvatar } from "../../firebase";
import {
saveServerChanges,
saveUserProfileChanges,
uploadAvatar,
uploadServerImage,
} from "../../firebase";
import { ServerData, setServer, useServersState } from "../features/servers";
import {
setServerCopy,
useServerSettingsState,
} from "../features/serverSettings";
import { setUser, useUserState } from "../features/user";
import { setUserCopy, useUserSettingsState } from "../features/userSettings";
import { useAppDispatch } from "../redux/hooks";

export default function UnsavedChanges() {
interface UnsavedChangesProps {
changes: string;
}

export default function UnsavedChanges(props: UnsavedChangesProps) {
const { user, avatarPreview } = useUserState();
const { userCopy, unsavedChangesError } = useUserSettingsState();
const { server } = useServersState();
const { serverCopy, serverIconPreview } = useServerSettingsState();
const dispatch = useAppDispatch();

function resetChanges() {
if (!userCopy) return;
dispatch(setUser(userCopy));
switch (props.changes) {
case "user":
if (!userCopy) return;
dispatch(setUser(userCopy));
break;

case "server":
if (!serverCopy) return;
dispatch(setServer(serverCopy));
break;
}
}

async function saveChanges() {
dispatch(setUserCopy(user));
switch (props.changes) {
case "user":
dispatch(setUserCopy(user));

if (user.avatar !== userCopy?.avatar) return await saveAvatar();
await saveUserProfileChanges(user);
break;

if (user.avatar !== userCopy?.avatar) return await saveAvatar();
await saveUserProfileChanges(user);
case "server":
dispatch(setServerCopy(server));

if (server.img !== serverCopy?.img) return await saveIcon();
await saveServerChanges(server);
break;
}
}

async function saveAvatar() {
@@ -34,6 +70,23 @@ export default function UnsavedChanges() {
await saveUserProfileChanges(newUser);
}

async function saveIcon() {
if (!serverIconPreview) return dispatchServerChanges(server);

const iconURL = await uploadServerImage(serverIconPreview, server.serverID);

const newServer = { ...server };
newServer.img = iconURL;

dispatchServerChanges(newServer);
}

async function dispatchServerChanges(newServer: ServerData) {
dispatch(setServer(newServer));
dispatch(setServerCopy(newServer));
await saveServerChanges(server);
}

return (
<Container>
<ContentContainer unsavedChangesError={unsavedChangesError}>
4 changes: 2 additions & 2 deletions src/components/channels/Channels.tsx
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@ export default function Channels() {
return () => {
unsubscribe();
};
}, [server]);
}, [server.serverID]);

useEffect(() => {
if (!server.serverID || !server.defaultChannel) return;
@@ -81,7 +81,7 @@ export default function Channels() {
return () => {
unsubscribe();
};
}, [server]);
}, [server.serverID]);

function joinChannel(channel: ChannelData) {
dispatch(setChannel(channel));
8 changes: 8 additions & 0 deletions src/components/serverDropdown/ServerDropdown.tsx
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ import {
setCreateChannelOpen,
setInviteFriendsOpen,
setServerDropdownOpen,
setServerSettingsOpen,
} from "../../features/serverSettings";
import { UserRole } from "../../features/user";
import { useAppDispatch } from "../../redux/hooks";
@@ -29,6 +30,13 @@ export default function ServerDropdown(props: ServerDropdownProps) {
<ListItemInvite onClick={() => dispatch(setInviteFriendsOpen(true))}>
Invite people
</ListItemInvite>

{props.userRoles && props.userRoles.serverOwner && (
<ListItem onClick={() => dispatch(setServerSettingsOpen(true))}>
Server Settings
</ListItem>
)}

{props.userRoles && props.userRoles.serverOwner && (
<ListItem onClick={() => dispatch(setCreateChannelOpen(true))}>
Create Channel
88 changes: 88 additions & 0 deletions src/components/serverSettings/ServerSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import tw from "tailwind-styled-components/dist/tailwind";
import ServerSettingsSidebar from "./ServerSettingsSidebar";
import ServerSettingsView from "./serverSettingsView/ServerSettingsView";
import Image from "next/image";
import closeButton from "../../../assets/closeButton.svg";
import { useAppDispatch } from "../../redux/hooks";
import { useEffect } from "react";
import {
setServerSettingsOpen,
setServerCopy,
setServerSettingsScreen,
useServerSettingsState,
} from "../../features/serverSettings";
import { useServersState } from "../../features/servers";
import { setUnsavedChangesError } from "../../features/userSettings";

export default function ServerSettings() {
const { server } = useServersState();
const { serverCopy } = useServerSettingsState();
const dispatch = useAppDispatch();

useEffect(() => {
dispatch(setServerCopy(server));
}, []);

function unsavedChanges() {
if (!serverCopy) return false;

if (serverCopy !== server) {
dispatch(setUnsavedChangesError(true));

setTimeout(() => {
dispatch(setUnsavedChangesError(false));
}, 1500);

return true;
}
}

function closeWindow() {
if (unsavedChanges()) return;

dispatch(setServerSettingsOpen(false));
dispatch(setServerSettingsScreen("Overview"));
}

return (
<Container>
<ServerSettingsSidebar />

<SettingsContainer>
<ServerSettingsView />

<CloseButton>
<StyledImage
onClick={closeWindow}
src={closeButton}
width={36}
height={36}
alt={"Close button"}
/>

<Caption>ESC</Caption>
</CloseButton>
</SettingsContainer>
</Container>
);
}

const Container = tw.div`
flex flex-none w-screen h-screen select-none
`;

const SettingsContainer = tw.div`
relative flex w-full h-full
`;

const CloseButton = tw.figure`
flex-none pt-15 text-center mr-5
`;

const StyledImage = tw(Image)`
cursor-pointer
`;

const Caption = tw.figcaption`
text-[13px] text-gray-300 font-semibold
`;
94 changes: 94 additions & 0 deletions src/components/serverSettings/ServerSettingsSidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import tw from "tailwind-styled-components/dist/tailwind";
import { useAppDispatch } from "../../redux/hooks";
import { useServersState } from "../../features/servers";
import {
setDeleteServerConfirmOpen,
setServerSettingsScreen,
useServerSettingsState,
} from "../../features/serverSettings";
import { setUnsavedChangesError } from "../../features/userSettings";

export default function ServerSettingsSidebar() {
const { serverSettingsScreen, serverCopy } = useServerSettingsState();
const { server } = useServersState();
const dispatch = useAppDispatch();

function unsavedChanges() {
if (!serverCopy) return false;

if (serverCopy !== server) {
dispatch(setUnsavedChangesError(true));

setTimeout(() => {
dispatch(setUnsavedChangesError(false));
}, 1500);

return true;
}
}

function viewServerOverview() {
if (unsavedChanges()) return;

dispatch(setServerSettingsScreen("Overview"));
}

return (
<Container>
<NavContainer>
<ListHeading>
{server.name.toUpperCase() || "SERVER SETTINGS"}
</ListHeading>

<SettingsList>
<ListItem
className={`${
serverSettingsScreen === "Overview" && "bg-gray-300"
}`}
onClick={viewServerOverview}
>
Overview
</ListItem>
</SettingsList>

<Divider />

<SettingsList>
<DeleteServer
onClick={() => dispatch(setDeleteServerConfirmOpen(true))}
>
Delete Server
</DeleteServer>
</SettingsList>
</NavContainer>
</Container>
);
}

const Container = tw.div`
flex flex-col items-end w-1/2 bg-gray-100
`;

const NavContainer = tw.nav`
w-[218px] py-15 pr-1.5 pl-5
`;

const SettingsList = tw.ol`
`;

const ListHeading = tw.h3`
px-2.5 pb-1.5 text-xs font-bold
`;

const ListItem = tw.li`
px-2.5 py-1.5 mb-0.5 font-medium rounded-md cursor-pointer
hover:bg-gray-200
`;

const Divider = tw.div`
h-px mx-2.5 my-2 bg-gray-200
`;

const DeleteServer = tw(ListItem)`
text-red-500
`;
Loading

1 comment on commit 7dddebb

@vercel
Copy link

@vercel vercel bot commented on 7dddebb Jan 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please # to comment.