Implement GETing the personal inbox
This commit is contained in:
parent
f6eaca2fa8
commit
8303baa69d
6 changed files with 136 additions and 22 deletions
|
@ -44,11 +44,15 @@ OutboxItem
|
||||||
activity PersistActivity
|
activity PersistActivity
|
||||||
published UTCTime
|
published UTCTime
|
||||||
|
|
||||||
|
InboxItem
|
||||||
|
|
||||||
InboxItemLocal
|
InboxItemLocal
|
||||||
person PersonId
|
person PersonId
|
||||||
activity OutboxItemId
|
activity OutboxItemId
|
||||||
|
item InboxItemId
|
||||||
|
|
||||||
UniqueInboxItemLocal person activity
|
UniqueInboxItemLocal person activity
|
||||||
|
UniqueInboxItemLocalItem item
|
||||||
|
|
||||||
RemoteActivity
|
RemoteActivity
|
||||||
instance InstanceId
|
instance InstanceId
|
||||||
|
@ -61,8 +65,10 @@ RemoteActivity
|
||||||
InboxItemRemote
|
InboxItemRemote
|
||||||
person PersonId
|
person PersonId
|
||||||
activity RemoteActivityId
|
activity RemoteActivityId
|
||||||
|
item InboxItemId
|
||||||
|
|
||||||
UniqueInboxItemRemote person activity
|
UniqueInboxItemRemote person activity
|
||||||
|
UniqueInboxItemRemoteItem item
|
||||||
|
|
||||||
UnlinkedDelivery
|
UnlinkedDelivery
|
||||||
recipient UnfetchedRemoteActorId
|
recipient UnfetchedRemoteActorId
|
||||||
|
|
|
@ -605,12 +605,17 @@ handleSharerInbox _now shrRecip (Left pidAuthor) _raw activity = do
|
||||||
throwE "Activity author in DB and in received JSON don't match"
|
throwE "Activity author in DB and in received JSON don't match"
|
||||||
if pidRecip == pidAuthor
|
if pidRecip == pidAuthor
|
||||||
then return "Received activity authored by self, ignoring"
|
then return "Received activity authored by self, ignoring"
|
||||||
else do
|
else lift $ do
|
||||||
miblid <- lift $ insertUnique $ InboxItemLocal pidRecip obid
|
ibid <- insert InboxItem
|
||||||
|
miblid <- insertUnique $ InboxItemLocal pidRecip obid ibid
|
||||||
let recip = shr2text shrRecip
|
let recip = shr2text shrRecip
|
||||||
return $ case miblid of
|
case miblid of
|
||||||
Nothing -> "Activity already exists in inbox of /s/" <> recip
|
Nothing -> do
|
||||||
Just _ -> "Activity inserted to inbox of /s/" <> recip
|
delete ibid
|
||||||
|
return $
|
||||||
|
"Activity already exists in inbox of /s/" <> recip
|
||||||
|
Just _ ->
|
||||||
|
return $ "Activity inserted to inbox of /s/" <> recip
|
||||||
handleSharerInbox now shrRecip (Right iidSender) raw activity =
|
handleSharerInbox now shrRecip (Right iidSender) raw activity =
|
||||||
case activitySpecific activity of
|
case activitySpecific activity of
|
||||||
CreateActivity (Create note) -> handleNote note
|
CreateActivity (Create note) -> handleNote note
|
||||||
|
@ -684,11 +689,14 @@ handleSharerInbox now shrRecip (Right iidSender) raw activity =
|
||||||
jsonObj = PersistJSON raw
|
jsonObj = PersistJSON raw
|
||||||
ract = RemoteActivity iidSender luActivity jsonObj now
|
ract = RemoteActivity iidSender luActivity jsonObj now
|
||||||
ractid <- either entityKey id <$> insertBy' ract
|
ractid <- either entityKey id <$> insertBy' ract
|
||||||
mibrid <- insertUnique $ InboxItemRemote pidRecip ractid
|
ibid <- insert InboxItem
|
||||||
|
mibrid <- insertUnique $ InboxItemRemote pidRecip ractid ibid
|
||||||
let recip = shr2text shrRecip
|
let recip = shr2text shrRecip
|
||||||
return $ case mibrid of
|
case mibrid of
|
||||||
Nothing -> "Activity already exists in inbox of /s/" <> recip
|
Nothing -> do
|
||||||
Just _ -> "Activity inserted to inbox of /s/" <> recip
|
delete ibid
|
||||||
|
return $ "Activity already exists in inbox of /s/" <> recip
|
||||||
|
Just _ -> return $ "Activity inserted to inbox of /s/" <> recip
|
||||||
|
|
||||||
handleProjectInbox
|
handleProjectInbox
|
||||||
:: UTCTime
|
:: UTCTime
|
||||||
|
@ -889,7 +897,11 @@ handleProjectInbox now shrRecip prjRecip iidSender hSender raidSender body raw a
|
||||||
let pids = union teamPids fsPids
|
let pids = union teamPids fsPids
|
||||||
-- TODO inefficient, see the other TODOs about mergeConcat
|
-- TODO inefficient, see the other TODOs about mergeConcat
|
||||||
remotes = map (second $ NE.nubBy ((==) `on` fst4)) $ mergeConcat teamRemotes fsRemotes
|
remotes = map (second $ NE.nubBy ((==) `on` fst4)) $ mergeConcat teamRemotes fsRemotes
|
||||||
for_ pids $ \ pid -> insertUnique_ $ InboxItemRemote pid ractid
|
for_ pids $ \ pid -> do
|
||||||
|
ibid <- insert InboxItem
|
||||||
|
mibrid <- insertUnique $ InboxItemRemote pid ractid ibid
|
||||||
|
when (isNothing mibrid) $
|
||||||
|
delete ibid
|
||||||
return remotes
|
return remotes
|
||||||
|
|
||||||
deliverRemoteDB
|
deliverRemoteDB
|
||||||
|
@ -1428,7 +1440,9 @@ handleOutboxNote host (Note mluNote luAttrib aud muParent muContext mpublished c
|
||||||
-- lists have the same instance.
|
-- lists have the same instance.
|
||||||
, map (second $ NE.nubBy ((==) `on` fst4)) $ mergeConcat teamRemotes fsRemotes
|
, map (second $ NE.nubBy ((==) `on` fst4)) $ mergeConcat teamRemotes fsRemotes
|
||||||
)
|
)
|
||||||
lift $ for_ (union recipPids morePids) $ \ pid -> insert_ $ InboxItemLocal pid obid
|
lift $ for_ (union recipPids morePids) $ \ pid -> do
|
||||||
|
ibid <- insert InboxItem
|
||||||
|
insert_ $ InboxItemLocal pid obid ibid
|
||||||
return remotes
|
return remotes
|
||||||
where
|
where
|
||||||
getPersonId :: ShrIdent -> ExceptT Text AppDB PersonId
|
getPersonId :: ShrIdent -> ExceptT Text AppDB PersonId
|
||||||
|
|
|
@ -235,6 +235,7 @@ instance Yesod App where
|
||||||
| a == resendVerifyR -> personFromResendForm
|
| a == resendVerifyR -> personFromResendForm
|
||||||
(AuthR (PluginR "account" ["verify", u, _]), False) -> personUnver u
|
(AuthR (PluginR "account" ["verify", u, _]), False) -> personUnver u
|
||||||
|
|
||||||
|
(SharerInboxR shr , False) -> person shr
|
||||||
(OutboxR shr , True) -> person shr
|
(OutboxR shr , True) -> person shr
|
||||||
|
|
||||||
(GroupsR , True) -> personAny
|
(GroupsR , True) -> personAny
|
||||||
|
|
|
@ -42,6 +42,7 @@ import Control.Monad.Trans.Maybe
|
||||||
import Crypto.Error (CryptoFailable (..))
|
import Crypto.Error (CryptoFailable (..))
|
||||||
import Crypto.PubKey.Ed25519 (publicKey, signature, verify)
|
import Crypto.PubKey.Ed25519 (publicKey, signature, verify)
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
|
import Data.Aeson.Encode.Pretty
|
||||||
import Data.Bifunctor (first, second)
|
import Data.Bifunctor (first, second)
|
||||||
import Data.Foldable (for_)
|
import Data.Foldable (for_)
|
||||||
import Data.HashMap.Strict (HashMap)
|
import Data.HashMap.Strict (HashMap)
|
||||||
|
@ -55,7 +56,7 @@ import Data.Text.Lazy.Encoding (decodeUtf8)
|
||||||
import Data.Time.Clock (UTCTime, getCurrentTime)
|
import Data.Time.Clock (UTCTime, getCurrentTime)
|
||||||
import Data.Time.Interval (TimeInterval, toTimeUnit)
|
import Data.Time.Interval (TimeInterval, toTimeUnit)
|
||||||
import Data.Time.Units (Second)
|
import Data.Time.Units (Second)
|
||||||
import Database.Persist (Entity (..), getBy, insertBy, insert_)
|
import Database.Persist
|
||||||
import Network.HTTP.Client (Manager, HttpException, requestFromURI)
|
import Network.HTTP.Client (Manager, HttpException, requestFromURI)
|
||||||
import Network.HTTP.Simple (httpJSONEither, getResponseBody, setRequestManager, addRequestHeader)
|
import Network.HTTP.Simple (httpJSONEither, getResponseBody, setRequestManager, addRequestHeader)
|
||||||
import Network.HTTP.Types.Header (hDate, hHost)
|
import Network.HTTP.Types.Header (hDate, hHost)
|
||||||
|
@ -71,14 +72,16 @@ import Yesod.Core.Handler
|
||||||
import Yesod.Form.Fields (Textarea (..), textField, textareaField)
|
import Yesod.Form.Fields (Textarea (..), textField, textareaField)
|
||||||
import Yesod.Form.Functions
|
import Yesod.Form.Functions
|
||||||
import Yesod.Form.Types
|
import Yesod.Form.Types
|
||||||
import Yesod.Persist.Core (runDB, get404)
|
import Yesod.Persist.Core
|
||||||
|
|
||||||
import qualified Data.ByteString.Char8 as BC (unpack)
|
import qualified Data.ByteString.Char8 as BC (unpack)
|
||||||
import qualified Data.CaseInsensitive as CI (mk)
|
import qualified Data.CaseInsensitive as CI (mk)
|
||||||
import qualified Data.HashMap.Strict as M (lookup, insert, adjust, fromList)
|
import qualified Data.HashMap.Strict as M (lookup, insert, adjust, fromList)
|
||||||
import qualified Data.Text as T (pack, unpack, concat)
|
import qualified Data.Text as T (pack, unpack, concat)
|
||||||
import qualified Data.Text.Lazy as TL (toStrict)
|
import qualified Data.Text.Lazy as TL (toStrict)
|
||||||
|
import qualified Data.Text.Lazy.Builder as TLB
|
||||||
import qualified Data.Vector as V
|
import qualified Data.Vector as V
|
||||||
|
import qualified Database.Esqueleto as E
|
||||||
import qualified Network.Wai as W (requestMethod, rawPathInfo, requestHeaders)
|
import qualified Network.Wai as W (requestMethod, rawPathInfo, requestHeaders)
|
||||||
|
|
||||||
import Network.HTTP.Signature hiding (Algorithm (..))
|
import Network.HTTP.Signature hiding (Algorithm (..))
|
||||||
|
@ -86,20 +89,25 @@ import Yesod.HttpSignature (verifyRequestSignature)
|
||||||
|
|
||||||
import qualified Network.HTTP.Signature as S (Algorithm (..))
|
import qualified Network.HTTP.Signature as S (Algorithm (..))
|
||||||
|
|
||||||
import Data.Aeson.Encode.Pretty
|
import Database.Persist.JSON
|
||||||
import Data.Aeson.Local
|
|
||||||
import Database.Persist.Local
|
|
||||||
import Network.FedURI
|
import Network.FedURI
|
||||||
import Web.ActivityPub
|
import Web.ActivityPub
|
||||||
import Yesod.Auth.Unverified
|
import Yesod.Auth.Unverified
|
||||||
import Yesod.FedURI
|
import Yesod.FedURI
|
||||||
import Yesod.Hashids
|
import Yesod.Hashids
|
||||||
|
|
||||||
|
import qualified Data.Aeson.Encode.Pretty.ToEncoding as AEP
|
||||||
|
|
||||||
|
import Data.Aeson.Local
|
||||||
|
import Database.Persist.Local
|
||||||
|
import Yesod.Persist.Local
|
||||||
|
|
||||||
import Vervis.ActorKey
|
import Vervis.ActorKey
|
||||||
import Vervis.Federation
|
import Vervis.Federation
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
|
import Vervis.Paginate
|
||||||
import Vervis.RemoteActorStore
|
import Vervis.RemoteActorStore
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
|
|
||||||
|
@ -129,7 +137,50 @@ getInboxR = do
|
||||||
|]
|
|]
|
||||||
|
|
||||||
getSharerInboxR :: ShrIdent -> Handler TypedContent
|
getSharerInboxR :: ShrIdent -> Handler TypedContent
|
||||||
getSharerInboxR _ = error "TODO implement getSharerInboxR"
|
getSharerInboxR shr = do
|
||||||
|
(items, navModel) <- getPageAndNav $ \ off lim -> runDB $ do
|
||||||
|
sid <- getKeyBy404 $ UniqueSharer shr
|
||||||
|
pid <- getKeyBy404 $ UniquePersonIdent sid
|
||||||
|
(,) <$> countItems pid
|
||||||
|
<*> (map adaptItem <$> getItems pid off lim)
|
||||||
|
let pageNav = navWidget navModel
|
||||||
|
selectRep $ provideRep $ defaultLayout $(widgetFile "person/inbox")
|
||||||
|
where
|
||||||
|
countItems pid =
|
||||||
|
(+) <$> count [InboxItemLocalPerson ==. pid]
|
||||||
|
<*> count [InboxItemRemotePerson ==. pid]
|
||||||
|
getItems pid off lim =
|
||||||
|
E.select $ E.from $
|
||||||
|
\ (ib `E.LeftOuterJoin` (ibl `E.InnerJoin` ob) `E.LeftOuterJoin` (ibr `E.InnerJoin` ract)) -> do
|
||||||
|
E.on $ ibr E.?. InboxItemRemoteActivity E.==. ract E.?. RemoteActivityId
|
||||||
|
E.on $ E.just (ib E.^. InboxItemId) E.==. ibr E.?. InboxItemRemoteItem
|
||||||
|
E.on $ ibl E.?. InboxItemLocalActivity E.==. ob E.?. OutboxItemId
|
||||||
|
E.on $ E.just (ib E.^. InboxItemId) E.==. ibl E.?. InboxItemLocalItem
|
||||||
|
E.where_
|
||||||
|
$ ( E.isNothing (ibr E.?. InboxItemRemotePerson) E.||.
|
||||||
|
ibr E.?. InboxItemRemotePerson E.==. E.just (E.val pid)
|
||||||
|
)
|
||||||
|
E.&&.
|
||||||
|
( E.isNothing (ibl E.?. InboxItemLocalPerson) E.||.
|
||||||
|
ibl E.?. InboxItemLocalPerson E.==. E.just (E.val pid)
|
||||||
|
)
|
||||||
|
E.orderBy [E.desc $ ib E.^. InboxItemId]
|
||||||
|
E.offset $ fromIntegral off
|
||||||
|
E.limit $ fromIntegral lim
|
||||||
|
return
|
||||||
|
( ib E.^. InboxItemId
|
||||||
|
, ob E.?. OutboxItemActivity
|
||||||
|
, ract E.?. RemoteActivityContent
|
||||||
|
)
|
||||||
|
adaptItem (E.Value ibid, E.Value mact, E.Value mobj) =
|
||||||
|
case (mact, mobj) of
|
||||||
|
(Nothing, Nothing) ->
|
||||||
|
error $
|
||||||
|
"InboxItem #" ++ show ibid ++ " neither local nor remote"
|
||||||
|
(Just _, Just _) ->
|
||||||
|
error $ "InboxItem #" ++ show ibid ++ " both local and remote"
|
||||||
|
(Just act, Nothing) -> Left $ persistJSONValue act
|
||||||
|
(Nothing, Just obj) -> Right $ persistJSONValue obj
|
||||||
|
|
||||||
getProjectInboxR :: ShrIdent -> PrjIdent -> Handler TypedContent
|
getProjectInboxR :: ShrIdent -> PrjIdent -> Handler TypedContent
|
||||||
getProjectInboxR _ _ = error "TODO implement getProjectInboxR"
|
getProjectInboxR _ _ = error "TODO implement getProjectInboxR"
|
||||||
|
|
|
@ -46,7 +46,8 @@ import Text.Email.Validate (unsafeEmailAddress)
|
||||||
import Web.PathPieces (toPathPiece)
|
import Web.PathPieces (toPathPiece)
|
||||||
|
|
||||||
import qualified Database.Esqueleto as E
|
import qualified Database.Esqueleto as E
|
||||||
import qualified Database.Persist.Schema as U (addEntity, unsetFieldDefault)
|
import qualified Database.Persist.Schema as S
|
||||||
|
import qualified Database.Persist.Schema.Types as ST
|
||||||
|
|
||||||
import Vervis.Migration.Model
|
import Vervis.Migration.Model
|
||||||
|
|
||||||
|
@ -70,13 +71,13 @@ changes =
|
||||||
[ -- 1
|
[ -- 1
|
||||||
addEntities model_2016_08_04
|
addEntities model_2016_08_04
|
||||||
-- 2
|
-- 2
|
||||||
, unchecked $ U.unsetFieldDefault "Sharer" "created"
|
, unchecked $ S.unsetFieldDefault "Sharer" "created"
|
||||||
-- 3
|
-- 3
|
||||||
, unchecked $ U.unsetFieldDefault "Project" "nextTicket"
|
, unchecked $ S.unsetFieldDefault "Project" "nextTicket"
|
||||||
-- 4
|
-- 4
|
||||||
, unchecked $ U.unsetFieldDefault "Repo" "vcs"
|
, unchecked $ S.unsetFieldDefault "Repo" "vcs"
|
||||||
-- 5
|
-- 5
|
||||||
, unchecked $ U.unsetFieldDefault "Repo" "mainBranch"
|
, unchecked $ S.unsetFieldDefault "Repo" "mainBranch"
|
||||||
-- 6
|
-- 6
|
||||||
, removeField "Ticket" "done"
|
, removeField "Ticket" "done"
|
||||||
-- 7
|
-- 7
|
||||||
|
@ -263,6 +264,16 @@ changes =
|
||||||
, addFieldPrimRequired "Follow" False "manual"
|
, addFieldPrimRequired "Follow" False "manual"
|
||||||
-- 68
|
-- 68
|
||||||
, addFieldPrimRequired "RemoteFollow" False "manual"
|
, addFieldPrimRequired "RemoteFollow" False "manual"
|
||||||
|
-- 69
|
||||||
|
, addEntity $ ST.Entity "InboxItem" [] []
|
||||||
|
-- 70
|
||||||
|
, addFieldRefRequiredEmpty "InboxItemLocal" "item" "InboxItem"
|
||||||
|
-- 71
|
||||||
|
, addFieldRefRequiredEmpty "InboxItemRemote" "item" "InboxItem"
|
||||||
|
-- 72
|
||||||
|
, addUnique "InboxItemLocal" $ Unique "UniqueInboxItemLocalItem" ["item"]
|
||||||
|
-- 73
|
||||||
|
, addUnique "InboxItemRemote" $ Unique "UniqueInboxItemRemoteItem" ["item"]
|
||||||
]
|
]
|
||||||
|
|
||||||
migrateDB :: MonadIO m => ReaderT SqlBackend m (Either Text (Int, Int))
|
migrateDB :: MonadIO m => ReaderT SqlBackend m (Either Text (Int, Int))
|
||||||
|
|
31
templates/person/inbox.hamlet
Normal file
31
templates/person/inbox.hamlet
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
$# This file is part of Vervis.
|
||||||
|
$#
|
||||||
|
$# Written in 2019 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
|
$#
|
||||||
|
$# ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
|
$#
|
||||||
|
$# The author(s) have dedicated all copyright and related and neighboring
|
||||||
|
$# rights to this software to the public domain worldwide. This software is
|
||||||
|
$# distributed without any warranty.
|
||||||
|
$#
|
||||||
|
$# You should have received a copy of the CC0 Public Domain Dedication along
|
||||||
|
$# with this software. If not, see
|
||||||
|
$# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This is your personal inbox. It's basically like your personal social
|
||||||
|
overview page. It corresponds to the "Home" column in Mastodon, and displays
|
||||||
|
the items in your ActivityPub inbox collection.
|
||||||
|
|
||||||
|
^{pageNav}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
$forall item <- items
|
||||||
|
<div><pre>
|
||||||
|
$case item
|
||||||
|
$of Left doc
|
||||||
|
#{AEP.encodePrettyToLazyText doc}
|
||||||
|
$of Right obj
|
||||||
|
#{TLB.toLazyText $ encodePrettyToTextBuilder obj}
|
||||||
|
|
||||||
|
^{pageNav}
|
Loading…
Reference in a new issue