group pages!
This commit is contained in:
		| @@ -9,6 +9,8 @@ | |||||||
|   import Main from "./pages/profiles/Main.svelte"; |   import Main from "./pages/profiles/Main.svelte"; | ||||||
|   import Status from './pages/status.svelte'; |   import Status from './pages/status.svelte'; | ||||||
|   import Member from './pages/Member.svelte'; |   import Member from './pages/Member.svelte'; | ||||||
|  |   import Group from './pages/Group.svelte'; | ||||||
|  |   import { Alert } from 'sveltestrap'; | ||||||
|    |    | ||||||
|   // theme cdns (I might make some myself too) |   // theme cdns (I might make some myself too) | ||||||
|   let light = "https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css"; |   let light = "https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css"; | ||||||
| @@ -51,21 +53,20 @@ | |||||||
|     <Route path="/"><Home /></Route> |     <Route path="/"><Home /></Route> | ||||||
|     <Route path="dash"><Dash /></Route> |     <Route path="dash"><Dash /></Route> | ||||||
|     <Route path="dash/m/:id"><Member isPublic={falseBool}/></Route> |     <Route path="dash/m/:id"><Member isPublic={falseBool}/></Route> | ||||||
|  |     <Route path = "dash/g/:id"><Group isPublic={falseBool}/></Route> | ||||||
|     <Route path="settings"><Settings /></Route> |     <Route path="settings"><Settings /></Route> | ||||||
|     <Route path="profile"><Public /></Route> |     <Route path="profile"><Public /></Route> | ||||||
|     <Route path = "profile/s/:id"><Main /></Route> |     <Route path = "profile/s/:id"><Main /></Route> | ||||||
|     <Route path = "s"> |     <Route path = "s"> | ||||||
|       hey please provide a system |       <Alert color="danger">Please provide a system ID in the URL.</Alert> | ||||||
|     </Route> |     </Route> | ||||||
|     <Route path = "profile/m/:id"><Member/></Route> |     <Route path = "profile/m/:id"><Member/></Route> | ||||||
|     <Route path = "profile/m"> |     <Route path = "profile/m"> | ||||||
|       hey please provide a member |       <Alert color="danger">Please provide a member ID in the URL.</Alert> | ||||||
|     </Route> |  | ||||||
|     <Route path = "profile/g/:id"> |  | ||||||
|       group! |  | ||||||
|     </Route> |     </Route> | ||||||
|  |     <Route path = "profile/g/:id"><Group/></Route> | ||||||
|     <Route path = "profile/g"> |     <Route path = "profile/g"> | ||||||
|       hey please provide a group |       <Alert color="danger">Please provide a group ID in the URL.</Alert> | ||||||
|     </Route> |     </Route> | ||||||
|     <Route path="status"><Status /></Route> |     <Route path="status"><Status /></Route> | ||||||
|   <Footer /> |   <Footer /> | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ | |||||||
|     import twemoji from 'twemoji'; |     import twemoji from 'twemoji'; | ||||||
|     import Privacy from './Privacy.svelte'; |     import Privacy from './Privacy.svelte'; | ||||||
|     import MemberEdit from './MemberEdit.svelte'; |     import MemberEdit from './MemberEdit.svelte'; | ||||||
|  |     import { navigate } from 'svelte-navigator'; | ||||||
|  |  | ||||||
|     import { Member, Group } from '../../api/types'; |     import { Member, Group } from '../../api/types'; | ||||||
|     |     | ||||||
| @@ -15,6 +16,7 @@ | |||||||
|     export let isPublic: boolean; |     export let isPublic: boolean; | ||||||
|     export let members: Member[] = []; |     export let members: Member[] = []; | ||||||
|     export let isMainDash = true; |     export let isMainDash = true; | ||||||
|  |     export let isPage = false; | ||||||
|  |  | ||||||
|     let htmlDescription: string; |     let htmlDescription: string; | ||||||
|     $: if (group.description) {  |     $: if (group.description) {  | ||||||
| @@ -107,6 +109,11 @@ | |||||||
| <Button style="flex: 0" color="primary" on:click={() => editMode = true}>Edit</Button>  | <Button style="flex: 0" color="primary" on:click={() => editMode = true}>Edit</Button>  | ||||||
| {#if isMainDash}<Button style="flex: 0" color="secondary" on:click={() => memberMode = true}>Members</Button>{/if} | {#if isMainDash}<Button style="flex: 0" color="secondary" on:click={() => memberMode = true}>Members</Button>{/if} | ||||||
| {/if} | {/if} | ||||||
|  | {#if !isPage} | ||||||
|  |     <Button style="flex: 0; {!isPublic && "float: right;"}" color="primary" on:click={() => navigate(isPublic ? `/profile/g/${group.id}` : `/dash/g/${group.id}`)}>View page</Button> | ||||||
|  |     {:else if !isPublic} | ||||||
|  |     <Button style="flex: 0; {!isPublic && "float: right;"}" color="primary" on:click={() => navigate("/dash?tab=groups")}>View system</Button> | ||||||
|  |     {/if} | ||||||
| {:else if editMode} | {:else if editMode} | ||||||
| <Edit on:deletion on:update bind:group bind:editMode /> | <Edit on:deletion on:update bind:group bind:editMode /> | ||||||
| {:else if memberMode} | {:else if memberMode} | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ | |||||||
|     export let member: Member; |     export let member: Member; | ||||||
|     export let isPublic: boolean = false; |     export let isPublic: boolean = false; | ||||||
|     export let isPage: boolean = false; |     export let isPage: boolean = false; | ||||||
|  |     export let isMainDash = true; | ||||||
|  |  | ||||||
|     let editMode: boolean = false; |     let editMode: boolean = false; | ||||||
|     let groupMode: boolean = false; |     let groupMode: boolean = false; | ||||||
| @@ -145,7 +146,8 @@ | |||||||
|     <img src={member.banner} alt="your system banner" class="w-100 mb-3 rounded" style="max-height: 17em; object-fit: cover"/> |     <img src={member.banner} alt="your system banner" class="w-100 mb-3 rounded" style="max-height: 17em; object-fit: cover"/> | ||||||
|     {/if} |     {/if} | ||||||
|     {#if !isPublic} |     {#if !isPublic} | ||||||
|     <Button style="flex: 0" color="primary" on:click={() => editMode = true}>Edit</Button> <Button style="flex: 0" color="secondary" on:click={() => groupMode = true}>Groups</Button> |     <Button style="flex: 0" color="primary" on:click={() => editMode = true}>Edit</Button> | ||||||
|  |     {#if isMainDash}<Button style="flex: 0" color="secondary" on:click={() => groupMode = true}>Groups</Button>{/if} | ||||||
|     {/if} |     {/if} | ||||||
|     {#if !isPage} |     {#if !isPage} | ||||||
|     <Button style="flex: 0; {!isPublic && "float: right;"}" color="primary" on:click={() => navigate(isPublic ? `/profile/m/${member.id}` : `/dash/m/${member.id}`)}>View page</Button> |     <Button style="flex: 0; {!isPublic && "float: right;"}" color="primary" on:click={() => navigate(isPublic ? `/profile/m/${member.id}` : `/dash/m/${member.id}`)}>View page</Button> | ||||||
|   | |||||||
							
								
								
									
										187
									
								
								src/pages/Group.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								src/pages/Group.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,187 @@ | |||||||
|  | <script lang="ts"> | ||||||
|  |     import { Container, Row, Col, Alert, Spinner, Card, CardHeader, CardBody, Accordion, AccordionItem, CardTitle } from "sveltestrap"; | ||||||
|  |     import Body from '../lib/group/Body.svelte'; | ||||||
|  |     import MemberBody from '../lib/member/Body.svelte'; | ||||||
|  |     import { useParams, Link } from 'svelte-navigator'; | ||||||
|  |     import { onMount } from 'svelte'; | ||||||
|  |     import api from "../api"; | ||||||
|  |     import { Member, Group } from "../api/types"; | ||||||
|  |     import CardsHeader from "../lib/CardsHeader.svelte"; | ||||||
|  |     import FaUsers from 'svelte-icons/fa/FaUsers.svelte'; | ||||||
|  |     import FaList from 'svelte-icons/fa/FaList.svelte'; | ||||||
|  |     import FaUserCircle from 'svelte-icons/fa/FaUserCircle.svelte'; | ||||||
|  |     import ListPagination from '../lib/ListPagination.svelte'; | ||||||
|  |     import FaLock from 'svelte-icons/fa/FaLock.svelte' | ||||||
|  |  | ||||||
|  |     let loading = true; | ||||||
|  |     let memberLoading = false; | ||||||
|  |     let params = useParams(); | ||||||
|  |     let err = ""; | ||||||
|  |     let memberErr = ""; | ||||||
|  |     let group: Group; | ||||||
|  |     let members: Member[] = []; | ||||||
|  |     let systemMembers: Group[] = []; | ||||||
|  |     let isMainDash = false; | ||||||
|  |     let isDeleted = false; | ||||||
|  |  | ||||||
|  |     const isPage = true; | ||||||
|  |     export let isPublic = true; | ||||||
|  |     let settings = JSON.parse(localStorage.getItem("pk-settings")); | ||||||
|  |  | ||||||
|  |     let currentPage = 1; | ||||||
|  |     let itemsPerPage = 10; | ||||||
|  |  | ||||||
|  |     $: indexOfLastItem = currentPage * itemsPerPage; | ||||||
|  |     $: indexOfFirstItem = indexOfLastItem - itemsPerPage; | ||||||
|  |     $: pageAmount = Math.ceil(members.length / itemsPerPage); | ||||||
|  |  | ||||||
|  |     $: orderedMembers = members.sort((a, b) => a.name.localeCompare(b.name)); | ||||||
|  |     $: slicedMembers = orderedMembers.slice(indexOfFirstItem, indexOfLastItem); | ||||||
|  |  | ||||||
|  |     onMount(() => { | ||||||
|  |         fetchGroup(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     async function fetchGroup() { | ||||||
|  |         try { | ||||||
|  |             group = await api().groups($params.id).get({auth: !isPublic}); | ||||||
|  |             if (!isPublic && !group.privacy) throw new Error("This group does not belong to your system, did you mean to look up their public page?") | ||||||
|  |             err = ""; | ||||||
|  |             loading = false; | ||||||
|  |             memberLoading = true; | ||||||
|  |             await new Promise(resolve => setTimeout(resolve, 1000)); | ||||||
|  |             fetchMembers(); | ||||||
|  |         } catch (error) { | ||||||
|  |             console.log(error); | ||||||
|  |             err = error.message; | ||||||
|  |             loading = false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async function fetchMembers() { | ||||||
|  |         try { | ||||||
|  |             members = await api().groups($params.id).members().get({auth: !isPublic}); | ||||||
|  |             if (!isPublic) { | ||||||
|  |                 await new Promise(resolve => setTimeout(resolve, 1000)); | ||||||
|  |                 systemMembers = await api().systems("@me").members.get({ auth: true }); | ||||||
|  |             } | ||||||
|  |             memberErr = ""; | ||||||
|  |             memberLoading = false; | ||||||
|  |             // we can't use with_members from a group list from a member endpoint yet, but I'm leaving this in in case we do | ||||||
|  |             // (this is needed for editing a group member list from the member page) | ||||||
|  |             /* if (!isPublic) { | ||||||
|  |                 await new Promise(resolve => setTimeout(resolve, 1000)); | ||||||
|  |                 systemMembers = await api().systems("@me").members.get({auth: true}); | ||||||
|  |             } */ | ||||||
|  |         } catch (error) { | ||||||
|  |             console.log(error); | ||||||
|  |             memberErr = error.message; | ||||||
|  |             memberLoading = false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async function updateMembers() { | ||||||
|  |       memberLoading = true; | ||||||
|  |       await new Promise(resolve => setTimeout(resolve, 500)); | ||||||
|  |       fetchMembers(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function updateDelete() { | ||||||
|  |         isDeleted = true; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     function updateMemberList(event: any) { | ||||||
|  |         members = members.map(member => member.id !== event.detail.id ? member : event.detail); | ||||||
|  |         systemMembers = systemMembers.map(member => member.id !== event.detail.id ? member : event.detail); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function deleteMemberFromList(event: any) { | ||||||
|  |         members = members.filter(member => member.id !== event.detail); | ||||||
|  |         systemMembers = systemMembers.filter(member => member.id !== event.detail); | ||||||
|  |   } | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | {#if settings && settings.appearance.color_background} | ||||||
|  |     <div class="background" style="background-color: {group && `#${group.color}`}"></div> | ||||||
|  | {/if} | ||||||
|  | {#if group && group.banner && ((settings && settings.appearance.banner_top))} | ||||||
|  | <div class="banner" style="background-image: url({group.banner})" /> | ||||||
|  | {/if} | ||||||
|  | <Container> | ||||||
|  |     <Row> | ||||||
|  |         <Col class="mx-auto" xs={12} lg={11} xl={10}> | ||||||
|  |             {#if isDeleted} | ||||||
|  |                 <Alert color="success">Group has been successfully deleted. <Link to="/dash">Return to dash</Link></Alert> | ||||||
|  |             {:else} | ||||||
|  |             {#if isPublic} | ||||||
|  |                 <Alert color="info">You are currently <b>viewing</b> a group.</Alert> | ||||||
|  |             {/if} | ||||||
|  |             {#if err} | ||||||
|  |                 <Alert color="danger">{err}</Alert> | ||||||
|  |             {:else if loading} | ||||||
|  |                 <Spinner/> | ||||||
|  |             {:else if group && group.id} | ||||||
|  |                 <Card class="mb-4"> | ||||||
|  |                     <CardHeader> | ||||||
|  |                         <CardsHeader bind:item={group}> | ||||||
|  |                             <FaUsers slot="icon" /> | ||||||
|  |                         </CardsHeader> | ||||||
|  |                     </CardHeader> | ||||||
|  |                     <CardBody> | ||||||
|  |                         <Body on:deletion={updateDelete} on:updateGroups={updateMembers} bind:members={systemMembers} bind:group={group} isPage={isPage} isPublic={isPublic}/> | ||||||
|  |                     </CardBody> | ||||||
|  |                 </Card> | ||||||
|  |             {/if} | ||||||
|  |             {#if memberLoading} | ||||||
|  |                 <Alert color="primary"><Spinner size="sm" /> Fetching members...</Alert> | ||||||
|  |             {:else if memberErr} | ||||||
|  |                 <Alert color="danger">{memberErr}</Alert> | ||||||
|  |             {:else if members && members.length > 0} | ||||||
|  |             <Card class="mb-2"> | ||||||
|  |                 <CardHeader> | ||||||
|  |                     <CardTitle style="margin-top: 8px; outline: none;"> | ||||||
|  |                         <div class="icon d-inline-block"> | ||||||
|  |                             <FaList /> | ||||||
|  |                         </div> Member groups | ||||||
|  |                     </CardTitle> | ||||||
|  |                 </CardHeader> | ||||||
|  |             </Card> | ||||||
|  |             <ListPagination bind:currentPage bind:pageAmount /> | ||||||
|  |             <Accordion class="mb-3" stayOpen> | ||||||
|  |             {#each slicedMembers as member, index (member.id)} | ||||||
|  |             {#if (!isPublic && member.privacy.visibility === "public") || isPublic} | ||||||
|  |                 <AccordionItem> | ||||||
|  |                     <CardsHeader bind:item={member} slot="header"> | ||||||
|  |                         <FaUserCircle slot="icon" /> | ||||||
|  |                     </CardsHeader> | ||||||
|  |                     <MemberBody on:update={updateMemberList} isMainDash={isMainDash} on:deletion={deleteMemberFromList} bind:member bind:isPublic={isPublic}/> | ||||||
|  |                 </AccordionItem> | ||||||
|  |                 {:else} | ||||||
|  |                 <AccordionItem> | ||||||
|  |                     <CardsHeader bind:item={member} slot="header"> | ||||||
|  |                         <FaLock slot="icon" /> | ||||||
|  |                     </CardsHeader> | ||||||
|  |                     <MemberBody on:update={updateMemberList} isMainDash={isMainDash} on:deletion={deleteMemberFromList} bind:member bind:isPublic={isPublic}/> | ||||||
|  |                 </AccordionItem> | ||||||
|  |                 {/if} | ||||||
|  |             {/each} | ||||||
|  |             </Accordion> | ||||||
|  |             <ListPagination bind:currentPage bind:pageAmount /> | ||||||
|  |             {/if} | ||||||
|  |             {/if} | ||||||
|  |         </Col> | ||||||
|  |     </Row> | ||||||
|  | </Container> | ||||||
|  |  | ||||||
|  | <style> | ||||||
|  |     .background { | ||||||
|  |         position: fixed; | ||||||
|  |         top: 0; | ||||||
|  |         left: 0; | ||||||
|  |         width: 100%; | ||||||
|  |         flex: 1; | ||||||
|  |         min-height: 100%; | ||||||
|  |         z-index: -30; | ||||||
|  |     } | ||||||
|  | </style> | ||||||
|  |  | ||||||
		Reference in New Issue
	
	Block a user