mirror of
https://github.com/balzack/databag.git
synced 2025-02-11 19:19:16 +00:00
adding and removing contacts
This commit is contained in:
parent
e761fd678d
commit
2d98ac4807
26
doc/api.oa3
26
doc/api.oa3
@ -363,7 +363,33 @@ paths:
|
||||
description: permission denied
|
||||
'500':
|
||||
description: internal server error
|
||||
|
||||
/account/listing/{guid}/message:
|
||||
get:
|
||||
tags:
|
||||
- account
|
||||
description: Get profile message of searchable account. Endpoint is publically accessible.
|
||||
operationId: get-account-listing-message
|
||||
parameters:
|
||||
- name: guid
|
||||
in: path
|
||||
description: filter for specified guid
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DataMessage'
|
||||
'401':
|
||||
description: permission denied
|
||||
'500':
|
||||
description: internal server error
|
||||
|
||||
|
||||
/account/token:
|
||||
get:
|
||||
tags:
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
|
||||
func AddCard(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
account, code, err := BearerAppToken(r, false)
|
||||
account, code, err := ParamAgentToken(r, false)
|
||||
if err != nil {
|
||||
ErrResponse(w, code, err)
|
||||
return
|
||||
@ -61,53 +61,27 @@ func AddCard(w http.ResponseWriter, r *http.Request) {
|
||||
AccountID: account.Guid,
|
||||
}
|
||||
|
||||
// create new card or update existing
|
||||
if err = store.DB.Where("account_id = ? AND card_id = 0", account.ID).First(slot).Error; err != nil {
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
ErrResponse(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
// save new card
|
||||
err = store.DB.Transaction(func(tx *gorm.DB) error {
|
||||
if res := tx.Save(card).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
err = store.DB.Transaction(func(tx *gorm.DB) error {
|
||||
if res := tx.Save(card).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
slot.CardSlotId = uuid.New().String()
|
||||
slot.AccountID = account.ID
|
||||
slot.Revision = account.CardRevision + 1
|
||||
slot.CardID = card.ID
|
||||
slot.Card = card
|
||||
if res := tx.Save(slot).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
if res := tx.Model(&account).Update("card_revision", account.CardRevision + 1).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
ErrResponse(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
slot.CardSlotId = uuid.New().String()
|
||||
slot.AccountID = account.ID
|
||||
slot.Revision = account.CardRevision + 1
|
||||
slot.CardID = card.ID
|
||||
slot.Card = card
|
||||
if res := tx.Save(slot).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
} else {
|
||||
err = store.DB.Transaction(func(tx *gorm.DB) error {
|
||||
if res := tx.Save(&card).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
slot.Revision = account.CardRevision + 1
|
||||
slot.CardID = card.ID
|
||||
slot.Card = card
|
||||
if res := tx.Save(&slot).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
if res := tx.Model(&account).Update("card_revision", account.CardRevision + 1).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
ErrResponse(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
if res := tx.Model(&account).Update("card_revision", account.CardRevision + 1).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
ErrResponse(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
|
||||
func RemoveCard(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
account, code, err := BearerAppToken(r, false);
|
||||
account, code, err := ParamAgentToken(r, false);
|
||||
if err != nil {
|
||||
ErrResponse(w, code, err)
|
||||
return
|
||||
|
@ -104,6 +104,13 @@ var routes = Routes{
|
||||
GetAccountListingImage,
|
||||
},
|
||||
|
||||
Route{
|
||||
"GetAccountListingMessage",
|
||||
strings.ToUpper("Get"),
|
||||
"/account/listing/{guid}/message",
|
||||
GetAccountListingMessage,
|
||||
},
|
||||
|
||||
Route{
|
||||
"GetAccountStatus",
|
||||
strings.ToUpper("Get"),
|
||||
|
@ -148,7 +148,6 @@ type Card struct {
|
||||
Location string
|
||||
Image string
|
||||
Version string `gorm:"not null"`
|
||||
Revision string `gorm:"not null"`
|
||||
Node string `gorm:"not null"`
|
||||
ProfileRevision int64 `gorm:"not null"`
|
||||
DetailRevision int64 `gorm:"not null;default:1"`
|
||||
|
@ -586,10 +586,9 @@ func AddTestCard(account string, contact string) (cardId string, err error) {
|
||||
}
|
||||
|
||||
// add A card in B
|
||||
if r, w, err = NewRequest("POST", "/contact/cards", &msg); err != nil {
|
||||
if r, w, err = NewRequest("POST", "/contact/cards?agent=" + account, &msg); err != nil {
|
||||
return
|
||||
}
|
||||
SetBearerAuth(r, account)
|
||||
AddCard(w, r)
|
||||
if err = ReadResponse(w, &card); err != nil {
|
||||
return
|
||||
|
8
net/web/src/Api/addCard.js
Normal file
8
net/web/src/Api/addCard.js
Normal file
@ -0,0 +1,8 @@
|
||||
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||
|
||||
export async function addCard(token, message) {
|
||||
let card = await fetchWithTimeout(`/contact/cards?agent=${token}`, { method: 'POST', body: JSON.stringify(message)} );
|
||||
checkResponse(card);
|
||||
return await card.json();
|
||||
}
|
||||
|
17
net/web/src/Api/fetchUtil.js
Normal file
17
net/web/src/Api/fetchUtil.js
Normal file
@ -0,0 +1,17 @@
|
||||
const TIMEOUT = 15000;
|
||||
|
||||
//await new Promise(r => setTimeout(r, 2000));
|
||||
|
||||
export function checkResponse(response) {
|
||||
if(response.status >= 400 && response.status < 600) {
|
||||
throw new Error(response.url + " failed");
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchWithTimeout(url, options) {
|
||||
return Promise.race([
|
||||
fetch(url, options).catch(err => { throw new Error(url + ' failed'); }),
|
||||
new Promise((_, reject) => setTimeout(() => reject(new Error(url + ' timeout')), TIMEOUT))
|
||||
]);
|
||||
}
|
||||
|
8
net/web/src/Api/getListingMessage.js
Normal file
8
net/web/src/Api/getListingMessage.js
Normal file
@ -0,0 +1,8 @@
|
||||
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||
|
||||
export async function getListingMessage(server, guid) {
|
||||
let listing = await fetchWithTimeout(`https://${server}/account/listing/${guid}/message`, { method: 'GET' });
|
||||
checkResponse(listing);
|
||||
return await listing.json();
|
||||
}
|
||||
|
8
net/web/src/Api/removeCard.js
Normal file
8
net/web/src/Api/removeCard.js
Normal file
@ -0,0 +1,8 @@
|
||||
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||
|
||||
export async function removeCard(token, cardId) {
|
||||
let card = await fetchWithTimeout(`/contact/cards/${cardId}?agent=${token}`, { method: 'DELETE' } );
|
||||
checkResponse(card);
|
||||
return await card.json();
|
||||
}
|
||||
|
@ -143,6 +143,7 @@ export function useAppContext() {
|
||||
|
||||
const resetData = () => {
|
||||
revision.current = null;
|
||||
accountRevision.current = null;
|
||||
profileRevision.current = null;
|
||||
groupRevision.current = null;
|
||||
cardRevision.current = null;
|
||||
|
@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from 'react'
|
||||
import { CloseOutlined, UserOutlined } from '@ant-design/icons';
|
||||
import { useContact } from './useContact.hook';
|
||||
import { Button, Checkbox, Modal } from 'antd'
|
||||
import { ContactWrapper, ProfileButton, CloseButton } from './Contact.styled';
|
||||
import { ContactWrapper, ProfileButton, CloseButton, ContactSpin } from './Contact.styled';
|
||||
|
||||
export function Contact() {
|
||||
|
||||
@ -48,7 +48,7 @@ export function Contact() {
|
||||
|
||||
const Remove = () => {
|
||||
if (state.showButtons.remove) {
|
||||
return <ProfileButton ghost>Remove Contact</ProfileButton>
|
||||
return <ProfileButton ghost onClick={() => actions.remove()}>Remove Contact</ProfileButton>
|
||||
}
|
||||
return <></>
|
||||
}
|
||||
@ -69,7 +69,7 @@ export function Contact() {
|
||||
|
||||
const Save = () => {
|
||||
if (state.showButtons.save) {
|
||||
return <ProfileButton ghost>Save Contact</ProfileButton>
|
||||
return <ProfileButton ghost onClick={() => actions.save()}>Save Contact</ProfileButton>
|
||||
}
|
||||
return <></>
|
||||
}
|
||||
@ -107,6 +107,7 @@ export function Contact() {
|
||||
<div class="header">
|
||||
<div class="title">{ state.handle }</div>
|
||||
<div class="buttons">
|
||||
<ContactSpin size="large" spinning={state.busy} />
|
||||
<Disconnect />
|
||||
<Remove />
|
||||
<Cancel />
|
||||
@ -125,7 +126,7 @@ export function Contact() {
|
||||
<Logo />
|
||||
</div>
|
||||
<div class="block">
|
||||
<span class="label">details:</span>
|
||||
<span class="label">status: { state.status }</span>
|
||||
</div>
|
||||
<div class="details">
|
||||
<div class="name"><Name /></div>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import styled from 'styled-components';
|
||||
import { Button } from 'antd';
|
||||
import { Button, Spin } from 'antd';
|
||||
|
||||
export const ContactWrapper = styled.div`
|
||||
display: flex;
|
||||
@ -31,7 +31,7 @@ export const ContactWrapper = styled.div`
|
||||
align-items: center;
|
||||
justify-content: flex-begin;
|
||||
color: white;
|
||||
padding-left: 32px;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.close {
|
||||
@ -57,9 +57,7 @@ export const ContactWrapper = styled.div`
|
||||
}
|
||||
|
||||
.status {
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
color: #444444;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
@ -109,9 +107,8 @@ export const ContactWrapper = styled.div`
|
||||
}
|
||||
|
||||
.label {
|
||||
padding-right: 8px;
|
||||
padding-right: 4;
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
@ -163,3 +160,7 @@ export const ProfileButton = styled(Button)`
|
||||
margin-right: 8px;
|
||||
`;
|
||||
|
||||
export const ContactSpin = styled(Spin)`
|
||||
padding-right: 32px;
|
||||
`;
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
import { useContext, useState, useEffect } from 'react';
|
||||
import { AppContext } from '../../AppContext/AppContext';
|
||||
import { useNavigate, useLocation, useParams } from "react-router-dom";
|
||||
import { getListingMessage } from '../../Api/getListingMessage';
|
||||
import { addCard } from '../../Api/addCard';
|
||||
import { removeCard } from '../../Api/removeCard';
|
||||
|
||||
export function useContact() {
|
||||
|
||||
@ -11,7 +14,10 @@ export function useContact() {
|
||||
location: '',
|
||||
description: '',
|
||||
imageUrl: null,
|
||||
node: '',
|
||||
cardId: '',
|
||||
showButtons: {},
|
||||
busy: false,
|
||||
});
|
||||
|
||||
const data = useLocation();
|
||||
@ -27,16 +33,57 @@ export function useContact() {
|
||||
close: () => {
|
||||
navigate('/user')
|
||||
},
|
||||
save: async () => {
|
||||
if (!state.busy) {
|
||||
updateState({ busy: true });
|
||||
try {
|
||||
let message = await getListingMessage(state.node, guid);
|
||||
let card = await addCard(app.state.token, message);
|
||||
}
|
||||
catch (err) {
|
||||
window.alert(err);
|
||||
}
|
||||
updateState({ busy: false });
|
||||
}
|
||||
},
|
||||
connect: () => {
|
||||
},
|
||||
disconnect: () => {
|
||||
},
|
||||
ignore: () => {
|
||||
},
|
||||
cancel: () => {
|
||||
},
|
||||
remove: async () => {
|
||||
if (!state.busy) {
|
||||
updateState({ busy: true });
|
||||
try {
|
||||
await removeCard(app.state.token, state.cardId);
|
||||
navigate('/user');
|
||||
}
|
||||
catch (err) {
|
||||
window.alert(err);
|
||||
}
|
||||
updateState({ busy: false });
|
||||
}
|
||||
},
|
||||
saveRequest: () => {
|
||||
},
|
||||
saveAccept: () => {
|
||||
},
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (app?.state?.access === 'user') {
|
||||
let card = app.actions.getCard(guid);
|
||||
if (card) {
|
||||
updateState({ handle: card.data.cardProfile.handle });
|
||||
updateState({ name: card.data.cardProfile.name });
|
||||
updateState({ description: card.data.cardProfile.description });
|
||||
updateState({ location: card.data.cardProfile.location });
|
||||
let profile = card.data.cardProfile;
|
||||
updateState({ cardId: card.id });
|
||||
updateState({ handle: profile.handle });
|
||||
updateState({ name: profile.name });
|
||||
updateState({ description: profile.description });
|
||||
updateState({ location: profile.location });
|
||||
updateState({ node: profile.node });
|
||||
if (card.data.cardProfile.imageSet) {
|
||||
updateState({ imageUrl: app.actions.getCardImageUrl(card.id, card.revision) });
|
||||
}
|
||||
@ -57,7 +104,7 @@ export function useContact() {
|
||||
updateState({ showButtons: { ignore: true, save: true, saveAccept: true }});
|
||||
}
|
||||
if (status === 'confirmed') {
|
||||
updateState({ status: 'saved' });
|
||||
updateState({ status: 'disconnected' });
|
||||
updateState({ showButtons: { remove: true, connect: true }});
|
||||
}
|
||||
if (status === 'requested') {
|
||||
@ -70,13 +117,14 @@ export function useContact() {
|
||||
updateState({ name: data.state.name });
|
||||
updateState({ description: data.state.description });
|
||||
updateState({ location: data.state.location });
|
||||
updateState({ node: data.state.node });
|
||||
if (data.state.imageSet) {
|
||||
updateState({ imageUrl: app.actions.getRegistryImageUrl(data.state.node, guid, data.state.revision) });
|
||||
}
|
||||
else {
|
||||
updateState({ imageUrl: '' });
|
||||
}
|
||||
updateState({ status: null });
|
||||
updateState({ status: 'unsaved' });
|
||||
updateState({ showButtons: { save: true, saveRequest: true }});
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,8 @@ export const ProfileWrapper = styled.div`
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
justify-content: flex-begin;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.close {
|
||||
|
Loading…
Reference in New Issue
Block a user